syned 1.0.47__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.
- syned/__init__.py +0 -0
- syned/__test/__init__.py +46 -0
- syned/__test/test.py +28 -0
- syned/beamline/__init__.py +1 -0
- syned/beamline/beamline.py +155 -0
- syned/beamline/beamline_element.py +76 -0
- syned/beamline/element_coordinates.py +199 -0
- syned/beamline/optical_element.py +47 -0
- syned/beamline/optical_element_with_surface_shape.py +126 -0
- syned/beamline/optical_elements/__init__.py +1 -0
- syned/beamline/optical_elements/absorbers/__init__.py +0 -0
- syned/beamline/optical_elements/absorbers/absorber.py +21 -0
- syned/beamline/optical_elements/absorbers/beam_stopper.py +64 -0
- syned/beamline/optical_elements/absorbers/filter.py +61 -0
- syned/beamline/optical_elements/absorbers/holed_filter.py +67 -0
- syned/beamline/optical_elements/absorbers/slit.py +81 -0
- syned/beamline/optical_elements/crystals/__init__.py +1 -0
- syned/beamline/optical_elements/crystals/crystal.py +70 -0
- syned/beamline/optical_elements/gratings/__init__.py +1 -0
- syned/beamline/optical_elements/gratings/grating.py +279 -0
- syned/beamline/optical_elements/ideal_elements/__init__.py +1 -0
- syned/beamline/optical_elements/ideal_elements/ideal_element.py +16 -0
- syned/beamline/optical_elements/ideal_elements/ideal_fzp.py +183 -0
- syned/beamline/optical_elements/ideal_elements/ideal_lens.py +54 -0
- syned/beamline/optical_elements/ideal_elements/screen.py +16 -0
- syned/beamline/optical_elements/mirrors/__init__.py +1 -0
- syned/beamline/optical_elements/mirrors/mirror.py +39 -0
- syned/beamline/optical_elements/multilayers/__init__.py +46 -0
- syned/beamline/optical_elements/multilayers/multilayer.py +45 -0
- syned/beamline/optical_elements/refractors/__init__.py +1 -0
- syned/beamline/optical_elements/refractors/crl.py +79 -0
- syned/beamline/optical_elements/refractors/interface.py +61 -0
- syned/beamline/optical_elements/refractors/lens.py +105 -0
- syned/beamline/shape.py +2803 -0
- syned/storage_ring/__init__.py +1 -0
- syned/storage_ring/electron_beam.py +804 -0
- syned/storage_ring/empty_light_source.py +40 -0
- syned/storage_ring/light_source.py +90 -0
- syned/storage_ring/magnetic_structure.py +8 -0
- syned/storage_ring/magnetic_structures/__init__.py +1 -0
- syned/storage_ring/magnetic_structures/bending_magnet.py +329 -0
- syned/storage_ring/magnetic_structures/insertion_device.py +169 -0
- syned/storage_ring/magnetic_structures/undulator.py +413 -0
- syned/storage_ring/magnetic_structures/wiggler.py +27 -0
- syned/syned_object.py +264 -0
- syned/util/__init__.py +22 -0
- syned/util/json_tools.py +198 -0
- syned/widget/__init__.py +0 -0
- syned/widget/widget_decorator.py +67 -0
- syned-1.0.47.dist-info/METADATA +88 -0
- syned-1.0.47.dist-info/RECORD +54 -0
- syned-1.0.47.dist-info/WHEEL +5 -0
- syned-1.0.47.dist-info/licenses/LICENSE +20 -0
- syned-1.0.47.dist-info/top_level.txt +1 -0
syned/beamline/shape.py
ADDED
|
@@ -0,0 +1,2803 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Classes with geometrical shapes.
|
|
3
|
+
|
|
4
|
+
* Shape: Base class for all possible geometric shapes.
|
|
5
|
+
* SurfaceShape: Base class to caracterize the shape (sphere, flat, etc.) of the optical element surface
|
|
6
|
+
* BoundaryShape: Base class to characterize the optical element dimensions (rectangle, etc.).
|
|
7
|
+
|
|
8
|
+
Additional classes help to define flags:
|
|
9
|
+
* Convexity: NONE = -1, UPWARD = 0, DOWNWARD = 1
|
|
10
|
+
* Direction: TANGENTIAL = 0, SAGITTAL = 1
|
|
11
|
+
* Side: SOURCE = 0, IMAGE = 1
|
|
12
|
+
"""
|
|
13
|
+
import numpy
|
|
14
|
+
from syned.syned_object import SynedObject
|
|
15
|
+
from collections import OrderedDict
|
|
16
|
+
|
|
17
|
+
class Convexity:
|
|
18
|
+
NONE = -1
|
|
19
|
+
UPWARD = 0
|
|
20
|
+
DOWNWARD = 1
|
|
21
|
+
|
|
22
|
+
class Direction:
|
|
23
|
+
TANGENTIAL = 0
|
|
24
|
+
SAGITTAL = 1
|
|
25
|
+
|
|
26
|
+
class Side:
|
|
27
|
+
SOURCE = 0
|
|
28
|
+
IMAGE = 1
|
|
29
|
+
|
|
30
|
+
class Shape(SynedObject):
|
|
31
|
+
"""
|
|
32
|
+
Constructor.
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
def __init__(self):
|
|
36
|
+
SynedObject.__init__(self)
|
|
37
|
+
|
|
38
|
+
class SurfaceShape(Shape):
|
|
39
|
+
"""
|
|
40
|
+
Constructor.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
convexity : int (as defined by Convexity), optional
|
|
45
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
def __init__(self, convexity = Convexity.UPWARD):
|
|
49
|
+
Shape.__init__(self)
|
|
50
|
+
|
|
51
|
+
self._convexity = convexity
|
|
52
|
+
|
|
53
|
+
def get_convexity(self):
|
|
54
|
+
"""
|
|
55
|
+
Gets the convexity flag.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
int
|
|
60
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
return self._convexity
|
|
64
|
+
|
|
65
|
+
class BoundaryShape(Shape):
|
|
66
|
+
"""
|
|
67
|
+
Constructor.
|
|
68
|
+
"""
|
|
69
|
+
def __init__(self):
|
|
70
|
+
Shape.__init__(self)
|
|
71
|
+
|
|
72
|
+
def get_boundaries(self):
|
|
73
|
+
"""
|
|
74
|
+
Returns the boundary shape. It must be defined in the children classes.
|
|
75
|
+
|
|
76
|
+
Raises
|
|
77
|
+
------
|
|
78
|
+
NotImplementedError
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
raise NotImplementedError()
|
|
83
|
+
|
|
84
|
+
#############################
|
|
85
|
+
# Subclasses for SurfaceShape
|
|
86
|
+
#############################
|
|
87
|
+
|
|
88
|
+
class Cylinder(SynedObject):
|
|
89
|
+
"""
|
|
90
|
+
Defines that a surface shape is cylindrical in one direction.
|
|
91
|
+
|
|
92
|
+
Usage: must be used with double inheritance in other classes (e.g. ParabolicCylinder).
|
|
93
|
+
It should not be used standalone.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
cylinder_direction : int, optional
|
|
98
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
def __init__(self, cylinder_direction=Direction.TANGENTIAL):
|
|
102
|
+
self._cylinder_direction = cylinder_direction
|
|
103
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
104
|
+
self._add_support_text([
|
|
105
|
+
("cylinder_direction" , "(0=tangential, 1=sagittal)", " " ),
|
|
106
|
+
] )
|
|
107
|
+
|
|
108
|
+
def get_cylinder_direction(self):
|
|
109
|
+
"""
|
|
110
|
+
Returns the cylinder direction.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
int
|
|
115
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
116
|
+
|
|
117
|
+
"""
|
|
118
|
+
return self._cylinder_direction
|
|
119
|
+
|
|
120
|
+
class Conic(SurfaceShape):
|
|
121
|
+
"""
|
|
122
|
+
Defines a conic surface shape expresses via the 10 conic coeffcients.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
conic_coefficients : list, optional
|
|
127
|
+
A list with the 10 coefficients.
|
|
128
|
+
|
|
129
|
+
"""
|
|
130
|
+
def __init__(self,
|
|
131
|
+
conic_coefficients=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]):
|
|
132
|
+
SurfaceShape.__init__(self, convexity=Convexity.NONE)
|
|
133
|
+
|
|
134
|
+
# stored as numpy array, not as list, to avoid in i/o to interpret the items as syned objects.
|
|
135
|
+
self._conic_coefficients = numpy.array(conic_coefficients)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
self._set_support_text([
|
|
139
|
+
("conic_coefficients" , "Conic coeffs. ", " " ),
|
|
140
|
+
] )
|
|
141
|
+
|
|
142
|
+
def get_conic_coefficients(self):
|
|
143
|
+
"""
|
|
144
|
+
Returns the coefficients.
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
list
|
|
149
|
+
A list with the 10 coefficients.
|
|
150
|
+
|
|
151
|
+
"""
|
|
152
|
+
return list(self._conic_coefficients)
|
|
153
|
+
|
|
154
|
+
class Plane(SurfaceShape):
|
|
155
|
+
"""
|
|
156
|
+
Defines a plane surface shape.
|
|
157
|
+
"""
|
|
158
|
+
def __init__(self):
|
|
159
|
+
SurfaceShape.__init__(self, convexity=Convexity.NONE)
|
|
160
|
+
|
|
161
|
+
class Sphere(SurfaceShape):
|
|
162
|
+
"""
|
|
163
|
+
Defines an spherical surface.
|
|
164
|
+
|
|
165
|
+
Parameters
|
|
166
|
+
----------
|
|
167
|
+
radius : float
|
|
168
|
+
The sphere radius.
|
|
169
|
+
convexity : int (as defined by Convexity), optional
|
|
170
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
def __init__(self, radius=1.0, convexity=Convexity.UPWARD):
|
|
174
|
+
SurfaceShape.__init__(self, convexity=convexity)
|
|
175
|
+
self._radius = radius
|
|
176
|
+
|
|
177
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
178
|
+
self._set_support_text([
|
|
179
|
+
("radius" , "Sphere radius ", "m" ),
|
|
180
|
+
("convexity" , "(0=upwards, 1=downwards)", " "),
|
|
181
|
+
] )
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@classmethod
|
|
185
|
+
def create_sphere_from_radius(cls, radius=0.0, convexity=Convexity.UPWARD):
|
|
186
|
+
"""
|
|
187
|
+
Defines an spherical surface.
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
radius : float
|
|
192
|
+
The sphere radius.
|
|
193
|
+
convexity : int (as defined by Convexity), optional
|
|
194
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
instance of Sphere
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
return Sphere(radius, convexity)
|
|
202
|
+
|
|
203
|
+
@classmethod
|
|
204
|
+
def create_sphere_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003, convexity=Convexity.UPWARD):
|
|
205
|
+
"""
|
|
206
|
+
Defines an spherical surface.
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
p : float, optional
|
|
211
|
+
distance source-optical element.
|
|
212
|
+
q : float, optional
|
|
213
|
+
distance optical element to focus.
|
|
214
|
+
grazing_angle : float, optional
|
|
215
|
+
grazing angle in rad.
|
|
216
|
+
convexity : int (as defined by Convexity), optional
|
|
217
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
instance of Sphere
|
|
222
|
+
|
|
223
|
+
"""
|
|
224
|
+
sphere = Sphere(convexity=convexity)
|
|
225
|
+
sphere.initialize_from_p_q(p, q, grazing_angle)
|
|
226
|
+
|
|
227
|
+
return sphere
|
|
228
|
+
|
|
229
|
+
def initialize_from_p_q(self, p=2.0, q=1.0, grazing_angle=0.003):
|
|
230
|
+
"""
|
|
231
|
+
Defines an spherical surface.
|
|
232
|
+
|
|
233
|
+
Parameters
|
|
234
|
+
----------
|
|
235
|
+
p : float, optional
|
|
236
|
+
distance source-optical element.
|
|
237
|
+
q : float, optional
|
|
238
|
+
distance optical element to focus.
|
|
239
|
+
grazing_angle : float, optional
|
|
240
|
+
grazing angle in rad.
|
|
241
|
+
|
|
242
|
+
"""
|
|
243
|
+
self._radius = Sphere.get_radius_from_p_q(p, q, grazing_angle)
|
|
244
|
+
|
|
245
|
+
@classmethod
|
|
246
|
+
def get_radius_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003):
|
|
247
|
+
"""
|
|
248
|
+
Calculates the radius of the sphere from factory parameters (1/p+1/q=2/(R sin theta_grazing)))
|
|
249
|
+
|
|
250
|
+
Parameters
|
|
251
|
+
----------
|
|
252
|
+
p : float, optional
|
|
253
|
+
distance source-optical element.
|
|
254
|
+
q : float, optional
|
|
255
|
+
distance optical element to focus.
|
|
256
|
+
grazing_angle : float, optional
|
|
257
|
+
grazing angle in rad.
|
|
258
|
+
|
|
259
|
+
Returns
|
|
260
|
+
-------
|
|
261
|
+
float
|
|
262
|
+
the calculated radius.
|
|
263
|
+
|
|
264
|
+
"""
|
|
265
|
+
# 1/p + 1/q = 2/(R cos(pi/2 - gr.a.))
|
|
266
|
+
return (2*p*q/(p+q))/numpy.sin(grazing_angle)
|
|
267
|
+
|
|
268
|
+
def get_radius(self):
|
|
269
|
+
"""
|
|
270
|
+
Returns the radius of the sphere.
|
|
271
|
+
|
|
272
|
+
Returns
|
|
273
|
+
-------
|
|
274
|
+
float
|
|
275
|
+
The radius of the sphere.
|
|
276
|
+
|
|
277
|
+
"""
|
|
278
|
+
return self._radius
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class SphericalCylinder(Sphere, Cylinder):
|
|
282
|
+
"""
|
|
283
|
+
Constructor.
|
|
284
|
+
|
|
285
|
+
Parameters
|
|
286
|
+
----------
|
|
287
|
+
radius : float
|
|
288
|
+
the radius of the circular section.
|
|
289
|
+
convexity : int (as defined by Convexity), optional
|
|
290
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
291
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
292
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
293
|
+
|
|
294
|
+
"""
|
|
295
|
+
def __init__(self,
|
|
296
|
+
radius=1.0,
|
|
297
|
+
convexity=Convexity.UPWARD,
|
|
298
|
+
cylinder_direction=Direction.TANGENTIAL):
|
|
299
|
+
Sphere.__init__(self, radius, convexity)
|
|
300
|
+
Cylinder.__init__(self, cylinder_direction)
|
|
301
|
+
|
|
302
|
+
@classmethod
|
|
303
|
+
def create_spherical_cylinder_from_radius(cls, radius=0.0, convexity=Convexity.UPWARD, cylinder_direction=Direction.TANGENTIAL):
|
|
304
|
+
"""
|
|
305
|
+
Creates a spherical cylinder.
|
|
306
|
+
|
|
307
|
+
Parameters
|
|
308
|
+
----------
|
|
309
|
+
radius : float
|
|
310
|
+
the radius of the circular section.
|
|
311
|
+
convexity : int (as defined by Convexity), optional
|
|
312
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
313
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
314
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
instance of SphericalCylinder
|
|
319
|
+
|
|
320
|
+
"""
|
|
321
|
+
return SphericalCylinder(radius, convexity, cylinder_direction)
|
|
322
|
+
|
|
323
|
+
@classmethod
|
|
324
|
+
def create_spherical_cylinder_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003,
|
|
325
|
+
convexity=Convexity.UPWARD, cylinder_direction=Direction.TANGENTIAL):
|
|
326
|
+
"""
|
|
327
|
+
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
p : float, optional
|
|
331
|
+
distance source-optical element.
|
|
332
|
+
q : float, optional
|
|
333
|
+
distance optical element to focus.
|
|
334
|
+
grazing_angle : float, optional
|
|
335
|
+
grazing angle in rad.
|
|
336
|
+
convexity : int (as defined by Convexity), optional
|
|
337
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
338
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
339
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
340
|
+
|
|
341
|
+
Returns
|
|
342
|
+
-------
|
|
343
|
+
instance of SphericalCylinder
|
|
344
|
+
|
|
345
|
+
"""
|
|
346
|
+
spherical_cylinder = SphericalCylinder(convexity=convexity, cylinder_direction=cylinder_direction)
|
|
347
|
+
spherical_cylinder.initialize_from_p_q(p, q, grazing_angle)
|
|
348
|
+
|
|
349
|
+
return spherical_cylinder
|
|
350
|
+
|
|
351
|
+
def initialize_from_p_q(self, p=2.0, q=1.0, grazing_angle=0.003):
|
|
352
|
+
"""
|
|
353
|
+
Calculates and sets the radius of curvature in the corresponding direction (tangential or sagittal.
|
|
354
|
+
Parameters
|
|
355
|
+
----------
|
|
356
|
+
p : float, optional
|
|
357
|
+
distance source-optical element.
|
|
358
|
+
q : float, optional
|
|
359
|
+
distance optical element to focus.
|
|
360
|
+
grazing_angle : float, optional
|
|
361
|
+
grazing angle in rad.
|
|
362
|
+
|
|
363
|
+
"""
|
|
364
|
+
if self._cylinder_direction == Direction.TANGENTIAL:
|
|
365
|
+
self._radius = Sphere.get_radius_from_p_q(p, q, grazing_angle)
|
|
366
|
+
elif self._cylinder_direction == Direction.SAGITTAL:
|
|
367
|
+
self._radius = SphericalCylinder.get_radius_from_p_q_sagittal(p, q, grazing_angle)
|
|
368
|
+
|
|
369
|
+
@classmethod
|
|
370
|
+
def get_radius_from_p_q_sagittal(cls, p=2.0, q=1.0, grazing_angle=0.003):
|
|
371
|
+
"""
|
|
372
|
+
Calculates the sagittal radius from the factory parameters (1/p + 1/q = 2 sin(grazing_angle)/Rs).
|
|
373
|
+
|
|
374
|
+
Parameters
|
|
375
|
+
----------
|
|
376
|
+
p : float, optional
|
|
377
|
+
distance source-optical element.
|
|
378
|
+
q : float, optional
|
|
379
|
+
distance optical element to focus.
|
|
380
|
+
grazing_angle : float, optional
|
|
381
|
+
grazing angle in rad.
|
|
382
|
+
|
|
383
|
+
Returns
|
|
384
|
+
-------
|
|
385
|
+
float
|
|
386
|
+
The calculated radius.
|
|
387
|
+
|
|
388
|
+
"""
|
|
389
|
+
# 1/p + 1/q = 2 cos(pi/2 - gr.a.)/r
|
|
390
|
+
return (2*p*q/(p+q))*numpy.sin(grazing_angle)
|
|
391
|
+
|
|
392
|
+
class Ellipsoid(SurfaceShape):
|
|
393
|
+
"""
|
|
394
|
+
Constructor.
|
|
395
|
+
|
|
396
|
+
Ellipsoid: Revolution ellipsoid (rotation around major axis).
|
|
397
|
+
It is defined with three parameters: axes of the ellipse and an additional parameter
|
|
398
|
+
defining the position of the origin of the mirror. This additional parameter can be "p", "x0", "y0"
|
|
399
|
+
or the angle beta from the ellipsoid center (tan(beta)=y0/x0). For simplicity, we store "p" in syned.
|
|
400
|
+
|
|
401
|
+
Parameters
|
|
402
|
+
----------
|
|
403
|
+
min_axis : float, optional
|
|
404
|
+
the ellipse minor axis.
|
|
405
|
+
maj_axis : float, optional
|
|
406
|
+
the ellipse majot axis.
|
|
407
|
+
p_focus : float, optional
|
|
408
|
+
the distance from the first focus (source position) to the mirror pole.
|
|
409
|
+
convexity : int (as defined by Convexity), optional
|
|
410
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
411
|
+
|
|
412
|
+
References
|
|
413
|
+
----------
|
|
414
|
+
Some equations can be found here: https://github.com/srio/shadow3-docs/blob/master/doc/conics.pdf
|
|
415
|
+
|
|
416
|
+
"""
|
|
417
|
+
def __init__(self, min_axis=0.0, maj_axis=0.0, p_focus=0.0, convexity=Convexity.UPWARD):
|
|
418
|
+
SurfaceShape.__init__(self, convexity)
|
|
419
|
+
|
|
420
|
+
self._min_axis = min_axis
|
|
421
|
+
self._maj_axis = maj_axis
|
|
422
|
+
self._p_focus = p_focus
|
|
423
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
424
|
+
self._set_support_text([
|
|
425
|
+
("min_axis" , "Ellipse major axis ", "m" ),
|
|
426
|
+
("maj_axis" , "Ellipse minor axis ", "m"),
|
|
427
|
+
("p_focus" , "Ellipse p (source-focus to pole) ", "m"),
|
|
428
|
+
("convexity" , "(0=upwards, 1=downwards)", " "),
|
|
429
|
+
] )
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
@classmethod
|
|
433
|
+
def create_ellipsoid_from_axes(cls, min_axis=0.0, maj_axis=0.0, p_focus=0.0, convexity=Convexity.UPWARD):
|
|
434
|
+
"""
|
|
435
|
+
Creates an ellipsoid.
|
|
436
|
+
|
|
437
|
+
Parameters
|
|
438
|
+
----------
|
|
439
|
+
min_axis : float, optional
|
|
440
|
+
the ellipse minor axis.
|
|
441
|
+
maj_axis : float, optional
|
|
442
|
+
the ellipse majot axis.
|
|
443
|
+
p_focus : float, optional
|
|
444
|
+
the distance from the first focus (source position) to the mirror pole.
|
|
445
|
+
convexity : int (as defined by Convexity), optional
|
|
446
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
447
|
+
|
|
448
|
+
Returns
|
|
449
|
+
-------
|
|
450
|
+
instance of Ellipsoid
|
|
451
|
+
|
|
452
|
+
"""
|
|
453
|
+
return Ellipsoid(min_axis, maj_axis, p_focus, convexity)
|
|
454
|
+
|
|
455
|
+
@classmethod
|
|
456
|
+
def create_ellipsoid_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003, convexity=Convexity.UPWARD):
|
|
457
|
+
"""
|
|
458
|
+
Creates an ellipsoid from factory parameters.
|
|
459
|
+
|
|
460
|
+
Parameters
|
|
461
|
+
----------
|
|
462
|
+
p : float, optional
|
|
463
|
+
distance source-optical element.
|
|
464
|
+
q : float, optional
|
|
465
|
+
distance optical element to focus.
|
|
466
|
+
grazing_angle : float, optional
|
|
467
|
+
grazing angle in rad.
|
|
468
|
+
convexity : int (as defined by Convexity), optional
|
|
469
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
470
|
+
|
|
471
|
+
Returns
|
|
472
|
+
-------
|
|
473
|
+
instance of Ellipsoid
|
|
474
|
+
|
|
475
|
+
"""
|
|
476
|
+
ellipsoid = Ellipsoid(convexity=convexity)
|
|
477
|
+
ellipsoid.initialize_from_p_q(p, q, grazing_angle)
|
|
478
|
+
|
|
479
|
+
return ellipsoid
|
|
480
|
+
|
|
481
|
+
def initialize_from_p_q(self, p=2.0, q=1.0, grazing_angle=0.003):
|
|
482
|
+
"""
|
|
483
|
+
Sets the ellipsoid parameters as calculated from the factory parameters.
|
|
484
|
+
|
|
485
|
+
Parameters
|
|
486
|
+
----------
|
|
487
|
+
p : float, optional
|
|
488
|
+
distance source-optical element.
|
|
489
|
+
q : float, optional
|
|
490
|
+
distance optical element to focus.
|
|
491
|
+
grazing_angle : float, optional
|
|
492
|
+
grazing angle in rad.
|
|
493
|
+
|
|
494
|
+
"""
|
|
495
|
+
self._min_axis, self._maj_axis = Ellipsoid.get_axis_from_p_q(p, q, grazing_angle)
|
|
496
|
+
self._p_focus = p
|
|
497
|
+
|
|
498
|
+
def initialize_from_shadow_parameters(self, axmaj=2.0, axmin=1.0, ell_the=0.003, convexity=Convexity.UPWARD):
|
|
499
|
+
"""
|
|
500
|
+
Sets the ellipsoid parameters as calculated from the parameters used in SHADOW.
|
|
501
|
+
|
|
502
|
+
Parameters
|
|
503
|
+
----------
|
|
504
|
+
min_axis : float, optional
|
|
505
|
+
the ellipse minor axis.
|
|
506
|
+
maj_axis : float, optional
|
|
507
|
+
the ellipse majot axis.
|
|
508
|
+
ell_the : float, optional
|
|
509
|
+
the angle beta from the ellipsoid center in rads.
|
|
510
|
+
convexity : int (as defined by Convexity), optional
|
|
511
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
512
|
+
|
|
513
|
+
"""
|
|
514
|
+
tanbeta2 = numpy.tan(ell_the) ** 2
|
|
515
|
+
y = axmaj * axmin / numpy.sqrt(axmin ** 2 + axmaj ** 2 * tanbeta2)
|
|
516
|
+
z = y * numpy.tan(ell_the)
|
|
517
|
+
c = numpy.sqrt(axmaj ** 2 - axmin ** 2)
|
|
518
|
+
p = numpy.sqrt( (y + c)**2 + z**2)
|
|
519
|
+
|
|
520
|
+
self.__init__(axmin, axmaj, p, convexity)
|
|
521
|
+
|
|
522
|
+
def get_axes(self):
|
|
523
|
+
"""
|
|
524
|
+
Returns the ellipsoid axes.
|
|
525
|
+
Note that the third axis of the ellipsoid is the same as the minor axis (revolution ellipsoid).
|
|
526
|
+
|
|
527
|
+
Returns
|
|
528
|
+
-------
|
|
529
|
+
tuple
|
|
530
|
+
(minor_axis, major_axis)
|
|
531
|
+
"""
|
|
532
|
+
return self._min_axis, self._maj_axis
|
|
533
|
+
|
|
534
|
+
def get_p_q(self, grazing_angle=0.003):
|
|
535
|
+
"""
|
|
536
|
+
Returns p and q for a given grazing angle.
|
|
537
|
+
|
|
538
|
+
Parameters
|
|
539
|
+
----------
|
|
540
|
+
grazing_angle : float
|
|
541
|
+
The grazing angle in rad.
|
|
542
|
+
|
|
543
|
+
Returns
|
|
544
|
+
-------
|
|
545
|
+
tuple
|
|
546
|
+
(p, q)
|
|
547
|
+
|
|
548
|
+
"""
|
|
549
|
+
return Ellipsoid.get_p_q_from_axis(self._min_axis, self._maj_axis, grazing_angle)
|
|
550
|
+
|
|
551
|
+
# semiaxes etc
|
|
552
|
+
def get_a(self):
|
|
553
|
+
"""
|
|
554
|
+
Returns a = half of the major axis.
|
|
555
|
+
|
|
556
|
+
Returns
|
|
557
|
+
-------
|
|
558
|
+
float
|
|
559
|
+
|
|
560
|
+
"""
|
|
561
|
+
return 0.5 * self._maj_axis
|
|
562
|
+
|
|
563
|
+
def get_b(self):
|
|
564
|
+
"""
|
|
565
|
+
Returns b = half of the minor axis.
|
|
566
|
+
|
|
567
|
+
Returns
|
|
568
|
+
-------
|
|
569
|
+
float
|
|
570
|
+
|
|
571
|
+
"""
|
|
572
|
+
return 0.5 * self._min_axis
|
|
573
|
+
|
|
574
|
+
def get_c(self):
|
|
575
|
+
"""
|
|
576
|
+
Returns c = sqrt(a^2 - b^2).
|
|
577
|
+
|
|
578
|
+
Returns
|
|
579
|
+
-------
|
|
580
|
+
float
|
|
581
|
+
|
|
582
|
+
"""
|
|
583
|
+
return numpy.sqrt(self.get_a()**2 - self.get_b()**2)
|
|
584
|
+
|
|
585
|
+
def get_p_focus(self):
|
|
586
|
+
"""
|
|
587
|
+
Returns p (=p_focus).
|
|
588
|
+
|
|
589
|
+
Returns
|
|
590
|
+
-------
|
|
591
|
+
float
|
|
592
|
+
|
|
593
|
+
"""
|
|
594
|
+
return self._p_focus
|
|
595
|
+
|
|
596
|
+
def get_q_focus(self):
|
|
597
|
+
"""
|
|
598
|
+
Returns q.
|
|
599
|
+
|
|
600
|
+
Returns
|
|
601
|
+
-------
|
|
602
|
+
float
|
|
603
|
+
|
|
604
|
+
"""
|
|
605
|
+
return 2 * self.get_a() - self.get_p_focus()
|
|
606
|
+
|
|
607
|
+
def get_eccentricity(self):
|
|
608
|
+
"""
|
|
609
|
+
returns the eccentricity e = c / a.
|
|
610
|
+
|
|
611
|
+
Returns
|
|
612
|
+
-------
|
|
613
|
+
float
|
|
614
|
+
|
|
615
|
+
"""
|
|
616
|
+
return self.get_c() / self.get_a()
|
|
617
|
+
|
|
618
|
+
def get_grazing_angle(self):
|
|
619
|
+
"""
|
|
620
|
+
Returns the grazing angle.
|
|
621
|
+
|
|
622
|
+
Returns
|
|
623
|
+
-------
|
|
624
|
+
float
|
|
625
|
+
|
|
626
|
+
"""
|
|
627
|
+
return numpy.arcsin(self.get_b() / numpy.sqrt(self.get_p_focus() * self.get_q_focus()))
|
|
628
|
+
|
|
629
|
+
def get_mirror_center(self):
|
|
630
|
+
"""
|
|
631
|
+
Returns the coordinates of the mirror pole or center.
|
|
632
|
+
|
|
633
|
+
Returns
|
|
634
|
+
-------
|
|
635
|
+
tuple
|
|
636
|
+
(coor_along_axis_maj, coor_along_axis_min).
|
|
637
|
+
|
|
638
|
+
"""
|
|
639
|
+
coor_along_axis_maj = (self.get_p_focus()**2 - self.get_q_focus()**1) / (4 * self.get_c())
|
|
640
|
+
coor_along_axis_min = self.get_b * numpy.sqrt(1 - (coor_along_axis_maj / self.get_a())**2)
|
|
641
|
+
return coor_along_axis_maj, coor_along_axis_min
|
|
642
|
+
|
|
643
|
+
def get_angle_pole_from_origin(self):
|
|
644
|
+
"""
|
|
645
|
+
Return the angle from pole to origin (beta).
|
|
646
|
+
|
|
647
|
+
Returns
|
|
648
|
+
-------
|
|
649
|
+
float
|
|
650
|
+
|
|
651
|
+
"""
|
|
652
|
+
x1, x2 = self.get_mirror_center()
|
|
653
|
+
return numpy.arctan(x2 / x1)
|
|
654
|
+
|
|
655
|
+
@classmethod
|
|
656
|
+
def get_axis_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003):
|
|
657
|
+
"""
|
|
658
|
+
Calculates the ellipse axes from the factory parameters.
|
|
659
|
+
|
|
660
|
+
Parameters
|
|
661
|
+
----------
|
|
662
|
+
p : float, optional
|
|
663
|
+
distance source-optical element.
|
|
664
|
+
q : float, optional
|
|
665
|
+
distance optical element to focus.
|
|
666
|
+
grazing_angle : float, optional
|
|
667
|
+
grazing angle in rad.
|
|
668
|
+
|
|
669
|
+
Returns
|
|
670
|
+
-------
|
|
671
|
+
tuple
|
|
672
|
+
(minor_axis, major_axis).
|
|
673
|
+
|
|
674
|
+
"""
|
|
675
|
+
# see calculation of ellipse axis in shadow_kernel.f90 row 3605
|
|
676
|
+
min_axis = 2*numpy.sqrt(p*q)*numpy.sin(grazing_angle)
|
|
677
|
+
maj_axis = (p + q)
|
|
678
|
+
|
|
679
|
+
return min_axis, maj_axis
|
|
680
|
+
|
|
681
|
+
@classmethod
|
|
682
|
+
def get_p_q_from_axis(cls, min_axis=2.0, maj_axis=1.0, grazing_angle=0.003):
|
|
683
|
+
"""
|
|
684
|
+
Calculates the p and q values from axis and grazing angle.
|
|
685
|
+
|
|
686
|
+
Parameters
|
|
687
|
+
----------
|
|
688
|
+
min_axis : float, optional
|
|
689
|
+
the ellipse minor axis.
|
|
690
|
+
maj_axis : float, optional
|
|
691
|
+
the ellipse majot axis.
|
|
692
|
+
grazing_angle : float, optional
|
|
693
|
+
grazing angle in rad.
|
|
694
|
+
|
|
695
|
+
Returns
|
|
696
|
+
-------
|
|
697
|
+
tuple
|
|
698
|
+
(p, q).
|
|
699
|
+
|
|
700
|
+
"""
|
|
701
|
+
a = maj_axis/2
|
|
702
|
+
b = min_axis/2
|
|
703
|
+
p = a + numpy.sqrt(a**2 - (b/numpy.sin(grazing_angle))**2)
|
|
704
|
+
q = maj_axis - p
|
|
705
|
+
|
|
706
|
+
return p, q
|
|
707
|
+
|
|
708
|
+
class EllipticalCylinder(Ellipsoid, Cylinder):
|
|
709
|
+
"""
|
|
710
|
+
Constructor.
|
|
711
|
+
|
|
712
|
+
Parameters
|
|
713
|
+
----------
|
|
714
|
+
min_axis : float, optional
|
|
715
|
+
the ellipse minor axis.
|
|
716
|
+
maj_axis : float, optional
|
|
717
|
+
the ellipse majot axis.
|
|
718
|
+
p_focus : float, optional
|
|
719
|
+
the distance from the first focus (source position) to the mirror pole.
|
|
720
|
+
convexity : int (as defined by Convexity), optional
|
|
721
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
722
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
723
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
724
|
+
|
|
725
|
+
"""
|
|
726
|
+
def __init__(self,
|
|
727
|
+
min_axis=0.0,
|
|
728
|
+
maj_axis=0.0,
|
|
729
|
+
p_focus=0.0,
|
|
730
|
+
convexity=Convexity.UPWARD,
|
|
731
|
+
cylinder_direction=Direction.TANGENTIAL):
|
|
732
|
+
Ellipsoid.__init__(self, min_axis, maj_axis, p_focus, convexity)
|
|
733
|
+
Cylinder.__init__(self, cylinder_direction)
|
|
734
|
+
|
|
735
|
+
@classmethod
|
|
736
|
+
def create_elliptical_cylinder_from_axes(cls, min_axis=0.0, maj_axis=0.0, p_focus=0.0,
|
|
737
|
+
convexity=Convexity.UPWARD, cylinder_direction=Direction.TANGENTIAL):
|
|
738
|
+
"""
|
|
739
|
+
Returns an EllipticalCylinder instance from main parameters.
|
|
740
|
+
|
|
741
|
+
Parameters
|
|
742
|
+
----------
|
|
743
|
+
min_axis : float, optional
|
|
744
|
+
the ellipse minor axis.
|
|
745
|
+
maj_axis : float, optional
|
|
746
|
+
the ellipse majot axis.
|
|
747
|
+
p_focus : float, optional
|
|
748
|
+
the distance from the first focus (source position) to the mirror pole.
|
|
749
|
+
convexity : int (as defined by Convexity), optional
|
|
750
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
751
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
752
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
753
|
+
|
|
754
|
+
Returns
|
|
755
|
+
-------
|
|
756
|
+
instance of EllipticalCylinder
|
|
757
|
+
|
|
758
|
+
"""
|
|
759
|
+
return EllipticalCylinder(min_axis, maj_axis, p_focus, convexity, cylinder_direction)
|
|
760
|
+
|
|
761
|
+
@classmethod
|
|
762
|
+
def create_elliptical_cylinder_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003,
|
|
763
|
+
convexity=Convexity.UPWARD, cylinder_direction=Direction.TANGENTIAL):
|
|
764
|
+
"""
|
|
765
|
+
Returns an EllipticalCylinder instance from factory parameters.
|
|
766
|
+
|
|
767
|
+
Parameters
|
|
768
|
+
----------
|
|
769
|
+
p : float, optional
|
|
770
|
+
distance source-optical element.
|
|
771
|
+
q : float, optional
|
|
772
|
+
distance optical element to focus.
|
|
773
|
+
grazing_angle : float, optional
|
|
774
|
+
grazing angle in rad.
|
|
775
|
+
convexity : int (as defined by Convexity), optional
|
|
776
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
777
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
778
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
779
|
+
|
|
780
|
+
Returns
|
|
781
|
+
-------
|
|
782
|
+
instance of EllipticalCylinder
|
|
783
|
+
|
|
784
|
+
"""
|
|
785
|
+
elliptical_cylinder = EllipticalCylinder(convexity=convexity, cylinder_direction=cylinder_direction)
|
|
786
|
+
elliptical_cylinder.initialize_from_p_q(p, q, grazing_angle)
|
|
787
|
+
|
|
788
|
+
return elliptical_cylinder
|
|
789
|
+
|
|
790
|
+
def initialize_from_p_q(self, p=2.0, q=1.0, grazing_angle=0.003):
|
|
791
|
+
"""
|
|
792
|
+
Sets the ellipsoid parameters for given factory parameters.
|
|
793
|
+
|
|
794
|
+
Parameters
|
|
795
|
+
----------
|
|
796
|
+
p : float, optional
|
|
797
|
+
distance source-optical element.
|
|
798
|
+
q : float, optional
|
|
799
|
+
distance optical element to focus.
|
|
800
|
+
grazing_angle : float, optional
|
|
801
|
+
grazing angle in rad.
|
|
802
|
+
|
|
803
|
+
"""
|
|
804
|
+
if self._cylinder_direction == Direction.SAGITTAL: raise NotImplementedError("Operation not possible for SAGITTAL direction")
|
|
805
|
+
|
|
806
|
+
super().initialize_from_p_q(p, q, grazing_angle)
|
|
807
|
+
|
|
808
|
+
def get_p_q(self, grazing_angle=0.003):
|
|
809
|
+
"""
|
|
810
|
+
Returns p and q distances for a given grazing angle.
|
|
811
|
+
|
|
812
|
+
Parameters
|
|
813
|
+
----------
|
|
814
|
+
grazing_angle : float
|
|
815
|
+
The grazing angle in rad.
|
|
816
|
+
|
|
817
|
+
Returns
|
|
818
|
+
-------
|
|
819
|
+
tuple
|
|
820
|
+
(p, q).
|
|
821
|
+
|
|
822
|
+
"""
|
|
823
|
+
if self._cylinder_direction == Direction.SAGITTAL: raise NotImplementedError("Operation not possible for SAGITTAL direction")
|
|
824
|
+
|
|
825
|
+
return super().get_p_q(grazing_angle)
|
|
826
|
+
|
|
827
|
+
class Hyperboloid(SurfaceShape):
|
|
828
|
+
"""
|
|
829
|
+
Constructor.
|
|
830
|
+
|
|
831
|
+
Hyperboloid: Revolution hyperboloid (two sheets: rotation around major axis).
|
|
832
|
+
It is defined with three parameters: axes of the hyperbola and an additional parameter
|
|
833
|
+
defining the position of the origin of the mirror. This additional parameter can be "p", "x0", "y0"
|
|
834
|
+
or the angle beta from the ellipsoid center (tan(beta)=y0/x0). For simplicity, we store "p" in syned.
|
|
835
|
+
|
|
836
|
+
Parameters
|
|
837
|
+
----------
|
|
838
|
+
min_axis : float, optional
|
|
839
|
+
the hyperbola minor axis.
|
|
840
|
+
maj_axis : float, optional
|
|
841
|
+
the hyperbola majot axis.
|
|
842
|
+
p_focus : float, optional
|
|
843
|
+
the distance from the first focus (source position) to the mirror pole.
|
|
844
|
+
convexity : int (as defined by Convexity), optional
|
|
845
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
846
|
+
|
|
847
|
+
References
|
|
848
|
+
----------
|
|
849
|
+
Some equations can be found here: https://github.com/srio/shadow3-docs/blob/master/doc/conics.pdf
|
|
850
|
+
|
|
851
|
+
"""
|
|
852
|
+
def __init__(self, min_axis=0.0, maj_axis=0.0, p_focus=0.0, convexity=Convexity.UPWARD):
|
|
853
|
+
SurfaceShape.__init__(self, convexity)
|
|
854
|
+
|
|
855
|
+
self._min_axis = min_axis
|
|
856
|
+
self._maj_axis = maj_axis
|
|
857
|
+
self._p_focus = p_focus
|
|
858
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
859
|
+
self._set_support_text([
|
|
860
|
+
("min_axis" , "Hyperbola major axis ", "m" ),
|
|
861
|
+
("maj_axis" , "Hyperbola minor axis ", "m"),
|
|
862
|
+
("p_focus" , "Hyperbola p (source-focus to pole) ", "m"),
|
|
863
|
+
("convexity" , "(0=upwards, 1=downwards)", " "),
|
|
864
|
+
] )
|
|
865
|
+
|
|
866
|
+
@classmethod
|
|
867
|
+
def create_hyperboloid_from_axes(cls, min_axis=0.0, maj_axis=0.0, p_focus=0.0, convexity=Convexity.UPWARD):
|
|
868
|
+
"""
|
|
869
|
+
Creates an hyperboloid from main parameters.
|
|
870
|
+
|
|
871
|
+
Parameters
|
|
872
|
+
----------
|
|
873
|
+
min_axis : float, optional
|
|
874
|
+
the ellipse minor axis.
|
|
875
|
+
maj_axis : float, optional
|
|
876
|
+
the ellipse majot axis.
|
|
877
|
+
p_focus : float, optional
|
|
878
|
+
the angle beta from the hyperbola center in rads.
|
|
879
|
+
convexity : int (as defined by Convexity), optional
|
|
880
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
881
|
+
|
|
882
|
+
Returns
|
|
883
|
+
-------
|
|
884
|
+
instance of Hyperboloid
|
|
885
|
+
|
|
886
|
+
"""
|
|
887
|
+
return Hyperboloid(min_axis, maj_axis, p_focus, convexity)
|
|
888
|
+
|
|
889
|
+
@classmethod
|
|
890
|
+
def create_hyperboloid_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003, convexity=Convexity.UPWARD):
|
|
891
|
+
"""
|
|
892
|
+
Creates an hyperboloid from factory parameters.
|
|
893
|
+
|
|
894
|
+
Parameters
|
|
895
|
+
----------
|
|
896
|
+
p : float, optional
|
|
897
|
+
distance source-optical element.
|
|
898
|
+
q : float, optional
|
|
899
|
+
distance optical element to focus.
|
|
900
|
+
grazing_angle : float, optional
|
|
901
|
+
grazing angle in rad.
|
|
902
|
+
convexity : int (as defined by Convexity), optional
|
|
903
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
904
|
+
|
|
905
|
+
Returns
|
|
906
|
+
-------
|
|
907
|
+
instance of Hyoerboloid
|
|
908
|
+
|
|
909
|
+
"""
|
|
910
|
+
hyperboloid = Hyperboloid(convexity=convexity)
|
|
911
|
+
hyperboloid.initialize_from_p_q(p, q, grazing_angle)
|
|
912
|
+
|
|
913
|
+
return hyperboloid
|
|
914
|
+
|
|
915
|
+
def initialize_from_p_q(self, p=2.0, q=1.0, grazing_angle=0.003):
|
|
916
|
+
"""
|
|
917
|
+
Sets the hyperboloid parameters as calculated from the factory parameters.
|
|
918
|
+
|
|
919
|
+
Parameters
|
|
920
|
+
----------
|
|
921
|
+
p : float, optional
|
|
922
|
+
distance source-optical element.
|
|
923
|
+
q : float, optional
|
|
924
|
+
distance optical element to focus.
|
|
925
|
+
grazing_angle : float, optional
|
|
926
|
+
grazing angle in rad.
|
|
927
|
+
|
|
928
|
+
"""
|
|
929
|
+
self._min_axis, self._maj_axis = Hyperboloid.get_axis_from_p_q(p, q, grazing_angle)
|
|
930
|
+
self._p_focus = p
|
|
931
|
+
|
|
932
|
+
# TODO:
|
|
933
|
+
def initialize_from_shadow_parameters(self, axmaj=2.0, axmin=1.0, ell_the=0.003, convexity=Convexity.UPWARD):
|
|
934
|
+
"""
|
|
935
|
+
Sets the hyperboloid parameters as calculated from the parameters used in SHADOW.
|
|
936
|
+
Note that in SHADOW3 the definition of the hyperbola from the factory parameters is buggy.
|
|
937
|
+
|
|
938
|
+
Raises
|
|
939
|
+
------
|
|
940
|
+
NotImplementedError
|
|
941
|
+
|
|
942
|
+
"""
|
|
943
|
+
raise NotImplementedError("TODO")
|
|
944
|
+
|
|
945
|
+
def get_axes(self):
|
|
946
|
+
"""
|
|
947
|
+
Returns the hyperboloid axes.
|
|
948
|
+
Note that the third axis of the ellipsoid is the same as the minor axis (revolution ellipsoid).
|
|
949
|
+
|
|
950
|
+
Returns
|
|
951
|
+
-------
|
|
952
|
+
tuple
|
|
953
|
+
(minor_axis, major_axis)
|
|
954
|
+
"""
|
|
955
|
+
return self._min_axis, self._maj_axis
|
|
956
|
+
|
|
957
|
+
def get_p_q(self, grazing_angle=0.003):
|
|
958
|
+
"""
|
|
959
|
+
Returns p and q for a given grazing angle.
|
|
960
|
+
|
|
961
|
+
Parameters
|
|
962
|
+
----------
|
|
963
|
+
grazing_angle : float
|
|
964
|
+
The grazing angle in rad.
|
|
965
|
+
|
|
966
|
+
Returns
|
|
967
|
+
-------
|
|
968
|
+
tuple
|
|
969
|
+
(p, q)
|
|
970
|
+
|
|
971
|
+
"""
|
|
972
|
+
return Hyperboloid.get_p_q_from_axis(self._min_axis, self._maj_axis, grazing_angle)
|
|
973
|
+
|
|
974
|
+
# semiaxes etc
|
|
975
|
+
def get_a(self):
|
|
976
|
+
"""
|
|
977
|
+
Returns a = half of the major axis.
|
|
978
|
+
|
|
979
|
+
Returns
|
|
980
|
+
-------
|
|
981
|
+
float
|
|
982
|
+
|
|
983
|
+
"""
|
|
984
|
+
return 0.5 * self._maj_axis
|
|
985
|
+
|
|
986
|
+
def get_b(self):
|
|
987
|
+
"""
|
|
988
|
+
Returns b = half of the minor axis.
|
|
989
|
+
|
|
990
|
+
Returns
|
|
991
|
+
-------
|
|
992
|
+
float
|
|
993
|
+
|
|
994
|
+
"""
|
|
995
|
+
return 0.5 * self._min_axis
|
|
996
|
+
|
|
997
|
+
def get_c(self):
|
|
998
|
+
"""
|
|
999
|
+
Returns c = sqrt(a^2 + b^2).
|
|
1000
|
+
|
|
1001
|
+
Returns
|
|
1002
|
+
-------
|
|
1003
|
+
float
|
|
1004
|
+
|
|
1005
|
+
"""
|
|
1006
|
+
return numpy.sqrt(self.get_a()**2 + self.get_b()**2)
|
|
1007
|
+
|
|
1008
|
+
def get_p_focus(self):
|
|
1009
|
+
"""
|
|
1010
|
+
Returns p (=p_focus).
|
|
1011
|
+
|
|
1012
|
+
Returns
|
|
1013
|
+
-------
|
|
1014
|
+
float
|
|
1015
|
+
|
|
1016
|
+
"""
|
|
1017
|
+
return self._p_focus
|
|
1018
|
+
|
|
1019
|
+
def get_q_focus(self):
|
|
1020
|
+
"""
|
|
1021
|
+
Returns q.
|
|
1022
|
+
|
|
1023
|
+
Returns
|
|
1024
|
+
-------
|
|
1025
|
+
float
|
|
1026
|
+
|
|
1027
|
+
"""
|
|
1028
|
+
return self.get_p_focus() - 2 * self.get_a()
|
|
1029
|
+
|
|
1030
|
+
def get_eccentricity(self):
|
|
1031
|
+
"""
|
|
1032
|
+
returns the eccentricity e = c / a.
|
|
1033
|
+
|
|
1034
|
+
Returns
|
|
1035
|
+
-------
|
|
1036
|
+
float
|
|
1037
|
+
|
|
1038
|
+
"""
|
|
1039
|
+
return self.get_c / self.get_a()
|
|
1040
|
+
|
|
1041
|
+
def get_grazing_angle(self):
|
|
1042
|
+
"""
|
|
1043
|
+
Returns the grazing angle.
|
|
1044
|
+
|
|
1045
|
+
Returns
|
|
1046
|
+
-------
|
|
1047
|
+
float
|
|
1048
|
+
|
|
1049
|
+
"""
|
|
1050
|
+
return numpy.arcsin(self.get_b() / numpy.sqrt(self.get_p_focus() * self.get_q_focus()))
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
def get_mirror_center(self): #TODO:
|
|
1054
|
+
"""
|
|
1055
|
+
Returns the coordinates of the mirror pole or center.
|
|
1056
|
+
|
|
1057
|
+
Raises
|
|
1058
|
+
------
|
|
1059
|
+
NotImplementedError
|
|
1060
|
+
|
|
1061
|
+
"""
|
|
1062
|
+
raise NotImplementedError("TODO")
|
|
1063
|
+
|
|
1064
|
+
def get_angle_pole_from_origin(self): #TODO:
|
|
1065
|
+
"""
|
|
1066
|
+
Return the angle from pole to origin (beta).
|
|
1067
|
+
|
|
1068
|
+
Raises
|
|
1069
|
+
------
|
|
1070
|
+
NotImplementedError
|
|
1071
|
+
|
|
1072
|
+
"""
|
|
1073
|
+
raise NotImplementedError("TODO")
|
|
1074
|
+
|
|
1075
|
+
@classmethod
|
|
1076
|
+
def get_axis_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003, branch_sign=+1):
|
|
1077
|
+
"""
|
|
1078
|
+
Calculates the hyperbola axes from the factory parameters.
|
|
1079
|
+
|
|
1080
|
+
Parameters
|
|
1081
|
+
----------
|
|
1082
|
+
p : float, optional
|
|
1083
|
+
distance source-optical element.
|
|
1084
|
+
q : float, optional
|
|
1085
|
+
distance optical element to focus.
|
|
1086
|
+
grazing_angle : float, optional
|
|
1087
|
+
grazing angle in rad.
|
|
1088
|
+
branch_sign : int
|
|
1089
|
+
+1 (positive) or -1 (negative) branch.
|
|
1090
|
+
|
|
1091
|
+
Returns
|
|
1092
|
+
-------
|
|
1093
|
+
tuple
|
|
1094
|
+
(minor_axis, major_axis).
|
|
1095
|
+
|
|
1096
|
+
"""
|
|
1097
|
+
min_axis = 2*numpy.sqrt(p*q)*numpy.sin(grazing_angle)
|
|
1098
|
+
maj_axis = (p - q) * branch_sign
|
|
1099
|
+
|
|
1100
|
+
return min_axis, maj_axis
|
|
1101
|
+
|
|
1102
|
+
# TODO:
|
|
1103
|
+
@classmethod
|
|
1104
|
+
def get_p_q_from_axis(cls, min_axis=2.0, maj_axis=1.0, grazing_angle=0.003):
|
|
1105
|
+
"""
|
|
1106
|
+
Calculates the p and q values from axis and grazing angle.
|
|
1107
|
+
|
|
1108
|
+
Raises
|
|
1109
|
+
------
|
|
1110
|
+
NotImplementedError
|
|
1111
|
+
|
|
1112
|
+
"""
|
|
1113
|
+
raise NotImplementedError("TODO")
|
|
1114
|
+
|
|
1115
|
+
class HyperbolicCylinder(Hyperboloid, Cylinder):
|
|
1116
|
+
"""
|
|
1117
|
+
Constructor.
|
|
1118
|
+
|
|
1119
|
+
Parameters
|
|
1120
|
+
----------
|
|
1121
|
+
min_axis : float, optional
|
|
1122
|
+
the ellipse minor axis.
|
|
1123
|
+
maj_axis : float, optional
|
|
1124
|
+
the ellipse majot axis.
|
|
1125
|
+
p_focus : float, optional
|
|
1126
|
+
the distance from the first focus (source position) to the mirror pole.
|
|
1127
|
+
convexity : int (as defined by Convexity), optional
|
|
1128
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
1129
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
1130
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
1131
|
+
|
|
1132
|
+
"""
|
|
1133
|
+
def __init__(self,
|
|
1134
|
+
min_axis=0.0,
|
|
1135
|
+
maj_axis=0.0,
|
|
1136
|
+
p_focus=0.0,
|
|
1137
|
+
convexity=Convexity.UPWARD,
|
|
1138
|
+
cylinder_direction=Direction.TANGENTIAL):
|
|
1139
|
+
Hyperboloid.__init__(self, min_axis, maj_axis, p_focus, convexity)
|
|
1140
|
+
Cylinder.__init__(self, cylinder_direction)
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
@classmethod
|
|
1144
|
+
def create_hyperbolic_cylinder_from_axes(cls, min_axis=0.0, maj_axis=0.0, p_focus=0.0,
|
|
1145
|
+
convexity=Convexity.UPWARD, cylinder_direction=Direction.TANGENTIAL):
|
|
1146
|
+
"""
|
|
1147
|
+
Returns an HyperbolicCylinder instance from main parameters.
|
|
1148
|
+
|
|
1149
|
+
Parameters
|
|
1150
|
+
----------
|
|
1151
|
+
min_axis : float, optional
|
|
1152
|
+
the ellipse minor axis.
|
|
1153
|
+
maj_axis : float, optional
|
|
1154
|
+
the ellipse majot axis.
|
|
1155
|
+
p_focus : float, optional
|
|
1156
|
+
the distance from the first focus (source position) to the mirror pole.
|
|
1157
|
+
convexity : int (as defined by Convexity), optional
|
|
1158
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
1159
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
1160
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
1161
|
+
|
|
1162
|
+
Returns
|
|
1163
|
+
-------
|
|
1164
|
+
instance of HyperbolicCylinder
|
|
1165
|
+
|
|
1166
|
+
"""
|
|
1167
|
+
return HyperbolicCylinder(min_axis, maj_axis, p_focus, convexity, cylinder_direction)
|
|
1168
|
+
|
|
1169
|
+
@classmethod
|
|
1170
|
+
def create_hyperbolic_cylinder_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003,
|
|
1171
|
+
convexity=Convexity.UPWARD, cylinder_direction=Direction.TANGENTIAL):
|
|
1172
|
+
"""
|
|
1173
|
+
Returns an HyperbolicCylinder instance from factory parameters.
|
|
1174
|
+
|
|
1175
|
+
Parameters
|
|
1176
|
+
----------
|
|
1177
|
+
p : float, optional
|
|
1178
|
+
distance source-optical element.
|
|
1179
|
+
q : float, optional
|
|
1180
|
+
distance optical element to focus.
|
|
1181
|
+
grazing_angle : float, optional
|
|
1182
|
+
grazing angle in rad.
|
|
1183
|
+
convexity : int (as defined by Convexity), optional
|
|
1184
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
1185
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
1186
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
1187
|
+
|
|
1188
|
+
Returns
|
|
1189
|
+
-------
|
|
1190
|
+
instance of HyperbolicCylinder
|
|
1191
|
+
|
|
1192
|
+
"""
|
|
1193
|
+
hyperbolic_cylinder = HyperbolicCylinder(convexity=convexity, cylinder_direction=cylinder_direction)
|
|
1194
|
+
hyperbolic_cylinder.initialize_from_p_q(p, q, grazing_angle)
|
|
1195
|
+
|
|
1196
|
+
return hyperbolic_cylinder
|
|
1197
|
+
|
|
1198
|
+
def initialize_from_p_q(self, p=2.0, q=1.0, grazing_angle=0.003):
|
|
1199
|
+
"""
|
|
1200
|
+
Sets the hyperboloid parameters for given factory parameters.
|
|
1201
|
+
|
|
1202
|
+
Parameters
|
|
1203
|
+
----------
|
|
1204
|
+
p : float, optional
|
|
1205
|
+
distance source-optical element.
|
|
1206
|
+
q : float, optional
|
|
1207
|
+
distance optical element to focus.
|
|
1208
|
+
grazing_angle : float, optional
|
|
1209
|
+
grazing angle in rad.
|
|
1210
|
+
|
|
1211
|
+
"""
|
|
1212
|
+
if self._cylinder_direction == Direction.SAGITTAL: raise NotImplementedError("Operation not possible for SAGITTAL direction")
|
|
1213
|
+
|
|
1214
|
+
super().initialize_from_p_q(p, q, grazing_angle)
|
|
1215
|
+
|
|
1216
|
+
def get_p_q(self, grazing_angle=0.003):
|
|
1217
|
+
"""
|
|
1218
|
+
Returns p and q distances for a given grazing angle.
|
|
1219
|
+
|
|
1220
|
+
Parameters
|
|
1221
|
+
----------
|
|
1222
|
+
grazing_angle : float
|
|
1223
|
+
The grazing angle in rad.
|
|
1224
|
+
|
|
1225
|
+
Returns
|
|
1226
|
+
-------
|
|
1227
|
+
tuple
|
|
1228
|
+
(p, q).
|
|
1229
|
+
|
|
1230
|
+
"""
|
|
1231
|
+
if self._cylinder_direction == Direction.SAGITTAL: raise NotImplementedError("Operation not possible for SAGITTAL direction")
|
|
1232
|
+
|
|
1233
|
+
return super().get_p_q(grazing_angle)
|
|
1234
|
+
|
|
1235
|
+
class Paraboloid(SurfaceShape):
|
|
1236
|
+
"""
|
|
1237
|
+
Constructor.
|
|
1238
|
+
|
|
1239
|
+
Paraboloid: Revolution paraboloid (rotation around symmetry axis).
|
|
1240
|
+
|
|
1241
|
+
It is defined with three parameters: the parabola_parameter and two more parameters
|
|
1242
|
+
defining the position of the origin of the mirror.
|
|
1243
|
+
|
|
1244
|
+
The parabola_parameter = 2 * focal_length = - 0.5 * ccc_9 / ccc_2
|
|
1245
|
+
|
|
1246
|
+
The additional parameter can be the focal distances
|
|
1247
|
+
("p" or "q", one is infinity), "x0", "y0" or the grazing angle.
|
|
1248
|
+
Here, we selected the at_infinity and the finite focal distance p or q or distance from
|
|
1249
|
+
the mirror pole to focus (pole to focus).
|
|
1250
|
+
|
|
1251
|
+
The parabola equation is:
|
|
1252
|
+
|
|
1253
|
+
ccc_2 y^2 + ccc_9 z = 0 or
|
|
1254
|
+
|
|
1255
|
+
y^2 = -ccc_9/ccc_2 z = 2 parabola_parameter z = 4 focal_length z
|
|
1256
|
+
|
|
1257
|
+
The focus is at (0, 0, focal_length).
|
|
1258
|
+
|
|
1259
|
+
The directrix is at (0, 0, -focal_length).
|
|
1260
|
+
|
|
1261
|
+
The distance from the directrix to focus is 2 * focal_length.
|
|
1262
|
+
|
|
1263
|
+
The radius of curvature at the vertex is 2 * focal_length.
|
|
1264
|
+
|
|
1265
|
+
Parameters
|
|
1266
|
+
----------
|
|
1267
|
+
parabola_parameter : float, optional
|
|
1268
|
+
parabola_parameter = 2 * focal_length = - 0.5 * ccc_9 / ccc_2. Equation: y^2 = 2 parabola_parameter z.
|
|
1269
|
+
at_infinity : int (as defined by Side), optional
|
|
1270
|
+
SOURCE = 0, IMAGE = 1.
|
|
1271
|
+
pole_to_focus : float, optional
|
|
1272
|
+
The p distance.
|
|
1273
|
+
convexity : int (as defined by Convexity), optional
|
|
1274
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
1275
|
+
|
|
1276
|
+
References
|
|
1277
|
+
----------
|
|
1278
|
+
https://en.wikipedia.org/wiki/Parabola
|
|
1279
|
+
|
|
1280
|
+
https://doi.org/10.1107/S1600577522004593
|
|
1281
|
+
|
|
1282
|
+
Some equations can be found here: https://github.com/srio/shadow3-docs/blob/master/doc/conics.pdf
|
|
1283
|
+
|
|
1284
|
+
"""
|
|
1285
|
+
def __init__(self,
|
|
1286
|
+
parabola_parameter=0.0,
|
|
1287
|
+
at_infinity=Side.SOURCE,
|
|
1288
|
+
pole_to_focus=None,
|
|
1289
|
+
convexity=Convexity.UPWARD):
|
|
1290
|
+
SurfaceShape.__init__(self, convexity)
|
|
1291
|
+
|
|
1292
|
+
self._parabola_parameter = parabola_parameter
|
|
1293
|
+
self._at_infinity = at_infinity
|
|
1294
|
+
self._pole_to_focus = pole_to_focus
|
|
1295
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
1296
|
+
self._set_support_text([
|
|
1297
|
+
("parabola_parameter" , "Parabola parameter ", "m" ),
|
|
1298
|
+
("at_infinity" , "(0=source, 1=image)", " " ),
|
|
1299
|
+
("pole_to_focus" , "pole to focus", "m"),
|
|
1300
|
+
("convexity" , "(0=upwards, 1=downwards)", " "),
|
|
1301
|
+
] )
|
|
1302
|
+
|
|
1303
|
+
@classmethod
|
|
1304
|
+
def create_paraboloid_from_parabola_parameter(cls, parabola_parameter=0.0, at_infinity=Side.SOURCE,
|
|
1305
|
+
pole_to_focus=None, convexity=Convexity.UPWARD):
|
|
1306
|
+
"""
|
|
1307
|
+
Create a paraboloid.
|
|
1308
|
+
|
|
1309
|
+
Parameters
|
|
1310
|
+
----------
|
|
1311
|
+
parabola_parameter : float, optional
|
|
1312
|
+
parabola_parameter = 2 * focal_distance = - 0.5 * ccc_9 / ccc_2.
|
|
1313
|
+
at_infinity : int (as defined by Side), optional
|
|
1314
|
+
SOURCE = 0, IMAGE = 1.
|
|
1315
|
+
pole_to_focus : float, optional
|
|
1316
|
+
The p distance.
|
|
1317
|
+
convexity : int (as defined by Convexity), optional
|
|
1318
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
1319
|
+
|
|
1320
|
+
Returns
|
|
1321
|
+
-------
|
|
1322
|
+
instance of Paraboloid
|
|
1323
|
+
|
|
1324
|
+
"""
|
|
1325
|
+
return Paraboloid(parabola_parameter, at_infinity=at_infinity, pole_to_focus=pole_to_focus, convexity=convexity)
|
|
1326
|
+
|
|
1327
|
+
@classmethod
|
|
1328
|
+
def create_paraboloid_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003,
|
|
1329
|
+
at_infinity=Side.SOURCE, convexity=Convexity.UPWARD):
|
|
1330
|
+
"""
|
|
1331
|
+
Creates a paraboloid from the factory parameters.
|
|
1332
|
+
|
|
1333
|
+
Parameters
|
|
1334
|
+
----------
|
|
1335
|
+
p : float
|
|
1336
|
+
The distance p (used if at_infinity=Side.IMAGE)
|
|
1337
|
+
q : float
|
|
1338
|
+
The distance q (used if at_infinity=Side.SOURCE)
|
|
1339
|
+
grazing_angle : float
|
|
1340
|
+
The distance p
|
|
1341
|
+
at_infinity : int (as defined by Side), optional
|
|
1342
|
+
SOURCE = 0, IMAGE = 1.
|
|
1343
|
+
convexity : int (as defined by Convexity), optional
|
|
1344
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
1345
|
+
|
|
1346
|
+
Returns
|
|
1347
|
+
-------
|
|
1348
|
+
instance of Paraboloid
|
|
1349
|
+
|
|
1350
|
+
"""
|
|
1351
|
+
paraboloid = Paraboloid(convexity=convexity)
|
|
1352
|
+
paraboloid.initialize_from_p_q(p, q, grazing_angle=grazing_angle, at_infinity=at_infinity)
|
|
1353
|
+
|
|
1354
|
+
return paraboloid
|
|
1355
|
+
|
|
1356
|
+
def initialize_from_p_q(self, p=2.0, q=1.0, grazing_angle=0.003, at_infinity=Side.SOURCE):
|
|
1357
|
+
"""
|
|
1358
|
+
Sets the paraboloid parameters as calculated from the factory parameters.
|
|
1359
|
+
|
|
1360
|
+
Parameters
|
|
1361
|
+
----------
|
|
1362
|
+
p : float
|
|
1363
|
+
The distance p (used if at_infinity=Side.IMAGE)
|
|
1364
|
+
q : float
|
|
1365
|
+
The distance q (used if at_infinity=Side.SOURCE)
|
|
1366
|
+
grazing_angle : float
|
|
1367
|
+
The distance p
|
|
1368
|
+
at_infinity : int (as defined by Side), optional
|
|
1369
|
+
SOURCE = 0, IMAGE = 1.
|
|
1370
|
+
|
|
1371
|
+
Returns
|
|
1372
|
+
-------
|
|
1373
|
+
instance of Paraboloid
|
|
1374
|
+
|
|
1375
|
+
"""
|
|
1376
|
+
self._parabola_parameter = Paraboloid.get_parabola_parameter_from_p_q(p=p, q=q, grazing_angle=grazing_angle, at_infinity=at_infinity)
|
|
1377
|
+
self._at_infinity = at_infinity
|
|
1378
|
+
if at_infinity == Side.SOURCE:
|
|
1379
|
+
self._pole_to_focus = q
|
|
1380
|
+
elif at_infinity == Side.IMAGE:
|
|
1381
|
+
self._pole_to_focus = p
|
|
1382
|
+
|
|
1383
|
+
@classmethod
|
|
1384
|
+
def get_parabola_parameter_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003, at_infinity=Side.SOURCE):
|
|
1385
|
+
"""
|
|
1386
|
+
Calculates the parabola parameter from the factory parameters.
|
|
1387
|
+
|
|
1388
|
+
Parameters
|
|
1389
|
+
----------
|
|
1390
|
+
p : float
|
|
1391
|
+
The distance p (used if at_infinity=Side.IMAGE)
|
|
1392
|
+
q : float
|
|
1393
|
+
The distance q (used if at_infinity=Side.SOURCE)
|
|
1394
|
+
grazing_angle : float
|
|
1395
|
+
The distance p
|
|
1396
|
+
at_infinity : int (as defined by Side), optional
|
|
1397
|
+
SOURCE = 0, IMAGE = 1.
|
|
1398
|
+
|
|
1399
|
+
Returns
|
|
1400
|
+
-------
|
|
1401
|
+
float
|
|
1402
|
+
The parabola parameter.
|
|
1403
|
+
|
|
1404
|
+
"""
|
|
1405
|
+
if at_infinity == Side.IMAGE:
|
|
1406
|
+
return 2*p*(numpy.sin(grazing_angle))**2
|
|
1407
|
+
elif at_infinity == Side.SOURCE:
|
|
1408
|
+
return 2*q*(numpy.sin(grazing_angle))**2
|
|
1409
|
+
|
|
1410
|
+
def get_parabola_parameter(self):
|
|
1411
|
+
"""
|
|
1412
|
+
Returns the parabola parameter.
|
|
1413
|
+
|
|
1414
|
+
Returns
|
|
1415
|
+
-------
|
|
1416
|
+
float
|
|
1417
|
+
|
|
1418
|
+
"""
|
|
1419
|
+
return self._parabola_parameter
|
|
1420
|
+
|
|
1421
|
+
def get_at_infinity(self):
|
|
1422
|
+
"""
|
|
1423
|
+
Returns the "at_infinity" flag.
|
|
1424
|
+
|
|
1425
|
+
Returns
|
|
1426
|
+
-------
|
|
1427
|
+
int (as defined by Side)
|
|
1428
|
+
SOURCE = 0, IMAGE = 1.
|
|
1429
|
+
|
|
1430
|
+
"""
|
|
1431
|
+
return self._at_infinity
|
|
1432
|
+
|
|
1433
|
+
def get_pole_to_focus(self):
|
|
1434
|
+
"""
|
|
1435
|
+
Returns the distance from focus to pole.
|
|
1436
|
+
|
|
1437
|
+
Returns
|
|
1438
|
+
-------
|
|
1439
|
+
float
|
|
1440
|
+
|
|
1441
|
+
"""
|
|
1442
|
+
return self._pole_to_focus
|
|
1443
|
+
|
|
1444
|
+
def get_grazing_angle(self):
|
|
1445
|
+
"""
|
|
1446
|
+
Returns the grazing angle.
|
|
1447
|
+
|
|
1448
|
+
Returns
|
|
1449
|
+
-------
|
|
1450
|
+
float
|
|
1451
|
+
|
|
1452
|
+
"""
|
|
1453
|
+
return numpy.arcsin( numpy.sqrt( self.get_parabola_parameter() / (2 * self.get_pole_to_focus())))
|
|
1454
|
+
|
|
1455
|
+
|
|
1456
|
+
class ParabolicCylinder(Paraboloid, Cylinder):
|
|
1457
|
+
"""
|
|
1458
|
+
Constructor.
|
|
1459
|
+
|
|
1460
|
+
Parameters
|
|
1461
|
+
----------
|
|
1462
|
+
parabola_parameter : float, optional
|
|
1463
|
+
parabola_parameter = 2 * focal_distance = - 0.5 * ccc_9 / ccc_2.
|
|
1464
|
+
at_infinity : int (as defined by Side), optional
|
|
1465
|
+
SOURCE = 0, IMAGE = 1.
|
|
1466
|
+
pole_to_focus : float, optional
|
|
1467
|
+
The p distance.
|
|
1468
|
+
convexity : int (as defined by Convexity), optional
|
|
1469
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
1470
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
1471
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
1472
|
+
|
|
1473
|
+
"""
|
|
1474
|
+
def __init__(self,
|
|
1475
|
+
parabola_parameter=0.0,
|
|
1476
|
+
at_infinity=Side.SOURCE,
|
|
1477
|
+
pole_to_focus=None,
|
|
1478
|
+
convexity=Convexity.UPWARD,
|
|
1479
|
+
cylinder_direction=Direction.TANGENTIAL):
|
|
1480
|
+
Paraboloid.__init__(self, parabola_parameter=parabola_parameter, at_infinity=at_infinity,
|
|
1481
|
+
pole_to_focus=pole_to_focus, convexity=convexity)
|
|
1482
|
+
Cylinder.__init__(self, cylinder_direction)
|
|
1483
|
+
|
|
1484
|
+
@classmethod
|
|
1485
|
+
def create_parabolic_cylinder_from_parabola_parameter(cls,
|
|
1486
|
+
parabola_parameter=0.0,
|
|
1487
|
+
at_infinity=Side.SOURCE,
|
|
1488
|
+
pole_to_focus=None,
|
|
1489
|
+
convexity=Convexity.UPWARD,
|
|
1490
|
+
cylinder_direction=Direction.TANGENTIAL):
|
|
1491
|
+
"""
|
|
1492
|
+
Returns a ParabolicCylinder instance.
|
|
1493
|
+
|
|
1494
|
+
Parameters
|
|
1495
|
+
----------
|
|
1496
|
+
parabola_parameter : float, optional
|
|
1497
|
+
parabola_parameter = 2 * focal_distance = - 0.5 * ccc_9 / ccc_2.
|
|
1498
|
+
at_infinity : int (as defined by Side), optional
|
|
1499
|
+
SOURCE = 0, IMAGE = 1.
|
|
1500
|
+
pole_to_focus : float, optional
|
|
1501
|
+
The p distance.
|
|
1502
|
+
convexity : int (as defined by Convexity), optional
|
|
1503
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
1504
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
1505
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
1506
|
+
|
|
1507
|
+
Returns
|
|
1508
|
+
-------
|
|
1509
|
+
instance of ParabolicCylinder
|
|
1510
|
+
|
|
1511
|
+
"""
|
|
1512
|
+
return ParabolicCylinder(parabola_parameter, at_infinity, pole_to_focus, convexity, cylinder_direction)
|
|
1513
|
+
|
|
1514
|
+
@classmethod
|
|
1515
|
+
def create_parabolic_cylinder_from_p_q(cls,
|
|
1516
|
+
p=2.0,
|
|
1517
|
+
q=1.0,
|
|
1518
|
+
grazing_angle=0.003,
|
|
1519
|
+
at_infinity=Side.SOURCE,
|
|
1520
|
+
convexity=Convexity.UPWARD,
|
|
1521
|
+
cylinder_direction=Direction.TANGENTIAL):
|
|
1522
|
+
"""
|
|
1523
|
+
Returns a ParabolicCylinder instance from factory parameters.
|
|
1524
|
+
|
|
1525
|
+
Parameters
|
|
1526
|
+
----------
|
|
1527
|
+
p : float
|
|
1528
|
+
The distance p (used if at_infinity=Side.IMAGE)
|
|
1529
|
+
q : float
|
|
1530
|
+
The distance q (used if at_infinity=Side.SOURCE)
|
|
1531
|
+
grazing_angle : float
|
|
1532
|
+
The distance p
|
|
1533
|
+
at_infinity : int (as defined by Side), optional
|
|
1534
|
+
SOURCE = 0, IMAGE = 1.
|
|
1535
|
+
convexity : int (as defined by Convexity), optional
|
|
1536
|
+
NONE = -1, UPWARD = 0, DOWNWARD = 1.
|
|
1537
|
+
cylinder_direction : int (as defined by Direction), optional
|
|
1538
|
+
TANGENTIAL = 0, SAGITTAL = 1.
|
|
1539
|
+
|
|
1540
|
+
Returns
|
|
1541
|
+
-------
|
|
1542
|
+
instance of ParabolicCylinder
|
|
1543
|
+
|
|
1544
|
+
"""
|
|
1545
|
+
parabolic_cylinder = ParabolicCylinder(convexity=convexity, cylinder_direction=cylinder_direction)
|
|
1546
|
+
parabolic_cylinder.initialize_from_p_q(p, q, grazing_angle, at_infinity)
|
|
1547
|
+
|
|
1548
|
+
return parabolic_cylinder
|
|
1549
|
+
|
|
1550
|
+
def initialize_from_p_q(self, p=2.0, q=1.0, grazing_angle=0.003, at_infinity=Side.SOURCE):
|
|
1551
|
+
"""
|
|
1552
|
+
Sets the parameters calculated from factory parameters.
|
|
1553
|
+
|
|
1554
|
+
Parameters
|
|
1555
|
+
----------
|
|
1556
|
+
p : float
|
|
1557
|
+
The distance p (used if at_infinity=Side.IMAGE)
|
|
1558
|
+
q : float
|
|
1559
|
+
The distance q (used if at_infinity=Side.SOURCE)
|
|
1560
|
+
grazing_angle : float
|
|
1561
|
+
The distance p
|
|
1562
|
+
at_infinity : int (as defined by Side), optional
|
|
1563
|
+
SOURCE = 0, IMAGE = 1.
|
|
1564
|
+
|
|
1565
|
+
Returns
|
|
1566
|
+
-------
|
|
1567
|
+
instance of ParabolicCylinder
|
|
1568
|
+
|
|
1569
|
+
"""
|
|
1570
|
+
if self._cylinder_direction == Direction.SAGITTAL:
|
|
1571
|
+
raise NotImplementedError("Operation not possible for SAGITTAL direction")
|
|
1572
|
+
|
|
1573
|
+
return super().initialize_from_p_q(p, q, grazing_angle, at_infinity)
|
|
1574
|
+
|
|
1575
|
+
class Toroid(SurfaceShape):
|
|
1576
|
+
"""
|
|
1577
|
+
Creator.
|
|
1578
|
+
|
|
1579
|
+
Parameters
|
|
1580
|
+
----------
|
|
1581
|
+
min_radius : float, optional
|
|
1582
|
+
The toroid minor radius
|
|
1583
|
+
maj_radius : float, optional
|
|
1584
|
+
The toroid major radius. Note that this is the "optical" major radius at the farest surface from the center
|
|
1585
|
+
of the toroid. Indeed, it corresponds to the "toroid major radius" plus the min_radius.
|
|
1586
|
+
|
|
1587
|
+
"""
|
|
1588
|
+
def __init__(self, min_radius=0.0, maj_radius=0.0):
|
|
1589
|
+
SurfaceShape.__init__(self, convexity=Convexity.NONE)
|
|
1590
|
+
|
|
1591
|
+
self._min_radius = min_radius
|
|
1592
|
+
self._maj_radius = maj_radius
|
|
1593
|
+
|
|
1594
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
1595
|
+
self._set_support_text([
|
|
1596
|
+
("min_radius" , "Minor radius r ", "m" ),
|
|
1597
|
+
("maj_radius" , "Major (optical) radius R (R=Ro+r)", "m" ),
|
|
1598
|
+
] )
|
|
1599
|
+
|
|
1600
|
+
@classmethod
|
|
1601
|
+
def create_toroid_from_radii(cls, min_radius=0.0, maj_radius=0.0):
|
|
1602
|
+
"""
|
|
1603
|
+
returns a Toroid from main parameters (radii).
|
|
1604
|
+
|
|
1605
|
+
Parameters
|
|
1606
|
+
----------
|
|
1607
|
+
min_radius : float, optional
|
|
1608
|
+
The toroid minor radius
|
|
1609
|
+
maj_radius : float, optional
|
|
1610
|
+
The toroid major radius. Note that this is the "optical" major radius at the farest surface from the center
|
|
1611
|
+
of the toroid. Indeed, it corresponds to the "toroid major radius" plus the min_radius.
|
|
1612
|
+
|
|
1613
|
+
Returns
|
|
1614
|
+
-------
|
|
1615
|
+
instance of Toroid
|
|
1616
|
+
|
|
1617
|
+
"""
|
|
1618
|
+
return Toroid(min_radius, maj_radius)
|
|
1619
|
+
|
|
1620
|
+
@classmethod
|
|
1621
|
+
def create_toroid_from_p_q(cls, p=2.0, q=1.0, grazing_angle=0.003):
|
|
1622
|
+
"""
|
|
1623
|
+
returns a Toroid from factory parameters.
|
|
1624
|
+
|
|
1625
|
+
Parameters
|
|
1626
|
+
----------
|
|
1627
|
+
p : float, optional
|
|
1628
|
+
distance source-optical element.
|
|
1629
|
+
q : float, optional
|
|
1630
|
+
distance optical element to focus.
|
|
1631
|
+
grazing_angle : float, optional
|
|
1632
|
+
grazing angle in rad.
|
|
1633
|
+
|
|
1634
|
+
Returns
|
|
1635
|
+
-------
|
|
1636
|
+
instance of Toroid
|
|
1637
|
+
|
|
1638
|
+
"""
|
|
1639
|
+
R = 2 / numpy.sin(grazing_angle) * p * q / (p + q)
|
|
1640
|
+
r = 2 * numpy.sin(grazing_angle) * p * q / (p + q)
|
|
1641
|
+
return Toroid(min_radius=r, maj_radius=R)
|
|
1642
|
+
|
|
1643
|
+
def get_radii(self):
|
|
1644
|
+
"""
|
|
1645
|
+
Returns the radii.
|
|
1646
|
+
|
|
1647
|
+
Returns
|
|
1648
|
+
-------
|
|
1649
|
+
tuple
|
|
1650
|
+
(min_radius, maj_radius).
|
|
1651
|
+
|
|
1652
|
+
"""
|
|
1653
|
+
return self._min_radius, self._maj_radius
|
|
1654
|
+
|
|
1655
|
+
def get_min_radius(self):
|
|
1656
|
+
"""
|
|
1657
|
+
Returns the minor radius.
|
|
1658
|
+
|
|
1659
|
+
Returns
|
|
1660
|
+
-------
|
|
1661
|
+
float
|
|
1662
|
+
|
|
1663
|
+
"""
|
|
1664
|
+
return self._min_radius
|
|
1665
|
+
|
|
1666
|
+
def get_maj_radius(self):
|
|
1667
|
+
"""
|
|
1668
|
+
Returns the major (optical) radius.
|
|
1669
|
+
|
|
1670
|
+
Returns
|
|
1671
|
+
-------
|
|
1672
|
+
float
|
|
1673
|
+
|
|
1674
|
+
"""
|
|
1675
|
+
return self._maj_radius
|
|
1676
|
+
|
|
1677
|
+
def initialize_from_p_q(self, p=2.0, q=1.0, grazing_angle=0.003):
|
|
1678
|
+
"""
|
|
1679
|
+
Sets the parameters calculated from the factory parameters.
|
|
1680
|
+
|
|
1681
|
+
Parameters
|
|
1682
|
+
----------
|
|
1683
|
+
p : float, optional
|
|
1684
|
+
distance source-optical element.
|
|
1685
|
+
q : float, optional
|
|
1686
|
+
distance optical element to focus.
|
|
1687
|
+
grazing_angle : float, optional
|
|
1688
|
+
grazing angle in rad.
|
|
1689
|
+
|
|
1690
|
+
"""
|
|
1691
|
+
self._maj_radius = Sphere.get_radius_from_p_q(p, q, grazing_angle)
|
|
1692
|
+
self._min_radius = SphericalCylinder.get_radius_from_p_q_sagittal(p, q, grazing_angle)
|
|
1693
|
+
|
|
1694
|
+
# FROM SHADOW3:
|
|
1695
|
+
#! C
|
|
1696
|
+
#! C NOTE : The major radius is the in reality the radius of the torus
|
|
1697
|
+
#! C max. circle. The true major radius is then
|
|
1698
|
+
#! C
|
|
1699
|
+
# R_MAJ = R_MAJ - R_MIN
|
|
1700
|
+
self._maj_radius -= self._min_radius
|
|
1701
|
+
|
|
1702
|
+
|
|
1703
|
+
# This is exactly the same as OasysSurfaceData
|
|
1704
|
+
# class OasysSurfaceData(object):
|
|
1705
|
+
class NumericalMesh(SurfaceShape):
|
|
1706
|
+
"""
|
|
1707
|
+
Implements an optical surface from a numerical mesh.
|
|
1708
|
+
|
|
1709
|
+
Constructor.
|
|
1710
|
+
|
|
1711
|
+
Parameters
|
|
1712
|
+
----------
|
|
1713
|
+
xx : numpy array, optional
|
|
1714
|
+
The x vector.
|
|
1715
|
+
yy : numpy array, optional
|
|
1716
|
+
The y vector.
|
|
1717
|
+
zz : numpy array, optional
|
|
1718
|
+
The z (2D) array.
|
|
1719
|
+
surface_data_file : str, optional
|
|
1720
|
+
a file name from where the dara may come.
|
|
1721
|
+
|
|
1722
|
+
Notes
|
|
1723
|
+
-----
|
|
1724
|
+
This is exactly the same as OasysSurfaceData class OasysSurfaceData(object), with added methods.
|
|
1725
|
+
|
|
1726
|
+
"""
|
|
1727
|
+
def __init__(self,
|
|
1728
|
+
xx=None,
|
|
1729
|
+
yy=None,
|
|
1730
|
+
zz=None,
|
|
1731
|
+
surface_data_file=None):
|
|
1732
|
+
self._xx = xx
|
|
1733
|
+
self._yy = yy
|
|
1734
|
+
self._zz = zz
|
|
1735
|
+
self._surface_data_file=surface_data_file
|
|
1736
|
+
|
|
1737
|
+
def has_surface_data(self):
|
|
1738
|
+
"""
|
|
1739
|
+
Returns True is data is loaded.
|
|
1740
|
+
|
|
1741
|
+
Returns
|
|
1742
|
+
-------
|
|
1743
|
+
boolean
|
|
1744
|
+
|
|
1745
|
+
"""
|
|
1746
|
+
return not (self._xx is None or self._yy is None or self._zz is None)
|
|
1747
|
+
|
|
1748
|
+
def has_surface_data_file(self):
|
|
1749
|
+
"""
|
|
1750
|
+
Returns True is data file is set.
|
|
1751
|
+
|
|
1752
|
+
Returns
|
|
1753
|
+
-------
|
|
1754
|
+
boolean
|
|
1755
|
+
|
|
1756
|
+
"""
|
|
1757
|
+
return not self._surface_data_file is None
|
|
1758
|
+
|
|
1759
|
+
|
|
1760
|
+
##############################
|
|
1761
|
+
# subclasses for BoundaryShape
|
|
1762
|
+
##############################
|
|
1763
|
+
|
|
1764
|
+
|
|
1765
|
+
class Rectangle(BoundaryShape):
|
|
1766
|
+
"""
|
|
1767
|
+
Constructor.
|
|
1768
|
+
|
|
1769
|
+
Parameters
|
|
1770
|
+
----------
|
|
1771
|
+
x_left : float, optional
|
|
1772
|
+
The coordinate (signed) of the minimum (left) along the X axis.
|
|
1773
|
+
x_right : float, optional
|
|
1774
|
+
The coordinate (signed) of the maximum (right) along the X axis.
|
|
1775
|
+
y_bottom : float, optional
|
|
1776
|
+
The coordinate (signed) of the minimum (left) along the Y axis.
|
|
1777
|
+
y_top : float, optional
|
|
1778
|
+
The coordinate (signed) of the maximum (right) along the Y axis.
|
|
1779
|
+
"""
|
|
1780
|
+
def __init__(self, x_left=-0.010, x_right=0.010, y_bottom=-0.020, y_top=0.020):
|
|
1781
|
+
super().__init__()
|
|
1782
|
+
|
|
1783
|
+
self._x_left = x_left
|
|
1784
|
+
self._x_right = x_right
|
|
1785
|
+
self._y_bottom = y_bottom
|
|
1786
|
+
self._y_top = y_top
|
|
1787
|
+
|
|
1788
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
1789
|
+
self._set_support_text([
|
|
1790
|
+
("x_left" , "x (width) minimum (signed) ", "m" ),
|
|
1791
|
+
("x_right" , "x (width) maximum (signed) ", "m" ),
|
|
1792
|
+
("y_bottom" , "y (length) minimum (signed) ", "m" ),
|
|
1793
|
+
("y_top" , "y (length) maximum (signed) ", "m" ),
|
|
1794
|
+
] )
|
|
1795
|
+
|
|
1796
|
+
def get_boundaries(self):
|
|
1797
|
+
"""
|
|
1798
|
+
Return the rectangle coordinates.
|
|
1799
|
+
|
|
1800
|
+
Returns
|
|
1801
|
+
-------
|
|
1802
|
+
tuple
|
|
1803
|
+
(x_left, x_right, y_bottom, y_top).
|
|
1804
|
+
|
|
1805
|
+
"""
|
|
1806
|
+
return self._x_left, self._x_right, self._y_bottom, self._y_top
|
|
1807
|
+
|
|
1808
|
+
def set_boundaries(self,x_left=-0.010, x_right=0.010, y_bottom=-0.020, y_top=0.020):
|
|
1809
|
+
"""
|
|
1810
|
+
Sets the rectangle coordinates.
|
|
1811
|
+
|
|
1812
|
+
Parameters
|
|
1813
|
+
----------
|
|
1814
|
+
x_left : float, optional
|
|
1815
|
+
The coordinate (signed) of the minimum (left) along the X axis.
|
|
1816
|
+
x_right : float, optional
|
|
1817
|
+
The coordinate (signed) of the maximum (right) along the X axis.
|
|
1818
|
+
y_bottom : float, optional
|
|
1819
|
+
The coordinate (signed) of the minimum (left) along the Y axis.
|
|
1820
|
+
y_top : float, optional
|
|
1821
|
+
The coordinate (signed) of the maximum (right) along the Y axis.
|
|
1822
|
+
|
|
1823
|
+
"""
|
|
1824
|
+
self._x_left = x_left
|
|
1825
|
+
self._x_right = x_right
|
|
1826
|
+
self._y_bottom = y_bottom
|
|
1827
|
+
self._y_top = y_top
|
|
1828
|
+
|
|
1829
|
+
def set_width_and_length(self,width=10e-3,length=30e-3):
|
|
1830
|
+
"""
|
|
1831
|
+
Sets the rectangle parameters from width and length (centered at the origin).
|
|
1832
|
+
|
|
1833
|
+
Parameters
|
|
1834
|
+
----------
|
|
1835
|
+
width : float, optional
|
|
1836
|
+
The rectangle width.
|
|
1837
|
+
length : float, optional
|
|
1838
|
+
The rectangle length.
|
|
1839
|
+
|
|
1840
|
+
"""
|
|
1841
|
+
self._x_left = -0.5 * width
|
|
1842
|
+
self._x_right = 0.5 * width
|
|
1843
|
+
self._y_bottom = -0.5 * length
|
|
1844
|
+
self._y_top = 0.5 * length
|
|
1845
|
+
|
|
1846
|
+
class Ellipse(BoundaryShape):
|
|
1847
|
+
"""
|
|
1848
|
+
Constructor.
|
|
1849
|
+
|
|
1850
|
+
Parameters
|
|
1851
|
+
----------
|
|
1852
|
+
a_axis_min : float, optional
|
|
1853
|
+
The coordinate (signed) of the minimum (left) along the major axis.
|
|
1854
|
+
a_axis_max : float, optional
|
|
1855
|
+
The coordinate (signed) of the maximum (right) along the major axis.
|
|
1856
|
+
b_axis_min : float, optional
|
|
1857
|
+
The coordinate (signed) of the minimum (left) along the minor axis.
|
|
1858
|
+
b_axis_max : float, optional
|
|
1859
|
+
The coordinate (signed) of the maximum (right) along the minor axis.
|
|
1860
|
+
|
|
1861
|
+
"""
|
|
1862
|
+
def __init__(self, a_axis_min=-10e-6, a_axis_max=10e-6, b_axis_min=-5e-6, b_axis_max=5e-6):
|
|
1863
|
+
super().__init__()
|
|
1864
|
+
|
|
1865
|
+
self._a_axis_min = a_axis_min
|
|
1866
|
+
self._a_axis_max = a_axis_max
|
|
1867
|
+
self._b_axis_min = b_axis_min
|
|
1868
|
+
self._b_axis_max = b_axis_max
|
|
1869
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
1870
|
+
self._set_support_text([
|
|
1871
|
+
("a_axis_min" , "x (width) axis starts (signed) ", "m" ),
|
|
1872
|
+
("a_axis_max" , "x (width) axis ends (signed) ", "m" ),
|
|
1873
|
+
("b_axis_min" , "y (length) axis starts (signed) ", "m" ),
|
|
1874
|
+
("b_axis_max" , "y (length) axis ends (signed) ", "m" ),
|
|
1875
|
+
] )
|
|
1876
|
+
|
|
1877
|
+
def get_boundaries(self):
|
|
1878
|
+
"""
|
|
1879
|
+
Returns the coordinates of the ellipse.
|
|
1880
|
+
|
|
1881
|
+
Returns
|
|
1882
|
+
-------
|
|
1883
|
+
tuple
|
|
1884
|
+
(a_axis_min, a_axis_max, b_axis_min, b_axis_max).
|
|
1885
|
+
|
|
1886
|
+
"""
|
|
1887
|
+
return self._a_axis_min, self._a_axis_max, self._b_axis_min, self._b_axis_max
|
|
1888
|
+
|
|
1889
|
+
def get_axis(self):
|
|
1890
|
+
"""
|
|
1891
|
+
Returns the length of the ellipse axes.
|
|
1892
|
+
|
|
1893
|
+
Returns
|
|
1894
|
+
-------
|
|
1895
|
+
tuple
|
|
1896
|
+
(a_length, b_length).
|
|
1897
|
+
|
|
1898
|
+
"""
|
|
1899
|
+
return numpy.abs(self._a_axis_max - self._a_axis_min), numpy.abs(self._b_axis_max - self._b_axis_min)
|
|
1900
|
+
|
|
1901
|
+
|
|
1902
|
+
class TwoEllipses(BoundaryShape):
|
|
1903
|
+
"""
|
|
1904
|
+
Constructor.
|
|
1905
|
+
|
|
1906
|
+
Parameters
|
|
1907
|
+
----------
|
|
1908
|
+
a1_axis_min : float, optional
|
|
1909
|
+
The coordinate (signed) of the minimum (left) along the major axis of ellipse 1.
|
|
1910
|
+
a1_axis_max : float, optional
|
|
1911
|
+
The coordinate (signed) of the maximum (right) along the major axis of ellipse 1.
|
|
1912
|
+
b1_axis_min : float, optional
|
|
1913
|
+
TThe coordinate (signed) of the minimum (left) along the minor axis of ellipse 1.
|
|
1914
|
+
b1_axis_max : float, optional
|
|
1915
|
+
The coordinate (signed) of the maximum (right) along the minor axis of ellipse 1.
|
|
1916
|
+
a2_axis_min : float, optional
|
|
1917
|
+
The coordinate (signed) of the minimum (left) along the major axis of ellipse 2.
|
|
1918
|
+
a2_axis_max : float, optional
|
|
1919
|
+
The coordinate (signed) of the maximum (right) along the major axis of ellipse 2.
|
|
1920
|
+
b2_axis_min : float, optional
|
|
1921
|
+
TThe coordinate (signed) of the minimum (left) along the minor axis of ellipse 2.
|
|
1922
|
+
b2_axis_max : float, optional
|
|
1923
|
+
The coordinate (signed) of the maximum (right) along the minor axis of ellipse 2.
|
|
1924
|
+
|
|
1925
|
+
"""
|
|
1926
|
+
def __init__(self,
|
|
1927
|
+
a1_axis_min=-10e-6, a1_axis_max=10e-6, b1_axis_min=-5e-6, b1_axis_max=5e-6,
|
|
1928
|
+
a2_axis_min=-20e-6, a2_axis_max=20e-6, b2_axis_min=-8e-6, b2_axis_max=8e-6):
|
|
1929
|
+
super().__init__()
|
|
1930
|
+
|
|
1931
|
+
self._a1_axis_min = a1_axis_min
|
|
1932
|
+
self._a1_axis_max = a1_axis_max
|
|
1933
|
+
self._b1_axis_min = b1_axis_min
|
|
1934
|
+
self._b1_axis_max = b1_axis_max
|
|
1935
|
+
self._a2_axis_min = a2_axis_min
|
|
1936
|
+
self._a2_axis_max = a2_axis_max
|
|
1937
|
+
self._b2_axis_min = b2_axis_min
|
|
1938
|
+
self._b2_axis_max = b2_axis_max
|
|
1939
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
1940
|
+
self._set_support_text([
|
|
1941
|
+
("a1_axis_min", "x (width) axis 1 starts (signed) ", "m" ),
|
|
1942
|
+
("a1_axis_max", "x (width) axis 1 ends (signed) ", "m" ),
|
|
1943
|
+
("b1_axis_min", "y (length) axis 1 starts (signed) ", "m" ),
|
|
1944
|
+
("b1_axis_max", "y (length) axis 1 ends (signed) ", "m" ),
|
|
1945
|
+
("a2_axis_min", "x (width) axis 2 starts (signed) ", "m"),
|
|
1946
|
+
("a2_axis_max", "x (width) axis 2 ends (signed) ", "m"),
|
|
1947
|
+
("b2_axis_min", "y (length) axis 2 starts (signed) ", "m"),
|
|
1948
|
+
("b2_axis_max", "y (length) axis 2 ends (signed) ", "m"),
|
|
1949
|
+
] )
|
|
1950
|
+
|
|
1951
|
+
def get_boundaries(self):
|
|
1952
|
+
"""
|
|
1953
|
+
Return the coordinates of the ellipses.
|
|
1954
|
+
|
|
1955
|
+
Returns
|
|
1956
|
+
-------
|
|
1957
|
+
tuple
|
|
1958
|
+
(a1_axis_min, a1_axis_max, b1_axis_min, b1_axis_max, a2_axis_min, a2_axis_max, b2_axis_min, b2_axis_max).
|
|
1959
|
+
|
|
1960
|
+
"""
|
|
1961
|
+
return \
|
|
1962
|
+
self._a1_axis_min, self._a1_axis_max, self._b1_axis_min, self._b1_axis_max, \
|
|
1963
|
+
self._a2_axis_min, self._a2_axis_max, self._b2_axis_min, self._b2_axis_max
|
|
1964
|
+
|
|
1965
|
+
def get_axis(self):
|
|
1966
|
+
"""
|
|
1967
|
+
Returns the lengths of the axes of the two ellipses.
|
|
1968
|
+
|
|
1969
|
+
Returns
|
|
1970
|
+
-------
|
|
1971
|
+
tuple
|
|
1972
|
+
(a1_length, b1_length, a2_length, b2_length).
|
|
1973
|
+
|
|
1974
|
+
"""
|
|
1975
|
+
return \
|
|
1976
|
+
numpy.abs(self._a1_axis_max - self._a1_axis_min), numpy.abs(self._b1_axis_max - self._b2_axis_min), \
|
|
1977
|
+
numpy.abs(self._a2_axis_max - self._a2_axis_min), numpy.abs(self._b2_axis_max - self._b2_axis_min)
|
|
1978
|
+
|
|
1979
|
+
|
|
1980
|
+
class Circle(BoundaryShape):
|
|
1981
|
+
"""
|
|
1982
|
+
Constructor.
|
|
1983
|
+
|
|
1984
|
+
Parameters
|
|
1985
|
+
----------
|
|
1986
|
+
radius : float
|
|
1987
|
+
The radius of the circle.
|
|
1988
|
+
x_center : float
|
|
1989
|
+
The x coordinate of the center of the circle.
|
|
1990
|
+
y_center : float
|
|
1991
|
+
The y coordinate of the center of the circle.
|
|
1992
|
+
|
|
1993
|
+
"""
|
|
1994
|
+
def __init__(self,radius=50e-6,x_center=0.0,y_center=0.0):
|
|
1995
|
+
super().__init__()
|
|
1996
|
+
|
|
1997
|
+
self._radius = radius
|
|
1998
|
+
self._x_center = x_center
|
|
1999
|
+
self._y_center = y_center
|
|
2000
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
2001
|
+
self._set_support_text([
|
|
2002
|
+
("radius" , "radius ", "m" ),
|
|
2003
|
+
("x_center" , "x center (signed) ", "m" ),
|
|
2004
|
+
("y_center" , "y center (signed) ", "m" ),
|
|
2005
|
+
] )
|
|
2006
|
+
|
|
2007
|
+
def get_boundaries(self):
|
|
2008
|
+
"""
|
|
2009
|
+
Returns the circle parameters.
|
|
2010
|
+
|
|
2011
|
+
Returns
|
|
2012
|
+
-------
|
|
2013
|
+
tuple
|
|
2014
|
+
(radius, x_center, y_center).
|
|
2015
|
+
|
|
2016
|
+
"""
|
|
2017
|
+
return self._radius, self._x_center, self._y_center
|
|
2018
|
+
|
|
2019
|
+
def set_boundaries(self, radius=1.0, x_center=0.0, y_center=0.0):
|
|
2020
|
+
"""
|
|
2021
|
+
Sets the circle parameters.
|
|
2022
|
+
|
|
2023
|
+
Parameters
|
|
2024
|
+
----------
|
|
2025
|
+
radius : float
|
|
2026
|
+
The radius of the circle.
|
|
2027
|
+
x_center : float
|
|
2028
|
+
The x coordinate of the center of the circle.
|
|
2029
|
+
y_center : float
|
|
2030
|
+
The y coordinate of the center of the circle.
|
|
2031
|
+
|
|
2032
|
+
"""
|
|
2033
|
+
self._radius = radius
|
|
2034
|
+
self._x_center = x_center
|
|
2035
|
+
self._y_center = y_center
|
|
2036
|
+
|
|
2037
|
+
def get_radius(self):
|
|
2038
|
+
"""
|
|
2039
|
+
Returns the radius of the circle.
|
|
2040
|
+
|
|
2041
|
+
Returns
|
|
2042
|
+
-------
|
|
2043
|
+
float
|
|
2044
|
+
|
|
2045
|
+
"""
|
|
2046
|
+
return self._radius
|
|
2047
|
+
|
|
2048
|
+
def get_center(self):
|
|
2049
|
+
"""
|
|
2050
|
+
Returns the coordinates of the circle.
|
|
2051
|
+
|
|
2052
|
+
Returns
|
|
2053
|
+
-------
|
|
2054
|
+
list
|
|
2055
|
+
[x_center, y_center]
|
|
2056
|
+
|
|
2057
|
+
"""
|
|
2058
|
+
return [self._x_center,self._y_center]
|
|
2059
|
+
|
|
2060
|
+
class Polygon(BoundaryShape):
|
|
2061
|
+
"""
|
|
2062
|
+
Constructor.
|
|
2063
|
+
|
|
2064
|
+
Parameters
|
|
2065
|
+
----------
|
|
2066
|
+
x : list, optional
|
|
2067
|
+
A list with the X coordinates of the patch vertices.
|
|
2068
|
+
y : list, optional
|
|
2069
|
+
A list with the Y coordinates of the patch vertices.
|
|
2070
|
+
|
|
2071
|
+
"""
|
|
2072
|
+
def __init__(self,x=[],y=[]):
|
|
2073
|
+
super().__init__()
|
|
2074
|
+
|
|
2075
|
+
self._x = numpy.array(x)
|
|
2076
|
+
self._y = numpy.array(y)
|
|
2077
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
2078
|
+
self._set_support_text([
|
|
2079
|
+
("x" , "x vertices ", "m" ),
|
|
2080
|
+
("y" , "y vertices ", "m" ),
|
|
2081
|
+
] )
|
|
2082
|
+
|
|
2083
|
+
def get_boundaries(self):
|
|
2084
|
+
"""
|
|
2085
|
+
Returns the coordinates of the patch vertices.
|
|
2086
|
+
|
|
2087
|
+
Returns
|
|
2088
|
+
-------
|
|
2089
|
+
tuple
|
|
2090
|
+
(list_of_x_coordinates, list_of_y_coordinates).
|
|
2091
|
+
|
|
2092
|
+
"""
|
|
2093
|
+
return self._x, self._y
|
|
2094
|
+
|
|
2095
|
+
def set_boundaries(self, x, y):
|
|
2096
|
+
"""
|
|
2097
|
+
Sets the coordinates of the patch vertices.
|
|
2098
|
+
|
|
2099
|
+
Parameters
|
|
2100
|
+
----------
|
|
2101
|
+
x : list
|
|
2102
|
+
A list with the X coordinates of the patch vertices.
|
|
2103
|
+
y : list
|
|
2104
|
+
A list with the Y coordinates of the patch vertices.
|
|
2105
|
+
|
|
2106
|
+
"""
|
|
2107
|
+
self._x = numpy.array(x)
|
|
2108
|
+
self._y = numpy.array(y)
|
|
2109
|
+
|
|
2110
|
+
def get_number_of_vertices(self):
|
|
2111
|
+
"""
|
|
2112
|
+
Returns the number of vertices.
|
|
2113
|
+
|
|
2114
|
+
Returns
|
|
2115
|
+
-------
|
|
2116
|
+
int
|
|
2117
|
+
|
|
2118
|
+
"""
|
|
2119
|
+
n = numpy.array(self._x).size
|
|
2120
|
+
if (numpy.abs(self._x[0] - self._x[-1]) < 1e-10) and (numpy.abs(self._y[0] - self._y[-1]) < 1e-10):
|
|
2121
|
+
# print(">>>>> same first and last point")
|
|
2122
|
+
n -= 1
|
|
2123
|
+
return n
|
|
2124
|
+
|
|
2125
|
+
def get_polygon(self):
|
|
2126
|
+
"""
|
|
2127
|
+
Returns the vertices arranges as a polugon.
|
|
2128
|
+
|
|
2129
|
+
Returns
|
|
2130
|
+
-------
|
|
2131
|
+
list
|
|
2132
|
+
[[x0,y0], [x1,y1], ...]
|
|
2133
|
+
|
|
2134
|
+
"""
|
|
2135
|
+
polygon = []
|
|
2136
|
+
for i in range(self.get_number_of_vertices()):
|
|
2137
|
+
polygon.append([self._x[i], self._y[i]])
|
|
2138
|
+
|
|
2139
|
+
return polygon
|
|
2140
|
+
|
|
2141
|
+
def check_inside_vector(self, x0, y0):
|
|
2142
|
+
"""
|
|
2143
|
+
Checks if a set of points are inside the patch (closed as polygon).
|
|
2144
|
+
|
|
2145
|
+
Parameters
|
|
2146
|
+
----------
|
|
2147
|
+
x0 : numpy array
|
|
2148
|
+
The X coordinates of the points to check.
|
|
2149
|
+
y0 : numpy array
|
|
2150
|
+
The Y coordinates of the points to check.
|
|
2151
|
+
|
|
2152
|
+
Returns
|
|
2153
|
+
-------
|
|
2154
|
+
numpy array
|
|
2155
|
+
0=No, 1=Yes (inside).
|
|
2156
|
+
|
|
2157
|
+
References
|
|
2158
|
+
----------
|
|
2159
|
+
https://stackoverflow.com/questions/36399381/whats-the-fastest-way-of-checking-if-a-point-is-inside-a-polygon-in-python
|
|
2160
|
+
|
|
2161
|
+
"""
|
|
2162
|
+
# see https://stackoverflow.com/questions/36399381/whats-the-fastest-way-of-checking-if-a-point-is-inside-a-polygon-in-python
|
|
2163
|
+
poly = self.get_polygon()
|
|
2164
|
+
n = len(poly)
|
|
2165
|
+
x = numpy.array(x0)
|
|
2166
|
+
y = numpy.array(y0)
|
|
2167
|
+
|
|
2168
|
+
inside = numpy.zeros(x.size, numpy.bool_)
|
|
2169
|
+
p2x = 0.0
|
|
2170
|
+
p2y = 0.0
|
|
2171
|
+
xints = 0.0
|
|
2172
|
+
p1x, p1y = poly[0]
|
|
2173
|
+
|
|
2174
|
+
for i in range(n + 1):
|
|
2175
|
+
p2x, p2y = poly[i % n]
|
|
2176
|
+
|
|
2177
|
+
idx = numpy.nonzero((y > min(p1y, p2y)) & (y <= max(p1y, p2y)) & (x <= max(p1x, p2x)))[0]
|
|
2178
|
+
if len(idx > 0): # added intuitively by srio TODO: make some tests to compare with self.check_insize
|
|
2179
|
+
if p1y != p2y:
|
|
2180
|
+
xints = (y[idx] - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
|
|
2181
|
+
if p1x == p2x:
|
|
2182
|
+
inside[idx] = ~inside[idx]
|
|
2183
|
+
else:
|
|
2184
|
+
idxx = idx[x[idx] <= xints]
|
|
2185
|
+
inside[idxx] = ~inside[idxx]
|
|
2186
|
+
|
|
2187
|
+
p1x, p1y = p2x, p2y
|
|
2188
|
+
return inside
|
|
2189
|
+
|
|
2190
|
+
def check_inside(self, x, y):
|
|
2191
|
+
"""
|
|
2192
|
+
Checks if a set of points are inside the patch (closed as polygon).
|
|
2193
|
+
|
|
2194
|
+
Parameters
|
|
2195
|
+
----------
|
|
2196
|
+
x0 : list
|
|
2197
|
+
The X coordinates of the points to check.
|
|
2198
|
+
y0 : list
|
|
2199
|
+
The Y coordinates of the points to check.
|
|
2200
|
+
|
|
2201
|
+
Returns
|
|
2202
|
+
-------
|
|
2203
|
+
numpy array
|
|
2204
|
+
0=No, 1=Yes (inside).
|
|
2205
|
+
|
|
2206
|
+
References
|
|
2207
|
+
----------
|
|
2208
|
+
https://stackoverflow.com/questions/36399381/whats-the-fastest-way-of-checking-if-a-point-is-inside-a-polygon-in-python
|
|
2209
|
+
|
|
2210
|
+
"""
|
|
2211
|
+
return [self.check_inside_one_point(xi, yi) for xi, yi in zip(x, y)]
|
|
2212
|
+
|
|
2213
|
+
def check_inside_one_point(self, x0, y0):
|
|
2214
|
+
"""
|
|
2215
|
+
Checks if a single point is inside the patch (closed as polygon).
|
|
2216
|
+
|
|
2217
|
+
Parameters
|
|
2218
|
+
----------
|
|
2219
|
+
x0 : float
|
|
2220
|
+
The X coordinate pf the point to check.
|
|
2221
|
+
y0 : float
|
|
2222
|
+
The Y coordinate pf the point to check.
|
|
2223
|
+
|
|
2224
|
+
Returns
|
|
2225
|
+
-------
|
|
2226
|
+
boolean
|
|
2227
|
+
|
|
2228
|
+
References
|
|
2229
|
+
----------
|
|
2230
|
+
https://stackoverflow.com/questions/36399381/whats-the-fastest-way-of-checking-if-a-point-is-inside-a-polygon-in-python
|
|
2231
|
+
|
|
2232
|
+
"""
|
|
2233
|
+
# see https://stackoverflow.com/questions/36399381/whats-the-fastest-way-of-checking-if-a-point-is-inside-a-polygon-in-python
|
|
2234
|
+
poly = self.get_polygon()
|
|
2235
|
+
x = x0
|
|
2236
|
+
y = y0
|
|
2237
|
+
n = len(poly)
|
|
2238
|
+
inside = False
|
|
2239
|
+
p2x = 0.0
|
|
2240
|
+
p2y = 0.0
|
|
2241
|
+
xints = 0.0
|
|
2242
|
+
p1x, p1y = poly[0]
|
|
2243
|
+
for i in range(n + 1):
|
|
2244
|
+
p2x, p2y = poly[i % n]
|
|
2245
|
+
if y > min(p1y, p2y):
|
|
2246
|
+
if y <= max(p1y, p2y):
|
|
2247
|
+
if x <= max(p1x, p2x):
|
|
2248
|
+
if p1y != p2y:
|
|
2249
|
+
xints = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
|
|
2250
|
+
if p1x == p2x or x <= xints:
|
|
2251
|
+
inside = not inside
|
|
2252
|
+
p1x, p1y = p2x, p2y
|
|
2253
|
+
|
|
2254
|
+
return inside
|
|
2255
|
+
|
|
2256
|
+
def check_outside(self, x0, y0):
|
|
2257
|
+
"""
|
|
2258
|
+
Checks if a set of points are outside the patch (closed as polygon).
|
|
2259
|
+
|
|
2260
|
+
Parameters
|
|
2261
|
+
----------
|
|
2262
|
+
x0 : list
|
|
2263
|
+
The X coordinates of the points to check.
|
|
2264
|
+
y0 : list
|
|
2265
|
+
The Y coordinates of the points to check.
|
|
2266
|
+
|
|
2267
|
+
Returns
|
|
2268
|
+
-------
|
|
2269
|
+
numpy array
|
|
2270
|
+
0=No, 1=Yes (outside).
|
|
2271
|
+
|
|
2272
|
+
References
|
|
2273
|
+
----------
|
|
2274
|
+
https://stackoverflow.com/questions/36399381/whats-the-fastest-way-of-checking-if-a-point-is-inside-a-polygon-in-python
|
|
2275
|
+
|
|
2276
|
+
"""
|
|
2277
|
+
inside = self.check_inside(x0, y0)
|
|
2278
|
+
if isinstance(inside, list):
|
|
2279
|
+
out = []
|
|
2280
|
+
for item in inside:
|
|
2281
|
+
out.append(not(item))
|
|
2282
|
+
else:
|
|
2283
|
+
out = not(inside)
|
|
2284
|
+
|
|
2285
|
+
return out
|
|
2286
|
+
|
|
2287
|
+
|
|
2288
|
+
class MultiplePatch(BoundaryShape):
|
|
2289
|
+
"""
|
|
2290
|
+
Constructor.
|
|
2291
|
+
|
|
2292
|
+
Parameters
|
|
2293
|
+
----------
|
|
2294
|
+
patch_list : list
|
|
2295
|
+
A list of patches (each one can be a Circle, Rectangle, Polygon, etc.)
|
|
2296
|
+
|
|
2297
|
+
"""
|
|
2298
|
+
def __init__(self, patch_list=None):
|
|
2299
|
+
super().__init__()
|
|
2300
|
+
|
|
2301
|
+
if patch_list is None:
|
|
2302
|
+
self._patch_list = []
|
|
2303
|
+
else:
|
|
2304
|
+
self._patch_list = patch_list
|
|
2305
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
2306
|
+
self._set_support_text([
|
|
2307
|
+
("patch_list", "Multiple Patch", ""),
|
|
2308
|
+
])
|
|
2309
|
+
|
|
2310
|
+
|
|
2311
|
+
# overwrites the SynedObject method for dealing with list
|
|
2312
|
+
def to_dictionary(self):
|
|
2313
|
+
"""
|
|
2314
|
+
Gets the dictionary with the multiple patch parameters.
|
|
2315
|
+
|
|
2316
|
+
Returns
|
|
2317
|
+
-------
|
|
2318
|
+
dict
|
|
2319
|
+
|
|
2320
|
+
"""
|
|
2321
|
+
dict_to_save = OrderedDict()
|
|
2322
|
+
dict_to_save.update({"CLASS_NAME":self.__class__.__name__})
|
|
2323
|
+
|
|
2324
|
+
dict_to_save["patch_list"] = [el.to_dictionary() for el in self._patch_list]
|
|
2325
|
+
|
|
2326
|
+
return dict_to_save
|
|
2327
|
+
|
|
2328
|
+
|
|
2329
|
+
def reset(self):
|
|
2330
|
+
"""
|
|
2331
|
+
Removes all existing patches.
|
|
2332
|
+
"""
|
|
2333
|
+
self._patch_list = []
|
|
2334
|
+
|
|
2335
|
+
def get_number_of_patches(self):
|
|
2336
|
+
"""
|
|
2337
|
+
Returns the number of stored patches.
|
|
2338
|
+
|
|
2339
|
+
Returns
|
|
2340
|
+
-------
|
|
2341
|
+
int
|
|
2342
|
+
|
|
2343
|
+
"""
|
|
2344
|
+
return len(self._patch_list)
|
|
2345
|
+
|
|
2346
|
+
def get_boundaries(self):
|
|
2347
|
+
"""
|
|
2348
|
+
Returns a list with the concatenated boundaries of the sotred patches.
|
|
2349
|
+
|
|
2350
|
+
Returns
|
|
2351
|
+
-------
|
|
2352
|
+
list
|
|
2353
|
+
|
|
2354
|
+
"""
|
|
2355
|
+
boundaries_list = []
|
|
2356
|
+
for i in range(self.get_number_of_patches()):
|
|
2357
|
+
boundaries_list.extend(list(self._patch_list[i].get_boundaries()))
|
|
2358
|
+
return tuple(boundaries_list)
|
|
2359
|
+
|
|
2360
|
+
def append_patch(self,patch=BoundaryShape()):
|
|
2361
|
+
"""
|
|
2362
|
+
Append a patch.
|
|
2363
|
+
|
|
2364
|
+
Parameters
|
|
2365
|
+
----------
|
|
2366
|
+
patch : instance of Rectangle, Circle, etc.
|
|
2367
|
+
|
|
2368
|
+
"""
|
|
2369
|
+
self._patch_list.append(patch)
|
|
2370
|
+
|
|
2371
|
+
def append_rectangle(self,x_left=-0.010,x_right=0.010,y_bottom=-0.020,y_top=0.020):
|
|
2372
|
+
"""
|
|
2373
|
+
Appends a rectangle.
|
|
2374
|
+
|
|
2375
|
+
Parameters
|
|
2376
|
+
----------
|
|
2377
|
+
x_left : float, optional
|
|
2378
|
+
The coordinate (signed) of the minimum (left) along the X axis.
|
|
2379
|
+
x_right : float, optional
|
|
2380
|
+
The coordinate (signed) of the maximum (right) along the X axis.
|
|
2381
|
+
y_bottom : float, optional
|
|
2382
|
+
The coordinate (signed) of the minimum (left) along the Y axis.
|
|
2383
|
+
y_top : float, optional
|
|
2384
|
+
The coordinate (signed) of the maximum (right) along the Y axis.
|
|
2385
|
+
|
|
2386
|
+
|
|
2387
|
+
"""
|
|
2388
|
+
self.append_patch(Rectangle(x_left=x_left, x_right=x_right, y_bottom=y_bottom, y_top=y_top))
|
|
2389
|
+
|
|
2390
|
+
def append_circle(self,radius, x_center=0.0, y_center=0.0):
|
|
2391
|
+
"""
|
|
2392
|
+
Appends a circle.
|
|
2393
|
+
|
|
2394
|
+
Parameters
|
|
2395
|
+
----------
|
|
2396
|
+
radius : float
|
|
2397
|
+
The radius of the circle.
|
|
2398
|
+
x_center : float
|
|
2399
|
+
The x coordinate of the center of the circle.
|
|
2400
|
+
y_center : float
|
|
2401
|
+
The y coordinate of the center of the circle.
|
|
2402
|
+
|
|
2403
|
+
"""
|
|
2404
|
+
self.append_patch(Circle(radius, x_center=x_center, y_center=y_center))
|
|
2405
|
+
|
|
2406
|
+
def append_ellipse(self,a_axis_min, a_axis_max, b_axis_min, b_axis_max):
|
|
2407
|
+
"""
|
|
2408
|
+
Appends an ellipse.
|
|
2409
|
+
|
|
2410
|
+
Parameters
|
|
2411
|
+
----------
|
|
2412
|
+
a_axis_min : float, optional
|
|
2413
|
+
The coordinate (signed) of the minimum (left) along the major axis.
|
|
2414
|
+
a_axis_max : float, optional
|
|
2415
|
+
The coordinate (signed) of the maximum (right) along the major axis.
|
|
2416
|
+
b_axis_min : float, optional
|
|
2417
|
+
TThe coordinate (signed) of the minimum (left) along the minor axis.
|
|
2418
|
+
b_axis_max : float, optional
|
|
2419
|
+
The coordinate (signed) of the maximum (right) along the minor axis.
|
|
2420
|
+
|
|
2421
|
+
"""
|
|
2422
|
+
self.append_patch(Ellipse(a_axis_min, a_axis_max, b_axis_min, b_axis_max))
|
|
2423
|
+
|
|
2424
|
+
def append_polygon(self,x, y):
|
|
2425
|
+
"""
|
|
2426
|
+
Appends a polygon.
|
|
2427
|
+
|
|
2428
|
+
Parameters
|
|
2429
|
+
----------
|
|
2430
|
+
x : list
|
|
2431
|
+
The polygon X coordinates.
|
|
2432
|
+
y : list
|
|
2433
|
+
The polygon Y coordinates.
|
|
2434
|
+
|
|
2435
|
+
Returns
|
|
2436
|
+
-------
|
|
2437
|
+
|
|
2438
|
+
"""
|
|
2439
|
+
self.append_patch(Polygon(x, y))
|
|
2440
|
+
|
|
2441
|
+
def get_patches(self):
|
|
2442
|
+
"""
|
|
2443
|
+
Returns a list with the patches.
|
|
2444
|
+
|
|
2445
|
+
Returns
|
|
2446
|
+
-------
|
|
2447
|
+
list
|
|
2448
|
+
|
|
2449
|
+
"""
|
|
2450
|
+
return self._patch_list
|
|
2451
|
+
|
|
2452
|
+
def get_patch(self, index):
|
|
2453
|
+
"""
|
|
2454
|
+
Returns the patch corresponding to a given index.
|
|
2455
|
+
|
|
2456
|
+
Parameters
|
|
2457
|
+
----------
|
|
2458
|
+
index : int
|
|
2459
|
+
The index of the wanted patch.
|
|
2460
|
+
|
|
2461
|
+
Returns
|
|
2462
|
+
-------
|
|
2463
|
+
instance of BoundaryShape (Circle, Rectangle, etc.).
|
|
2464
|
+
|
|
2465
|
+
"""
|
|
2466
|
+
return self.get_patches()[index]
|
|
2467
|
+
|
|
2468
|
+
def get_name_of_patch(self,index):
|
|
2469
|
+
"""
|
|
2470
|
+
Returns the name of the patch with a given index.
|
|
2471
|
+
|
|
2472
|
+
Parameters
|
|
2473
|
+
----------
|
|
2474
|
+
index : int
|
|
2475
|
+
The index of the wanted patch.
|
|
2476
|
+
|
|
2477
|
+
Returns
|
|
2478
|
+
-------
|
|
2479
|
+
str
|
|
2480
|
+
|
|
2481
|
+
"""
|
|
2482
|
+
return self._patch_list[index].__class__.__name__
|
|
2483
|
+
|
|
2484
|
+
class DoubleRectangle(MultiplePatch):
|
|
2485
|
+
"""
|
|
2486
|
+
Constructor.
|
|
2487
|
+
|
|
2488
|
+
Parameters
|
|
2489
|
+
----------
|
|
2490
|
+
x_left1 : float, optional
|
|
2491
|
+
The coordinate (signed) of the minimum (left) along the X axis of rectangle 1.
|
|
2492
|
+
x_right1 : float, optional
|
|
2493
|
+
The coordinate (signed) of the maximum (right) along the X axis of rectangle 1.
|
|
2494
|
+
y_bottom1 : float, optional
|
|
2495
|
+
The coordinate (signed) of the minimum (left) along the Y axis of rectangle 1.
|
|
2496
|
+
y_top1 : float, optional
|
|
2497
|
+
The coordinate (signed) of the maximum (right) along the Y axis of rectangle 1.
|
|
2498
|
+
x_left2 : float, optional
|
|
2499
|
+
The coordinate (signed) of the minimum (left) along the X axis of rectangle 2.
|
|
2500
|
+
x_right2 : float, optional
|
|
2501
|
+
The coordinate (signed) of the maximum (right) along the X axis of rectangle 2.
|
|
2502
|
+
y_bottom2 : float, optional
|
|
2503
|
+
The coordinate (signed) of the minimum (left) along the Y axis of rectangle 2.
|
|
2504
|
+
y_top2 : float, optional
|
|
2505
|
+
The coordinate (signed) of the maximum (right) along the Y axis of rectangle 2.
|
|
2506
|
+
"""
|
|
2507
|
+
def __init__(self, x_left1=-0.010, x_right1=0.0, y_bottom1=-0.020, y_top1=0.0,
|
|
2508
|
+
x_left2=-0.010, x_right2=0.010, y_bottom2=-0.001, y_top2=0.020):
|
|
2509
|
+
super().__init__()
|
|
2510
|
+
self.reset()
|
|
2511
|
+
self.append_patch(Rectangle(x_left=x_left1, x_right=x_right1, y_bottom=y_bottom1, y_top=y_top1))
|
|
2512
|
+
self.append_patch(Rectangle(x_left=x_left2, x_right=x_right2, y_bottom=y_bottom2, y_top=y_top2))
|
|
2513
|
+
|
|
2514
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
2515
|
+
self._set_support_text([
|
|
2516
|
+
("x_left1" , "x (width) minimum (signed) ", "m" ),
|
|
2517
|
+
("x_right1" , "x (width) maximum (signed) ", "m" ),
|
|
2518
|
+
("y_bottom1" , "y (length) minimum (signed) ", "m" ),
|
|
2519
|
+
("y_top1" , "y (length) maximum (signed) ", "m" ),
|
|
2520
|
+
("x_left2" , "x (width) minimum (signed) ", "m" ),
|
|
2521
|
+
("x_right2" , "x (width) maximum (signed) ", "m" ),
|
|
2522
|
+
("y_bottom2" , "y (length) minimum (signed) ", "m" ),
|
|
2523
|
+
("y_top2" , "y (length) maximum (signed) ", "m" ),
|
|
2524
|
+
] )
|
|
2525
|
+
|
|
2526
|
+
def set_boundaries(self,x_left1=-0.010, x_right1=0.0, y_bottom1=-0.020, y_top1=0.0,
|
|
2527
|
+
x_left2=-0.010, x_right2=0.010, y_bottom2=-0.001, y_top2=0.020):
|
|
2528
|
+
self._patch_list[0].set_boundaries(x_left1, x_right1, y_bottom1, y_top1)
|
|
2529
|
+
self._patch_list[1].set_boundaries(x_left2, x_right2, y_bottom2, y_top2)
|
|
2530
|
+
|
|
2531
|
+
class DoubleEllipse(MultiplePatch):
|
|
2532
|
+
"""
|
|
2533
|
+
Constructor.
|
|
2534
|
+
|
|
2535
|
+
Parameters
|
|
2536
|
+
----------
|
|
2537
|
+
a_axis_min1 : float, optional
|
|
2538
|
+
The coordinate (signed) of the minimum (left) along the major axis of ellipse 1.
|
|
2539
|
+
a_axis_max1 : float, optional
|
|
2540
|
+
The coordinate (signed) of the maximum (right) along the major axis of ellipse 1.
|
|
2541
|
+
b_axis_min1 : float, optional
|
|
2542
|
+
The coordinate (signed) of the minimum (left) along the minor axis of ellipse 1.
|
|
2543
|
+
b_axis_max1 : float, optional
|
|
2544
|
+
The coordinate (signed) of the maximum (right) along the minor axis of ellipse 1.
|
|
2545
|
+
a_axis_min2 : float, optional
|
|
2546
|
+
The coordinate (signed) of the minimum (left) along the major axis of ellipse 2.
|
|
2547
|
+
a_axis_max2 : float, optional
|
|
2548
|
+
The coordinate (signed) of the maximum (right) along the major axis of ellipse 2.
|
|
2549
|
+
b_axis_min2 : float, optional
|
|
2550
|
+
The coordinate (signed) of the minimum (left) along the minor axis of ellipse 2.
|
|
2551
|
+
b_axis_max2 : float, optional
|
|
2552
|
+
The coordinate (signed) of the maximum (right) along the minor axis of ellipse 2.
|
|
2553
|
+
|
|
2554
|
+
"""
|
|
2555
|
+
def __init__(self, a_axis_min1=-0.010, a_axis_max1=0.0, b_axis_min1=-0.020, b_axis_max1=0.0,
|
|
2556
|
+
a_axis_min2=-0.010, a_axis_max2=0.010, b_axis_min2=-0.001, b_axis_max2=0.020):
|
|
2557
|
+
|
|
2558
|
+
super().__init__()
|
|
2559
|
+
self.reset()
|
|
2560
|
+
self.append_patch(Ellipse(a_axis_min1, a_axis_max1, b_axis_min1, b_axis_max1))
|
|
2561
|
+
self.append_patch(Ellipse(a_axis_min2, a_axis_max2, b_axis_min2, b_axis_max2))
|
|
2562
|
+
self._set_support_text([
|
|
2563
|
+
("a_axis_min1" , "x (width) axis starts (signed) ", "m" ),
|
|
2564
|
+
("a_axis_max1" , "x (width) axis ends (signed) ", "m" ),
|
|
2565
|
+
("b_axis_min1" , "y (length) axis starts (signed) ", "m" ),
|
|
2566
|
+
("b_axis_max1" , "y (length) axis ends (signed) ", "m" ),
|
|
2567
|
+
("a_axis_min2" , "x (width) axis starts (signed) ", "m" ),
|
|
2568
|
+
("a_axis_max2" , "x (width) axis ends (signed) ", "m" ),
|
|
2569
|
+
("b_axis_min2" , "y (length) axis starts (signed) ", "m" ),
|
|
2570
|
+
("b_axis_max2" , "y (length) axis ends (signed) ", "m" ),
|
|
2571
|
+
] )
|
|
2572
|
+
def set_boundaries(self,a_axis_min1=-0.010, a_axis_max1=0.0, b_axis_min1=-0.020, b_axis_max1=0.0,
|
|
2573
|
+
a_axis_min2=-0.010, a_axis_max2=0.010, b_axis_min2=-0.001, b_axis_max2=0.020):
|
|
2574
|
+
"""
|
|
2575
|
+
Sets the coordinates of the ellipses.
|
|
2576
|
+
|
|
2577
|
+
Parameters
|
|
2578
|
+
----------
|
|
2579
|
+
a_axis_min1 : float, optional
|
|
2580
|
+
The coordinate (signed) of the minimum (left) along the major axis of ellipse 1.
|
|
2581
|
+
a_axis_max1 : float, optional
|
|
2582
|
+
The coordinate (signed) of the maximum (right) along the major axis of ellipse 1.
|
|
2583
|
+
b_axis_min1 : float, optional
|
|
2584
|
+
The coordinate (signed) of the minimum (left) along the minor axis of ellipse 1.
|
|
2585
|
+
b_axis_max1 : float, optional
|
|
2586
|
+
The coordinate (signed) of the maximum (right) along the minor axis of ellipse 1.
|
|
2587
|
+
a_axis_min2 : float, optional
|
|
2588
|
+
The coordinate (signed) of the minimum (left) along the major axis of ellipse 2.
|
|
2589
|
+
a_axis_max2 : float, optional
|
|
2590
|
+
The coordinate (signed) of the maximum (right) along the major axis of ellipse 2.
|
|
2591
|
+
b_axis_min2 : float, optional
|
|
2592
|
+
The coordinate (signed) of the minimum (left) along the minor axis of ellipse 2.
|
|
2593
|
+
b_axis_max2 : float, optional
|
|
2594
|
+
The coordinate (signed) of the maximum (right) along the minor axis of ellipse 2.
|
|
2595
|
+
|
|
2596
|
+
"""
|
|
2597
|
+
self._patch_list[0].set_boundaries(a_axis_min1,a_axis_max1,b_axis_min1,b_axis_max1)
|
|
2598
|
+
self._patch_list[1].set_boundaries(a_axis_min2,a_axis_max2,b_axis_min2,b_axis_max2)
|
|
2599
|
+
|
|
2600
|
+
class DoubleCircle(MultiplePatch):
|
|
2601
|
+
"""
|
|
2602
|
+
Constructor.
|
|
2603
|
+
|
|
2604
|
+
Parameters
|
|
2605
|
+
----------
|
|
2606
|
+
radius1 : float
|
|
2607
|
+
The radius of the circle 1.
|
|
2608
|
+
x_center1 : float
|
|
2609
|
+
The x coordinate of the center of the circle 1.
|
|
2610
|
+
y_center1 : float
|
|
2611
|
+
The y coordinate of the center of the circle 1.
|
|
2612
|
+
radius2 : float
|
|
2613
|
+
The radius of the circle 2.
|
|
2614
|
+
x_center2 : float
|
|
2615
|
+
The x coordinate of the center of the circle 2.
|
|
2616
|
+
y_center2 : float
|
|
2617
|
+
The y coordinate of the center of the circle 2.
|
|
2618
|
+
|
|
2619
|
+
"""
|
|
2620
|
+
def __init__(self, radius1=50e-6,x_center1=0.0,y_center1=0.0,
|
|
2621
|
+
radius2=50e-6,x_center2=100e-6,y_center2=100e-6):
|
|
2622
|
+
super().__init__()
|
|
2623
|
+
self.reset()
|
|
2624
|
+
self.append_patch(Circle(radius1,x_center1,y_center1))
|
|
2625
|
+
self.append_patch(Circle(radius2,x_center2,y_center2))
|
|
2626
|
+
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
|
|
2627
|
+
self._set_support_text([
|
|
2628
|
+
("radius1" , "radius ", "m" ),
|
|
2629
|
+
("x_center1" , "x center (signed) ", "m" ),
|
|
2630
|
+
("y_center1" , "y center (signed) ", "m" ),
|
|
2631
|
+
("radius2" , "radius ", "m" ),
|
|
2632
|
+
("x_center2" , "x center (signed) ", "m" ),
|
|
2633
|
+
("y_center2" , "y center (signed) ", "m" ),
|
|
2634
|
+
] )
|
|
2635
|
+
def set_boundaries(self,radius1=50e-6,x_center1=0.0,y_center1=0.0,
|
|
2636
|
+
radius2=50e-6,x_center2=100e-6,y_center2=100e-6):
|
|
2637
|
+
"""
|
|
2638
|
+
Sets the coordinates of the circles.
|
|
2639
|
+
|
|
2640
|
+
Parameters
|
|
2641
|
+
----------
|
|
2642
|
+
radius1 : float
|
|
2643
|
+
The radius of the circle 1.
|
|
2644
|
+
x_center1 : float
|
|
2645
|
+
The x coordinate of the center of the circle 1.
|
|
2646
|
+
y_center1 : float
|
|
2647
|
+
The y coordinate of the center of the circle 1.
|
|
2648
|
+
radius2 : float
|
|
2649
|
+
The radius of the circle 2.
|
|
2650
|
+
x_center2 : float
|
|
2651
|
+
The x coordinate of the center of the circle 2.
|
|
2652
|
+
y_center2 : float
|
|
2653
|
+
The y coordinate of the center of the circle 2.
|
|
2654
|
+
|
|
2655
|
+
"""
|
|
2656
|
+
self._patch_list[0].set_boundaries(radius1,x_center1,y_center1)
|
|
2657
|
+
self._patch_list[1].set_boundaries(radius2,x_center2,y_center2)
|
|
2658
|
+
|
|
2659
|
+
|
|
2660
|
+
|
|
2661
|
+
if __name__=="__main__":
|
|
2662
|
+
|
|
2663
|
+
|
|
2664
|
+
|
|
2665
|
+
p = 20
|
|
2666
|
+
q = 10
|
|
2667
|
+
theta_graz = 0.003
|
|
2668
|
+
|
|
2669
|
+
#
|
|
2670
|
+
# sphere
|
|
2671
|
+
#
|
|
2672
|
+
# sph = Sphere()
|
|
2673
|
+
sph = Sphere.create_sphere_from_p_q(10, 10, 0.021)
|
|
2674
|
+
print(sph.info())
|
|
2675
|
+
|
|
2676
|
+
#
|
|
2677
|
+
# Ellipsoid
|
|
2678
|
+
#
|
|
2679
|
+
ell = Ellipsoid()
|
|
2680
|
+
ell.initialize_from_p_q(p, q, theta_graz)
|
|
2681
|
+
|
|
2682
|
+
|
|
2683
|
+
#
|
|
2684
|
+
# toroid
|
|
2685
|
+
#
|
|
2686
|
+
par = Toroid.create_toroid_from_p_q(p=p, q=q, grazing_angle=theta_graz)
|
|
2687
|
+
print("inputs p, q, theta_graz: ", p, q, theta_graz)
|
|
2688
|
+
radii = par.get_radii()
|
|
2689
|
+
print("toroid radii: ", radii )
|
|
2690
|
+
R = 2 / numpy.sin(theta_graz) * p * q / (p + q)
|
|
2691
|
+
r = 2 * numpy.sin(theta_graz) * p * q / (p + q)
|
|
2692
|
+
assert ((radii[0] - R) < 1e-10 )
|
|
2693
|
+
assert ((radii[0] - r) < 1e-10 )
|
|
2694
|
+
print(par.info())
|
|
2695
|
+
|
|
2696
|
+
#
|
|
2697
|
+
# paraboloid
|
|
2698
|
+
#
|
|
2699
|
+
at_infinity = Side.SOURCE
|
|
2700
|
+
|
|
2701
|
+
par = Paraboloid.create_paraboloid_from_p_q(p=p, q=q, grazing_angle=theta_graz, at_infinity=at_infinity, convexity=Convexity.UPWARD)
|
|
2702
|
+
print("inputs p, q, theta_graz: ", p, q, theta_graz, at_infinity)
|
|
2703
|
+
print ("parabola p or q: ",par.get_pole_to_focus())
|
|
2704
|
+
print("parabola par: ", par.get_parabola_parameter())
|
|
2705
|
+
print("parabola grazing_angle: ", par.get_grazing_angle())
|
|
2706
|
+
if par.get_at_infinity() == Side.SOURCE:
|
|
2707
|
+
assert (numpy.abs(q - par.get_pole_to_focus()) < 1e-10 )
|
|
2708
|
+
else:
|
|
2709
|
+
assert (numpy.abs(p - par.get_pole_to_focus()) < 1e-10)
|
|
2710
|
+
assert (numpy.abs(theta_graz - par.get_grazing_angle()) < 1e-10)
|
|
2711
|
+
print(par.info())
|
|
2712
|
+
|
|
2713
|
+
#
|
|
2714
|
+
# parabolic cylinder: TODO: check that the info is not good for double inheritage
|
|
2715
|
+
#
|
|
2716
|
+
a = Cylinder()
|
|
2717
|
+
print(a.info())
|
|
2718
|
+
print(a.to_dictionary())
|
|
2719
|
+
|
|
2720
|
+
parC = ParabolicCylinder(par, a)
|
|
2721
|
+
print(parC.info())
|
|
2722
|
+
|
|
2723
|
+
|
|
2724
|
+
|
|
2725
|
+
#
|
|
2726
|
+
# some other checks...
|
|
2727
|
+
#
|
|
2728
|
+
|
|
2729
|
+
# conic coeffs.
|
|
2730
|
+
ccc = Conic()
|
|
2731
|
+
print(ccc.get_conic_coefficients())
|
|
2732
|
+
print(ccc.info())
|
|
2733
|
+
ccc.to_json("tmp.json")
|
|
2734
|
+
from syned.util.json_tools import load_from_json_file
|
|
2735
|
+
tmp = load_from_json_file("tmp.json")
|
|
2736
|
+
print("returned class: ",type(tmp))
|
|
2737
|
+
print(ccc.to_dictionary())
|
|
2738
|
+
print(tmp.to_dictionary())
|
|
2739
|
+
# from deepdiff import DeepDiff # use this because == gives an error
|
|
2740
|
+
# assert (len(DeepDiff(ccc.to_dictionary(), tmp.to_dictionary())) == 0)
|
|
2741
|
+
|
|
2742
|
+
|
|
2743
|
+
|
|
2744
|
+
|
|
2745
|
+
# circle
|
|
2746
|
+
circle = Circle(3.0)
|
|
2747
|
+
print(circle.get_radius(),circle.get_center())
|
|
2748
|
+
print(circle.get_boundaries())
|
|
2749
|
+
|
|
2750
|
+
|
|
2751
|
+
|
|
2752
|
+
# patches
|
|
2753
|
+
patches = MultiplePatch()
|
|
2754
|
+
|
|
2755
|
+
patches.append_rectangle(-0.02,-0.01,-0.001,0.001)
|
|
2756
|
+
patches.append_rectangle(0.01,0.02,-0.001,0.001)
|
|
2757
|
+
patches.append_polygon([-0.02,-0.02,0.02,0.02], [-0.02,0.02,0.02,-0.02])
|
|
2758
|
+
|
|
2759
|
+
print(patches.get_number_of_patches(),patches.get_boundaries())
|
|
2760
|
+
for patch in patches.get_patches():
|
|
2761
|
+
print(patch.info())
|
|
2762
|
+
print("Patch 0 is: ",patches.get_name_of_patch(0))
|
|
2763
|
+
print("Patch 1 is: ",patches.get_name_of_patch(1))
|
|
2764
|
+
print(patches.get_boundaries())
|
|
2765
|
+
|
|
2766
|
+
|
|
2767
|
+
# double rectangle
|
|
2768
|
+
double_rectangle = DoubleRectangle()
|
|
2769
|
+
double_rectangle.set_boundaries(-0.02,-0.01,-0.001,0.001,0.01,0.02,-0.001,0.001)
|
|
2770
|
+
print("Rectangle 0 is: ",double_rectangle.get_name_of_patch(0))
|
|
2771
|
+
print("Rectangle 1 is: ",double_rectangle.get_name_of_patch(1))
|
|
2772
|
+
print(double_rectangle.get_boundaries())
|
|
2773
|
+
|
|
2774
|
+
# polygon
|
|
2775
|
+
angle = numpy.linspace(0, 2 * numpy.pi, 5)
|
|
2776
|
+
x = numpy.sin(angle) + 0.5
|
|
2777
|
+
y = numpy.cos(angle) + 0.5
|
|
2778
|
+
poly = Polygon(x=x, y=y)
|
|
2779
|
+
print(poly.info())
|
|
2780
|
+
print("vertices: ", poly.get_number_of_vertices())
|
|
2781
|
+
if False:
|
|
2782
|
+
from srxraylib.plot.gol import plot,set_qt
|
|
2783
|
+
set_qt()
|
|
2784
|
+
plot(x,y)
|
|
2785
|
+
print(poly.get_polygon())
|
|
2786
|
+
print("inside? : ", poly.check_inside([0.5,0],[0.5,5]))
|
|
2787
|
+
print("outside? : ", poly.check_outside([0.5, 0], [0.5, 5]))
|
|
2788
|
+
|
|
2789
|
+
|
|
2790
|
+
# multiple patches
|
|
2791
|
+
patches = MultiplePatch()
|
|
2792
|
+
patches.append_polygon(numpy.array([-1,-1,1,1]),numpy.array([-1,1,1,-1]))
|
|
2793
|
+
x = [-0.00166557, 0.12180897, -0.11252591, -0.12274196, 0.00586896, -0.12999401, -0.12552975, -0.0377907, -0.01094828, -0.13689862]
|
|
2794
|
+
y = [ 0.16279557, -0.00085991, 0.01349174, -0.01371226, 0.01480265, -0.04810334, 0.07198068, -0.03725407, 0.13301309, -0.00296213]
|
|
2795
|
+
x = numpy.array(x)
|
|
2796
|
+
y = numpy.array(y)
|
|
2797
|
+
patch = patches.get_patch(0)
|
|
2798
|
+
# print(patch.check_inside(x,y))
|
|
2799
|
+
for i in range(x.size):
|
|
2800
|
+
tmp = patch.check_inside_one_point(x[i], y[i])
|
|
2801
|
+
print(x[i], y[i], tmp )
|
|
2802
|
+
print("inside? : ", patch.check_inside(x, y), type(patch.check_inside(x, y)))
|
|
2803
|
+
print("inside? : ", patch.check_inside_vector(x, y), type(patch.check_inside_vector(x, y)))
|