nbs-bl 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nbs_bl/__init__.py +15 -0
- nbs_bl/beamline.py +450 -0
- nbs_bl/configuration.py +838 -0
- nbs_bl/detectors.py +89 -0
- nbs_bl/devices/__init__.py +12 -0
- nbs_bl/devices/detectors.py +154 -0
- nbs_bl/devices/motors.py +242 -0
- nbs_bl/devices/sampleholders.py +360 -0
- nbs_bl/devices/shutters.py +120 -0
- nbs_bl/devices/slits.py +51 -0
- nbs_bl/gGrEqns.py +171 -0
- nbs_bl/geometry/__init__.py +0 -0
- nbs_bl/geometry/affine.py +197 -0
- nbs_bl/geometry/bars.py +189 -0
- nbs_bl/geometry/frames.py +534 -0
- nbs_bl/geometry/linalg.py +138 -0
- nbs_bl/geometry/polygons.py +56 -0
- nbs_bl/help.py +126 -0
- nbs_bl/hw.py +270 -0
- nbs_bl/load.py +113 -0
- nbs_bl/motors.py +19 -0
- nbs_bl/planStatus.py +5 -0
- nbs_bl/plans/__init__.py +8 -0
- nbs_bl/plans/batches.py +174 -0
- nbs_bl/plans/conditions.py +77 -0
- nbs_bl/plans/flyscan_base.py +180 -0
- nbs_bl/plans/groups.py +55 -0
- nbs_bl/plans/maximizers.py +423 -0
- nbs_bl/plans/metaplans.py +179 -0
- nbs_bl/plans/plan_stubs.py +246 -0
- nbs_bl/plans/preprocessors.py +160 -0
- nbs_bl/plans/scan_base.py +58 -0
- nbs_bl/plans/scan_decorators.py +524 -0
- nbs_bl/plans/scans.py +145 -0
- nbs_bl/plans/suspenders.py +87 -0
- nbs_bl/plans/time_estimation.py +168 -0
- nbs_bl/plans/xas.py +123 -0
- nbs_bl/printing.py +221 -0
- nbs_bl/qt/models/beamline.py +11 -0
- nbs_bl/qt/models/energy.py +53 -0
- nbs_bl/qt/widgets/energy.py +225 -0
- nbs_bl/queueserver.py +249 -0
- nbs_bl/redisDevice.py +96 -0
- nbs_bl/run_engine.py +63 -0
- nbs_bl/samples.py +130 -0
- nbs_bl/settings.py +68 -0
- nbs_bl/shutters.py +39 -0
- nbs_bl/sim/__init__.py +2 -0
- nbs_bl/sim/config/polphase.nc +0 -0
- nbs_bl/sim/energy.py +403 -0
- nbs_bl/sim/manipulator.py +14 -0
- nbs_bl/sim/utils.py +36 -0
- nbs_bl/startup.py +27 -0
- nbs_bl/status.py +114 -0
- nbs_bl/tests/__init__.py +0 -0
- nbs_bl/tests/modify_regions.py +160 -0
- nbs_bl/tests/test_frames.py +99 -0
- nbs_bl/tests/test_panels.py +69 -0
- nbs_bl/utils.py +235 -0
- nbs_bl-0.2.0.dist-info/METADATA +71 -0
- nbs_bl-0.2.0.dist-info/RECORD +64 -0
- nbs_bl-0.2.0.dist-info/WHEEL +4 -0
- nbs_bl-0.2.0.dist-info/entry_points.txt +2 -0
- nbs_bl-0.2.0.dist-info/licenses/LICENSE +13 -0
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from .linalg import (vec, constructBasis, changeBasisMatrix, rad_to_deg,
|
|
3
|
+
deg_to_rad, rotz, rotzMat)
|
|
4
|
+
from .polygons import isInPoly, getMinDist
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NullFrame:
|
|
8
|
+
def frame_to_beam(self, *args, **kwargs):
|
|
9
|
+
return args
|
|
10
|
+
|
|
11
|
+
def beam_to_frame(self, *args, **kwargs):
|
|
12
|
+
return args
|
|
13
|
+
|
|
14
|
+
def distance_to_beam(self, *args):
|
|
15
|
+
v = np.array(args)
|
|
16
|
+
return np.sqrt(np.dot(v, v))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Axis:
|
|
20
|
+
def __init__(self, x0, scale=1, parent=None):
|
|
21
|
+
self.reset(x0, scale, parent=parent)
|
|
22
|
+
|
|
23
|
+
def reset(self, x0, scale, parent=None):
|
|
24
|
+
self.x0 = x0
|
|
25
|
+
self.scale = scale
|
|
26
|
+
self.parent = parent
|
|
27
|
+
|
|
28
|
+
def update_basis(self, x):
|
|
29
|
+
self.x0 = x
|
|
30
|
+
|
|
31
|
+
def frame_to_parent(self, x):
|
|
32
|
+
return x*self.scale + self.x0
|
|
33
|
+
|
|
34
|
+
def parent_to_frame(self, x):
|
|
35
|
+
return x*self.scale - self.x0
|
|
36
|
+
|
|
37
|
+
def add_parent_frame(self, parent):
|
|
38
|
+
"""
|
|
39
|
+
Adds a new parent at the top of the parent
|
|
40
|
+
hierarchy, replacing the old global frame.
|
|
41
|
+
Maybe dangerous in practice...
|
|
42
|
+
"""
|
|
43
|
+
if self.parent is None:
|
|
44
|
+
self.parent = parent
|
|
45
|
+
else:
|
|
46
|
+
self.parent.add_parent_frame(parent)
|
|
47
|
+
|
|
48
|
+
def frame_to_global(self, x_frame):
|
|
49
|
+
x_parent = self.frame_to_parent(x_frame)
|
|
50
|
+
if self.parent is not None:
|
|
51
|
+
return self.parent.frame_to_global(x_parent)
|
|
52
|
+
else:
|
|
53
|
+
return x_parent
|
|
54
|
+
|
|
55
|
+
def global_to_frame(self, x_global):
|
|
56
|
+
if self.parent is None:
|
|
57
|
+
return self.parent_to_frame(x_global)
|
|
58
|
+
else:
|
|
59
|
+
x_parent = self.parent.global_to_frame(x_global)
|
|
60
|
+
return self.parent_to_frame(x_parent)
|
|
61
|
+
|
|
62
|
+
def frame_to_beam(self, x_frame):
|
|
63
|
+
return self.frame_to_global(x_frame)
|
|
64
|
+
|
|
65
|
+
def beam_to_frame(self, x_global):
|
|
66
|
+
return self.global_to_frame(x_global)
|
|
67
|
+
|
|
68
|
+
def distance_to_beam(self, x_beam_global):
|
|
69
|
+
"""
|
|
70
|
+
Distance from beam to origin
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
-----------
|
|
74
|
+
x_beam_global : float
|
|
75
|
+
beam position in global coordinate system
|
|
76
|
+
"""
|
|
77
|
+
x = self.global_to_frame(x_beam_global)
|
|
78
|
+
return x
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Interval(Axis):
|
|
82
|
+
def __init__(self, x0, length, *args, parent=None):
|
|
83
|
+
self.length = length
|
|
84
|
+
super().__init__(x0, *args, parent=parent)
|
|
85
|
+
|
|
86
|
+
def frame_to_global(self, x, origin="edge"):
|
|
87
|
+
if origin == "center":
|
|
88
|
+
x += self.length/2.0
|
|
89
|
+
return super().frame_to_global(x)
|
|
90
|
+
|
|
91
|
+
def global_to_frame(self, x, origin="edge"):
|
|
92
|
+
x = super().global_to_frame(x)
|
|
93
|
+
if origin == "center":
|
|
94
|
+
x -= self.length/2.0
|
|
95
|
+
return x
|
|
96
|
+
|
|
97
|
+
def frame_to_beam(self, x_frame, **kwargs):
|
|
98
|
+
return self.frame_to_global(x_frame, **kwargs)
|
|
99
|
+
|
|
100
|
+
def beam_to_frame(self, x_global, **kwargs):
|
|
101
|
+
return self.global_to_frame(x_global, **kwargs)
|
|
102
|
+
|
|
103
|
+
def distance_to_beam(self, x_beam_global):
|
|
104
|
+
x = self.global_to_frame(x_beam_global)
|
|
105
|
+
d1 = np.abs(x)
|
|
106
|
+
d2 = np.abs(x - self.length)
|
|
107
|
+
if (d1 < self.length) and (d2 < self.length):
|
|
108
|
+
return -1*min(d1, d2)
|
|
109
|
+
else:
|
|
110
|
+
return min(d1, d2)
|
|
111
|
+
|
|
112
|
+
def make_sample_frame(self, position, t=0):
|
|
113
|
+
x1, x2 = position
|
|
114
|
+
frame = Interval(x1, x2 - x1, parent=self)
|
|
115
|
+
return frame
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class Frame:
|
|
119
|
+
"""
|
|
120
|
+
20220117: This entire thing is too complicated. Having a "manip" argument
|
|
121
|
+
was a really bad idea. I should have used a moveable manipulator frame
|
|
122
|
+
instead. Indeed, this is what I have essentially done, but the complexity
|
|
123
|
+
remains. There are also too many X_to_frame and frame_to_X functions.
|
|
124
|
+
There should have only been "parent_to_frame" and "global_to_frame". No
|
|
125
|
+
time to fix this now. Interval/Axis are done better
|
|
126
|
+
"""
|
|
127
|
+
def __init__(self, p1, p2, p3, parent=None, rot_meas_axis=2):
|
|
128
|
+
"""
|
|
129
|
+
Parameters
|
|
130
|
+
------------
|
|
131
|
+
p1 : vector
|
|
132
|
+
origin of the frame in the parent system
|
|
133
|
+
p2 : vector
|
|
134
|
+
defines the frame's y basis vector
|
|
135
|
+
p3 : vector
|
|
136
|
+
defines the plane of the x basis vector
|
|
137
|
+
parent : Frame, optional
|
|
138
|
+
rot_meas_axis : int
|
|
139
|
+
Which axis will be used to measure rotation relative to the
|
|
140
|
+
global coordinate system X-Y plane
|
|
141
|
+
"""
|
|
142
|
+
self.rot_meas_axis = rot_meas_axis
|
|
143
|
+
self.reset(p1, p2, p3, parent=parent)
|
|
144
|
+
|
|
145
|
+
def reset(self, p1, p2, p3, parent=None):
|
|
146
|
+
self.parent = parent
|
|
147
|
+
self.update_basis(p1, p2, p3)
|
|
148
|
+
self.update_rotation()
|
|
149
|
+
|
|
150
|
+
def update_basis(self, p1, p2, p3):
|
|
151
|
+
self.vectors = [p1, p2, p3]
|
|
152
|
+
self.p0 = p1
|
|
153
|
+
self._basis = constructBasis(p1, p2, p3)
|
|
154
|
+
# r_offset
|
|
155
|
+
self.A = changeBasisMatrix(*self._basis)
|
|
156
|
+
self.Ainv = self.A.T
|
|
157
|
+
|
|
158
|
+
def update_rotation(self):
|
|
159
|
+
self.r0 = rad_to_deg(self._roffset())
|
|
160
|
+
|
|
161
|
+
def add_parent_frame(self, parent):
|
|
162
|
+
"""
|
|
163
|
+
Adds a new parent at the top of the parent
|
|
164
|
+
hierarchy, replacing the old global frame.
|
|
165
|
+
"""
|
|
166
|
+
if self.parent is None:
|
|
167
|
+
self.parent = parent
|
|
168
|
+
else:
|
|
169
|
+
self.parent.add_parent_frame(parent)
|
|
170
|
+
self.update_rotation()
|
|
171
|
+
|
|
172
|
+
def reset_z(self, z, parent=None):
|
|
173
|
+
self.p0[2] = z
|
|
174
|
+
|
|
175
|
+
def _roffset(self):
|
|
176
|
+
"""
|
|
177
|
+
R offset relative to the GLOBAL frame (not the parent frame!)
|
|
178
|
+
|
|
179
|
+
Important note! It is a BAKED IN ASSUMPTION that
|
|
180
|
+
these frames are being used for beamline samples, and
|
|
181
|
+
that there is only one rotation axis.
|
|
182
|
+
"Rotation" means the angle that the rotation axis makes
|
|
183
|
+
with respect to the beam in the global x-y plane. This
|
|
184
|
+
makes sense because we only have a 4-axis manipulator.
|
|
185
|
+
If we had, god forbid, a 6-axis manipulator, none of this
|
|
186
|
+
1-axis rotation stuff would make sense, and you would just
|
|
187
|
+
have to specify an exact vector, rather than an angle.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
# we bootstrap the rotation by finding the z-vector in the global
|
|
191
|
+
# frame, even if
|
|
192
|
+
# we have a parent.
|
|
193
|
+
axis = vec(0, 0, 0)
|
|
194
|
+
axis[self.rot_meas_axis] = 1
|
|
195
|
+
n3 = (self.frame_to_global(axis, rotation='global') -
|
|
196
|
+
self.frame_to_global(vec(0, 0, 0), rotation='global'))
|
|
197
|
+
x = n3[0]
|
|
198
|
+
y = n3[1]
|
|
199
|
+
|
|
200
|
+
if x > 0 and y >= 0:
|
|
201
|
+
return np.arctan(np.abs(y/x))
|
|
202
|
+
elif x <= 0 and y > 0:
|
|
203
|
+
return np.arctan(np.abs(x/y)) + np.pi/2.0
|
|
204
|
+
elif x < 0 and y <= 0:
|
|
205
|
+
return np.arctan(np.abs(y/x)) + np.pi
|
|
206
|
+
else:
|
|
207
|
+
return np.arctan(np.abs(x/y)) + np.pi*3/2.0
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _to_global(self, v):
|
|
211
|
+
return np.dot(self.A, v) + self.p0
|
|
212
|
+
|
|
213
|
+
def _to_frame(self, v):
|
|
214
|
+
return np.dot(self.Ainv, v - self.p0)
|
|
215
|
+
|
|
216
|
+
def _manip_to_global(self, v_manip, manip, r):
|
|
217
|
+
theta = deg_to_rad(r)
|
|
218
|
+
v_global = rotz(-theta, v_manip) + manip
|
|
219
|
+
return v_global
|
|
220
|
+
|
|
221
|
+
def _global_to_manip(self, v_global, manip, r):
|
|
222
|
+
theta = deg_to_rad(r)
|
|
223
|
+
v_manip = rotz(theta, v_global - manip)
|
|
224
|
+
return v_manip
|
|
225
|
+
|
|
226
|
+
def frame_to_global(self, v_frame, manip=vec(0, 0, 0), r=0,
|
|
227
|
+
rotation="frame"):
|
|
228
|
+
"""
|
|
229
|
+
Find the global coordinates of a point in the frame, given the
|
|
230
|
+
rotation of the frame, and the manipulator position
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
------------
|
|
234
|
+
v_frame : vector
|
|
235
|
+
Coordinates of a point in the frame system
|
|
236
|
+
manip : vector
|
|
237
|
+
Manipulator coordinates
|
|
238
|
+
r : float, degrees
|
|
239
|
+
rotation of the frame
|
|
240
|
+
(0 = grazing incidence, 90 = normal)
|
|
241
|
+
"""
|
|
242
|
+
if rotation == 'frame':
|
|
243
|
+
rg = r + self.r0
|
|
244
|
+
else:
|
|
245
|
+
rg = r
|
|
246
|
+
v_global = self._to_global(v_frame)
|
|
247
|
+
if self.parent is not None:
|
|
248
|
+
return self.parent.frame_to_global(v_global, manip, r=rg,
|
|
249
|
+
rotation='global')
|
|
250
|
+
else:
|
|
251
|
+
v_global = self._manip_to_global(v_global, manip, rg)
|
|
252
|
+
return v_global
|
|
253
|
+
|
|
254
|
+
def global_to_frame(self, v_global, manip=vec(0, 0, 0), r=0):
|
|
255
|
+
"""
|
|
256
|
+
Find the frame coordinates of a point in the global system,
|
|
257
|
+
given the manipulator position and rotation
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
-----------
|
|
261
|
+
v_global : vector
|
|
262
|
+
global vector
|
|
263
|
+
manip : vector
|
|
264
|
+
current position of manipulator
|
|
265
|
+
r : float, degrees
|
|
266
|
+
rotation of the manipulator
|
|
267
|
+
"""
|
|
268
|
+
if self.parent is not None:
|
|
269
|
+
v_manip = self.parent.global_to_frame(v_global, manip, r)
|
|
270
|
+
else:
|
|
271
|
+
v_manip = self._global_to_manip(v_global, manip, r)
|
|
272
|
+
v_frame = self._to_frame(v_manip)
|
|
273
|
+
return v_frame
|
|
274
|
+
|
|
275
|
+
def frame_to_beam(self, fx, fy, fz, fr=0, **kwargs):
|
|
276
|
+
"""
|
|
277
|
+
Given a frame coordinate, and rotation, find the manipulator position
|
|
278
|
+
and rotation that places the frame coordinate in the beam path.
|
|
279
|
+
The beam position is assumed to be the origin of the global coordinate
|
|
280
|
+
system.
|
|
281
|
+
|
|
282
|
+
Returns
|
|
283
|
+
--------
|
|
284
|
+
coordinates : tuple
|
|
285
|
+
The x, y, z, r coordinates of the manipulator that put the
|
|
286
|
+
frame coordinate into the beam path
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
v_frame = vec(fx, fy, fz)
|
|
290
|
+
v_global = -1*self.frame_to_global(v_frame, r=fr)
|
|
291
|
+
gr = fr + self.r0
|
|
292
|
+
gx, gy, gz = (v_global[0], v_global[1], v_global[2])
|
|
293
|
+
return gx, gy, gz, gr
|
|
294
|
+
|
|
295
|
+
def beam_to_frame(self, gx, gy, gz, gr=0, **kwargs):
|
|
296
|
+
"""
|
|
297
|
+
Given a manipulator coordinate and rotation, find the beam intersection
|
|
298
|
+
position and incidence angle in the frame coordinates. The beam
|
|
299
|
+
position is assumed to be the origin of the global coordinate system.
|
|
300
|
+
|
|
301
|
+
Parameters
|
|
302
|
+
------------
|
|
303
|
+
gx : float
|
|
304
|
+
manipulator x coordinate
|
|
305
|
+
gy : float
|
|
306
|
+
manipulator y coordinate
|
|
307
|
+
gz : float
|
|
308
|
+
manipulator z coordinate
|
|
309
|
+
gr : float, degrees
|
|
310
|
+
manipulator r coordinate
|
|
311
|
+
|
|
312
|
+
Returns
|
|
313
|
+
--------
|
|
314
|
+
coordinates : tuple
|
|
315
|
+
The x, y, z, r coordinates of the beam in the frame system
|
|
316
|
+
"""
|
|
317
|
+
manip = vec(gx, gy, gz)
|
|
318
|
+
v_frame = self.origin_to_frame(manip, gr)
|
|
319
|
+
fx, fy, fz = (v_frame[0], v_frame[1], v_frame[2])
|
|
320
|
+
fr = gr - self.r0
|
|
321
|
+
return fx, fy, fz, fr
|
|
322
|
+
|
|
323
|
+
def origin_to_frame(self, manip=vec(0, 0, 0), r=0):
|
|
324
|
+
return self.global_to_frame(vec(0, 0, 0), manip, r)
|
|
325
|
+
|
|
326
|
+
def project_beam_to_frame_xy(self, manip=vec(0, 0, 0), r=0):
|
|
327
|
+
op = self.origin_to_frame(manip, r)
|
|
328
|
+
theta = deg_to_rad(r)
|
|
329
|
+
Rz_inv = rotzMat(-theta)
|
|
330
|
+
vp = np.dot(self.Ainv, np.dot(Rz_inv, vec(0, 1, 0)))
|
|
331
|
+
a = op[-1]/vp[-1]
|
|
332
|
+
proj = op - a*vp
|
|
333
|
+
return proj
|
|
334
|
+
|
|
335
|
+
def distance_to_beam(self, gx, gy, gz, gr=0):
|
|
336
|
+
"""
|
|
337
|
+
Given the manipulator coordinate (and rotation, for consistency),
|
|
338
|
+
find the distance from the beam to the coordinate origin, ignoring
|
|
339
|
+
the beam y-axis
|
|
340
|
+
|
|
341
|
+
Parameters
|
|
342
|
+
------------
|
|
343
|
+
gx : float
|
|
344
|
+
manipulator x coordinate
|
|
345
|
+
gy : float
|
|
346
|
+
manipulator y coordinate
|
|
347
|
+
gz : float
|
|
348
|
+
manipulator z coordinate
|
|
349
|
+
gr : float, degrees
|
|
350
|
+
manipulator r coordinate
|
|
351
|
+
|
|
352
|
+
Returns
|
|
353
|
+
--------
|
|
354
|
+
distance : float
|
|
355
|
+
Distance from the global y-axis (the beam) to the coordinate origin
|
|
356
|
+
"""
|
|
357
|
+
op = self.frame_to_global(vec(0, 0, 0), manip=vec(gx, gy, gz), r=gr)
|
|
358
|
+
distance = np.sqrt(op[0]**2 + op[2]**2)
|
|
359
|
+
return distance
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class Panel(Frame):
|
|
363
|
+
"""
|
|
364
|
+
A frame that has boundaries, making it a rectangle
|
|
365
|
+
|
|
366
|
+
Parameters
|
|
367
|
+
------------
|
|
368
|
+
p1 : vector
|
|
369
|
+
origin of the frame in the parent system
|
|
370
|
+
p2 : vector
|
|
371
|
+
defines the frame's y basis vector
|
|
372
|
+
p3 : vector
|
|
373
|
+
defines the plane of the x basis vector
|
|
374
|
+
parent : Frame, optional
|
|
375
|
+
|
|
376
|
+
"""
|
|
377
|
+
def __init__(self, *args, width=19.5, height=130, parent=None):
|
|
378
|
+
super().__init__(*args, parent=parent)
|
|
379
|
+
self.width = width
|
|
380
|
+
self.height = height
|
|
381
|
+
self.edges = [vec(0, 0, 0), vec(width, 0, 0), vec(width, height, 0),
|
|
382
|
+
vec(0, height, 0)]
|
|
383
|
+
|
|
384
|
+
def frame_to_beam(self, fx, fy, fz, fr=0, origin="edge"):
|
|
385
|
+
if origin == "center":
|
|
386
|
+
fx += self.width/2.0
|
|
387
|
+
fy += self.height/2.0
|
|
388
|
+
return super().frame_to_beam(fx, fy, fz, fr)
|
|
389
|
+
|
|
390
|
+
def beam_to_frame(self, gx, gy, gz, gr=0, origin="edge"):
|
|
391
|
+
fx, fy, fz, fr = super().beam_to_frame(gx, gy, gz, gr)
|
|
392
|
+
if origin == "center":
|
|
393
|
+
fx -= self.width/2.0
|
|
394
|
+
fy -= self.height/2.0
|
|
395
|
+
return fx, fy, fz, fr
|
|
396
|
+
|
|
397
|
+
def real_edges(self, manip, r_manip):
|
|
398
|
+
"""
|
|
399
|
+
Finds the vertices of the panel in global coordinate system,
|
|
400
|
+
given the manipulator position
|
|
401
|
+
|
|
402
|
+
Parameters
|
|
403
|
+
-----------
|
|
404
|
+
manip : vector
|
|
405
|
+
Manipulator x,y,z position vector
|
|
406
|
+
r_manip : float
|
|
407
|
+
Manipulator rotation in degrees
|
|
408
|
+
|
|
409
|
+
Returns
|
|
410
|
+
--------
|
|
411
|
+
Vertex positions in the global frame
|
|
412
|
+
"""
|
|
413
|
+
re = []
|
|
414
|
+
for edge in self.edges:
|
|
415
|
+
real_coord = self.frame_to_global(edge, manip, r_manip,
|
|
416
|
+
rotation='global')
|
|
417
|
+
re.append(real_coord)
|
|
418
|
+
return re
|
|
419
|
+
|
|
420
|
+
def project_real_edges(self, manip, r_manip):
|
|
421
|
+
"""
|
|
422
|
+
|
|
423
|
+
Parameters
|
|
424
|
+
------------
|
|
425
|
+
manip : vector
|
|
426
|
+
Manipulator x,y,z position vector
|
|
427
|
+
r_manip : float
|
|
428
|
+
Manipulator rotation in degrees
|
|
429
|
+
|
|
430
|
+
Returns
|
|
431
|
+
--------
|
|
432
|
+
Vertex coordinates projected into the x-z axis
|
|
433
|
+
"""
|
|
434
|
+
|
|
435
|
+
re = self.real_edges(manip, r_manip)
|
|
436
|
+
ret = []
|
|
437
|
+
for edge in re:
|
|
438
|
+
ret.append(np.array([edge[0], edge[2]]))
|
|
439
|
+
return ret
|
|
440
|
+
|
|
441
|
+
def distance_to_beam(self, x, y, z, r):
|
|
442
|
+
"""
|
|
443
|
+
Returns the distance from the beam to the closest edge of
|
|
444
|
+
the Panel, given a manipulator position of x, y, z, r
|
|
445
|
+
|
|
446
|
+
Parameters
|
|
447
|
+
-------------
|
|
448
|
+
x : float
|
|
449
|
+
manipulator x coordinate
|
|
450
|
+
y : float
|
|
451
|
+
manipulator y coordinate
|
|
452
|
+
z : float
|
|
453
|
+
manipulator z coordinate
|
|
454
|
+
r : float
|
|
455
|
+
manipulator r coordinate (in degrees)
|
|
456
|
+
|
|
457
|
+
Returns
|
|
458
|
+
----------
|
|
459
|
+
distance : float
|
|
460
|
+
The sign of distance is negative if the beam is inside the Panel,
|
|
461
|
+
and positive if the beam is outside the Panel
|
|
462
|
+
"""
|
|
463
|
+
|
|
464
|
+
manip = vec(x, y, z)
|
|
465
|
+
real_edges = self.project_real_edges(manip, r)
|
|
466
|
+
inPoly = isInPoly(vec(0, 0), *real_edges)
|
|
467
|
+
distance = getMinDist(vec(0, 0), *real_edges)
|
|
468
|
+
if inPoly:
|
|
469
|
+
return -1*distance
|
|
470
|
+
else:
|
|
471
|
+
return distance
|
|
472
|
+
|
|
473
|
+
def make_sample_frame(self, position, t=0):
|
|
474
|
+
if len(position) == 4:
|
|
475
|
+
x1, y1, x2, y2 = position
|
|
476
|
+
p1 = vec(x1, y1, t)
|
|
477
|
+
p2 = vec(x1, y2, t)
|
|
478
|
+
p3 = vec(x2, y1, t)
|
|
479
|
+
width = x2 - x1
|
|
480
|
+
height = y2 - y1
|
|
481
|
+
|
|
482
|
+
frame = Panel(p1, p2, p3, height=height, width=width,
|
|
483
|
+
parent=self)
|
|
484
|
+
return frame
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def make_geometry(*args, **kwargs):
|
|
488
|
+
if len(args) == 3:
|
|
489
|
+
if "height" in kwargs and "width" in kwargs:
|
|
490
|
+
return Panel(*args, **kwargs)
|
|
491
|
+
else:
|
|
492
|
+
return Frame(*args, **kwargs)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def make_regular_polygon(width, height, nsides, points=None, parent=None, invert=True):
|
|
496
|
+
geometry = []
|
|
497
|
+
interior_angle = deg_to_rad(360.0 / nsides)
|
|
498
|
+
if invert:
|
|
499
|
+
az = -1
|
|
500
|
+
else:
|
|
501
|
+
az = 1
|
|
502
|
+
|
|
503
|
+
if points is None:
|
|
504
|
+
y = -1 * az * width / 2.0
|
|
505
|
+
x = width / (2.0 * np.tan(interior_angle / 2.0))
|
|
506
|
+
if invert:
|
|
507
|
+
z = height
|
|
508
|
+
else:
|
|
509
|
+
z = 0
|
|
510
|
+
p1 = vec(x, y, z)
|
|
511
|
+
p2 = p1 + vec(0, 0, az)
|
|
512
|
+
p3 = p1 + vec(0, az, 0)
|
|
513
|
+
else:
|
|
514
|
+
p1, p2, p3 = points
|
|
515
|
+
|
|
516
|
+
def _newSideFromSide(side):
|
|
517
|
+
prev_edges = side.real_edges(vec(0, 0, 0), 0)
|
|
518
|
+
new_vector = vec(np.cos(np.pi - interior_angle), 0, -np.sin(np.pi - interior_angle))
|
|
519
|
+
p1 = prev_edges[1]
|
|
520
|
+
p2 = prev_edges[2]
|
|
521
|
+
p3 = side.frame_to_global(new_vector + side.edges[1], r=0, rotation="global")
|
|
522
|
+
return Panel(p1, p2, p3, width=width, height=height, parent=parent)
|
|
523
|
+
|
|
524
|
+
current_side = Panel(p1, p2, p3, width=width, height=height, parent=parent)
|
|
525
|
+
geometry.append(current_side)
|
|
526
|
+
new_sides = []
|
|
527
|
+
for n in range(1, nsides):
|
|
528
|
+
new_side = _newSideFromSide(current_side)
|
|
529
|
+
new_sides.append(new_side)
|
|
530
|
+
current_side = new_side
|
|
531
|
+
# It is unfortunately easier to generate sides in reverse order
|
|
532
|
+
# due to the coordinate system, so we must reverse them.
|
|
533
|
+
geometry += new_sides[::-1]
|
|
534
|
+
return geometry
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Module that implements basic linear algebra as it relates to vectors and
|
|
5
|
+
basis transformation/construction
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def vec(*args):
|
|
10
|
+
"""
|
|
11
|
+
Construct a vector from components
|
|
12
|
+
"""
|
|
13
|
+
return np.array(args, dtype="float64")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def normVector(v):
|
|
17
|
+
"""
|
|
18
|
+
Return the unit vector of v
|
|
19
|
+
"""
|
|
20
|
+
n = np.sqrt(np.dot(v, v))
|
|
21
|
+
return v/n
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def vec_len(v):
|
|
25
|
+
"""
|
|
26
|
+
Find the magnitude of vector v
|
|
27
|
+
"""
|
|
28
|
+
return np.sqrt(np.dot(v, v))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def vec_angle(v1, v2):
|
|
32
|
+
"""
|
|
33
|
+
Find the angle between v1 and v2
|
|
34
|
+
"""
|
|
35
|
+
return np.arccos(np.dot(v1, v2)/(vec_len(v1)*vec_len(v2)))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def findOrthonormal(v1, v2):
|
|
39
|
+
"""
|
|
40
|
+
Find the length 1 vector normal to v1 and v2
|
|
41
|
+
"""
|
|
42
|
+
v3 = np.cross(v1, v2)
|
|
43
|
+
return normVector(v3)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def constructBasis(p1, p2, p3):
|
|
47
|
+
"""
|
|
48
|
+
Construct a basis from three points
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
------------
|
|
52
|
+
p1 : vector
|
|
53
|
+
The origin of the vector space
|
|
54
|
+
p2 : vector
|
|
55
|
+
defines the y basis vector
|
|
56
|
+
p3 : vector
|
|
57
|
+
defines the plane of the x basis vector
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
----------
|
|
61
|
+
Returns three vectors, n1, n2, and n3, that form a basis.
|
|
62
|
+
n2 : vector
|
|
63
|
+
the "y" vector, is defined by p2 - p1
|
|
64
|
+
n3 : vector
|
|
65
|
+
the "z" vector, is defined as being orthornormal to n2 and p3-p1,
|
|
66
|
+
n1 : vector
|
|
67
|
+
the "x" vector, is then uniquely defined as orthonormal to n2 and n3
|
|
68
|
+
"""
|
|
69
|
+
v1 = p3 - p1
|
|
70
|
+
v2 = p2 - p1
|
|
71
|
+
n2 = normVector(v2)
|
|
72
|
+
n3 = findOrthonormal(v1, n2)
|
|
73
|
+
n1 = findOrthonormal(n2, n3)
|
|
74
|
+
return n1, n2, n3
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def changeBasisMatrix(n1, n2, n3):
|
|
78
|
+
return np.vstack([n1, n2, n3]).T
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def constructBorders(p1, p2, p3, sidelength=1):
|
|
82
|
+
n1, n2, n3 = constructBasis(p1, p2, p3)
|
|
83
|
+
origin = p1
|
|
84
|
+
origin2 = n1*sidelength + origin
|
|
85
|
+
borders = {'n1': n1, 'n2': n2,
|
|
86
|
+
'o1': origin, 'o2': origin2}
|
|
87
|
+
return borders
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def rotzMat(theta):
|
|
91
|
+
"""
|
|
92
|
+
Parameters
|
|
93
|
+
-----------
|
|
94
|
+
theta : float, radians
|
|
95
|
+
"""
|
|
96
|
+
return np.array([[np.cos(theta), -np.sin(theta), 0],
|
|
97
|
+
[np.sin(theta), np.cos(theta), 0],
|
|
98
|
+
[0, 0, 1]])
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def rotyMat(theta):
|
|
102
|
+
return np.array([[np.cos(theta), 0, np.sin(theta)],
|
|
103
|
+
[0, 1, 0],
|
|
104
|
+
[-np.sin(theta), 0, np.cos(theta)]
|
|
105
|
+
])
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def rotxMat(theta):
|
|
109
|
+
return np.array([[1, 0, 0],
|
|
110
|
+
[0, np.cos(theta), -np.sin(theta)],
|
|
111
|
+
[0, np.sin(theta), np.cos(theta)]
|
|
112
|
+
])
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def rotz(theta, v):
|
|
116
|
+
"""
|
|
117
|
+
Rotate a vector by theta around the z axis
|
|
118
|
+
"""
|
|
119
|
+
rz = rotzMat(theta)
|
|
120
|
+
return np.dot(rz, v)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def roty(theta, v):
|
|
124
|
+
ry = rotyMat(theta)
|
|
125
|
+
return np.dot(ry, v)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def rotx(theta, v):
|
|
129
|
+
rx = rotxMat(theta)
|
|
130
|
+
return np.dot(rx, v)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def deg_to_rad(d):
|
|
134
|
+
return d*np.pi/180.0
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def rad_to_deg(d):
|
|
138
|
+
return d*180.0/np.pi
|