-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathring.py
More file actions
118 lines (88 loc) · 3.25 KB
/
ring.py
File metadata and controls
118 lines (88 loc) · 3.25 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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
from Geometry3D import Vector, Line
from math import cos, sin, pi
from gcode_geom import GPoint, GSegment, GHalfLine
from gcode_geom.utils import circle_intersection
from util import attrhelper
from gcode_geom.angle import Angle, atan2
from plot_helpers import update_figure
class Ring:
"""A class representing the ring and thread carrier."""
#Default plotting style
style = {
'ring': {'line': dict(color='white', width=10), 'opacity':.25},
'indicator': {'line': dict(color='blue', width= 4)},
}
def __init__(self, angle:Angle, radius=100, center:GPoint=None, **kwargs):
self.radius = radius
self._angle:Angle = angle
self.center = GPoint(radius, 0, 0) if center is None else GPoint(center).copy()
x = property(**attrhelper('center.x'))
z = property(**attrhelper('center.z'))
def attr_changed(self, attr, old_value, new_value):
raise ValueError(f"Can't adjust the {attr} coordinate of the ring!")
@property
def y(self): return self.center.y
@y.setter
def y(self, val):
if val == self.center.y: return
self.center = self.center.copy(y=val)
@property
def angle(self) -> Angle: return self._angle
@angle.setter
def angle(self, angle:Angle):
if not isinstance(angle, Angle): raise TypeError(f"Expected Angle, got {type(angle)}")
self._angle = (angle % (2*pi))
@property
def point(self):
return self.angle2point(self.angle)
def __repr__(self):
return f'Ring({self.angle:.3f}°, ⌀{self.radius*2} → {self.point}, ⊙{self.center})'
def intersection(self, seg:GSegment|GHalfLine|Line) -> list[GPoint]:
return circle_intersection(self.center, self.radius, seg)
def angle2point(self, angle:Angle) -> GPoint:
"""Return an x,y,z=0 location on the ring based on the given angle, without
moving the ring. Uses the coordinate system according to the ring's center
point."""
return GPoint(
cos(angle) * self.radius + self.center.x,
sin(angle) * self.radius + self.center.y,
self.center.z
)
def point2angle(self, point:GPoint) -> Angle:
"""Given a point in the coordinate system of the ring's center coordinate,
return the angle between the ring center and that point in degrees."""
return atan2(point.y - self.center.y, point.x - self.center.x)
def plot(self, fig, style=None, offset:Vector=None, angle:Angle=None):
angle = self.angle if angle is None else angle
center = self.center.copy()
if offset: center.move(offset)
fig.add_shape(
name='ring',
type='circle',
xref='x', yref='y',
x0=center.x-self.radius, y0=center.y-self.radius,
x1=center.x+self.radius, y1=center.y+self.radius,
**self.style['ring'],
)
update_figure(fig, 'ring', style, what='shapes')
ringwidth = next(fig.select_shapes(selector={'name':'ring'})).line.width
c1 = GPoint(
self.center.x + cos(angle)*(self.radius-ringwidth/2),
self.center.y + sin(angle)*(self.radius-ringwidth/2),
self.center.z)
c2 = GPoint(
self.center.x + cos(angle)*(self.radius+ringwidth/2),
self.center.y + sin(angle)*(self.radius+ringwidth/2),
self.center.z)
if offset:
c1.move(offset)
c2.move(offset)
fig.add_shape(
name='indicator',
type='line',
xref='x', yref='y',
x0=c1.x, y0=c1.y,
x1=c2.x, y1=c2.y,
**self.style['indicator'],
)
update_figure(fig, 'indicator', style, what='shapes')