-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspiral_arc.py
More file actions
79 lines (63 loc) · 2.6 KB
/
spiral_arc.py
File metadata and controls
79 lines (63 loc) · 2.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/usr/bin/env python3
"""
Spiral Arc Module
=================
Translates the pattern along a spiral path (radius increases as it goes around).
"""
import numpy as np
from fractions import Fraction
from math import pi
from main import TransformModule
class SpiralArcModule(TransformModule):
"""
Spiral arc: pattern SLIDES along a spiral path.
Like arc, but the radius changes as it goes around.
This is a SLIDING transform - it moves the pattern along a spiral trajectory.
Configuration:
inner_radius: Starting radius
outer_radius: Ending radius
start_angle: Starting angle in degrees
sweep_angle: Total angle swept in degrees
center_x, center_y: Center of the spiral
cycles: Number of spiral arms/traversals
normalize: If true, normalize t to [0,1] regardless of pipeline period
"""
def _load_config(self):
"""Load spiral configuration."""
self.inner_radius = self._getfloat('inner_radius', 50.0)
self.outer_radius = self._getfloat('outer_radius', 150.0)
self.start_angle = self._getfloat('start_angle', 0.0)
self.sweep_angle = self._getfloat('sweep_angle', 720.0)
self.center_x = self._getfloat('center_x', 0.0)
self.center_y = self._getfloat('center_y', 0.0)
self.cycles = self._getfloat('cycles', 1.0)
self.normalize = self._getboolean('normalize', True)
# Convert to radians
self.start_rad = self.start_angle * pi / 180
self.sweep_rad = self.sweep_angle * pi / 180
# Center as complex
self.center = self.center_x + 1j * self.center_y
def transform(self, z: complex, t: float) -> complex:
"""
Translate input coordinates along a spiral path.
"""
t_use = self._normalize_t(t)
# Current angle
angle = self.start_rad + t_use * self.sweep_rad
# Interpolate radius
radius = self.inner_radius + t_use * (self.outer_radius - self.inner_radius)
# Position on the spiral
spiral_position = self.center + radius * np.exp(1j * angle)
return z + spiral_position
@property
def natural_period(self) -> Fraction:
"""Period based on cycles."""
if self.cycles == 0:
return Fraction(1, 1)
return Fraction(self.cycles).limit_denominator(1000)
@property
def is_generator(self) -> bool:
return False
def __repr__(self):
return (f"SpiralArcModule(r={self.inner_radius}->{self.outer_radius}, "
f"sweep={self.sweep_angle}°)")