prefab 0.4.7__py3-none-any.whl → 1.1.8__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.
- prefab/__init__.py +15 -38
- prefab/__main__.py +95 -0
- prefab/compare.py +126 -0
- prefab/device.py +1486 -0
- prefab/geometry.py +394 -0
- prefab/models.py +114 -0
- prefab/predict.py +337 -0
- prefab/read.py +503 -0
- prefab/shapes.py +773 -0
- {prefab-0.4.7.dist-info → prefab-1.1.8.dist-info}/METADATA +47 -34
- prefab-1.1.8.dist-info/RECORD +14 -0
- {prefab-0.4.7.dist-info → prefab-1.1.8.dist-info}/WHEEL +1 -1
- prefab-1.1.8.dist-info/entry_points.txt +2 -0
- prefab/io.py +0 -200
- prefab/predictor.py +0 -161
- prefab/processor.py +0 -248
- prefab-0.4.7.dist-info/RECORD +0 -8
- {prefab-0.4.7.dist-info → prefab-1.1.8.dist-info}/licenses/LICENSE +0 -0
prefab/shapes.py
ADDED
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
"""Contains functions for creating various shapes as Device objects."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from skimage.draw import polygon
|
|
7
|
+
|
|
8
|
+
from .device import Device
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def rectangle(width: int = 200, height: Optional[int] = None, **kwargs) -> Device:
|
|
12
|
+
"""
|
|
13
|
+
Create a Device object with a rectangular shape.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
width : int
|
|
18
|
+
The width of the rectangle. Defaults to 200.
|
|
19
|
+
height : Optional[int]
|
|
20
|
+
The height of the rectangle. Defaults to the value of width if None.
|
|
21
|
+
**kwargs : dict
|
|
22
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
Device
|
|
27
|
+
A Device object containing the rectangular shape.
|
|
28
|
+
"""
|
|
29
|
+
if height is None:
|
|
30
|
+
height = width
|
|
31
|
+
rectangle = np.ones((height, width))
|
|
32
|
+
return Device(device_array=rectangle, **kwargs)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def window(
|
|
36
|
+
width: int = 200, height: Optional[int] = None, border_width: int = 60, **kwargs
|
|
37
|
+
) -> Device:
|
|
38
|
+
"""
|
|
39
|
+
Create a Device object with a window shape (hollow rectangle).
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
width : int
|
|
44
|
+
The overall width of the window. Defaults to 200.
|
|
45
|
+
height : Optional[int]
|
|
46
|
+
The overall height of the window. Defaults to the value of width.
|
|
47
|
+
border_width : int
|
|
48
|
+
The width of the window border. Defaults to 60.
|
|
49
|
+
**kwargs : dict
|
|
50
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
Device
|
|
55
|
+
A Device object containing the window shape.
|
|
56
|
+
"""
|
|
57
|
+
if height is None:
|
|
58
|
+
height = width
|
|
59
|
+
window = np.zeros((height, width))
|
|
60
|
+
window[:border_width, :] = 1
|
|
61
|
+
window[-border_width:, :] = 1
|
|
62
|
+
window[:, :border_width] = 1
|
|
63
|
+
window[:, -border_width:] = 1
|
|
64
|
+
return Device(device_array=window, **kwargs)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def cross(
|
|
68
|
+
width: int = 200, height: Optional[int] = None, arm_width: int = 60, **kwargs
|
|
69
|
+
) -> Device:
|
|
70
|
+
"""
|
|
71
|
+
Create a Device object with a cross shape.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
width : int
|
|
76
|
+
The overall width of the cross. Defaults to 200.
|
|
77
|
+
height : Optional[int]
|
|
78
|
+
The overall height of the cross. Defaults to the value of width.
|
|
79
|
+
arm_width : int
|
|
80
|
+
The width of the cross arms. Defaults to 60.
|
|
81
|
+
**kwargs : dict
|
|
82
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
Device
|
|
87
|
+
A Device object containing the cross shape.
|
|
88
|
+
"""
|
|
89
|
+
if height is None:
|
|
90
|
+
height = width
|
|
91
|
+
cross = np.zeros((height, width))
|
|
92
|
+
center_x = width // 2
|
|
93
|
+
center_y = height // 2
|
|
94
|
+
half_arm_width = arm_width // 2
|
|
95
|
+
cross[center_y - half_arm_width : center_y + half_arm_width + 1, :] = 1
|
|
96
|
+
cross[:, center_x - half_arm_width : center_x + half_arm_width + 1] = 1
|
|
97
|
+
return Device(device_array=cross, **kwargs)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def target(
|
|
101
|
+
width: int = 200, height: Optional[int] = None, arm_width: int = 60, **kwargs
|
|
102
|
+
) -> Device:
|
|
103
|
+
"""
|
|
104
|
+
Create a Device object with a target shape (cross with center removed).
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
width : int
|
|
109
|
+
The overall width of the target. Defaults to 200.
|
|
110
|
+
height : Optional[int]
|
|
111
|
+
The overall height of the target. Defaults to the value of width.
|
|
112
|
+
arm_width : int
|
|
113
|
+
The width of the target arms. Defaults to 60.
|
|
114
|
+
**kwargs : dict
|
|
115
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
Device
|
|
120
|
+
A Device object containing the target shape.
|
|
121
|
+
"""
|
|
122
|
+
if height is None:
|
|
123
|
+
height = width
|
|
124
|
+
target = np.zeros((height, width))
|
|
125
|
+
center_x = width // 2
|
|
126
|
+
center_y = height // 2
|
|
127
|
+
half_arm_width = arm_width // 2
|
|
128
|
+
target[center_y - half_arm_width : center_y + half_arm_width + 1, :] = 1
|
|
129
|
+
target[:, center_x - half_arm_width : center_x + half_arm_width + 1] = 1
|
|
130
|
+
target[
|
|
131
|
+
center_y - half_arm_width : center_y + half_arm_width + 1,
|
|
132
|
+
center_x - half_arm_width : center_x + half_arm_width + 1,
|
|
133
|
+
] = 0
|
|
134
|
+
return Device(device_array=target, **kwargs)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def disk(width: int = 200, height: Optional[int] = None, **kwargs) -> Device:
|
|
138
|
+
"""
|
|
139
|
+
Create a Device object with an elliptical shape.
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
width : int
|
|
144
|
+
The width of the ellipse. Defaults to 200.
|
|
145
|
+
height : Optional[int]
|
|
146
|
+
The height of the ellipse. Defaults to the value of width.
|
|
147
|
+
**kwargs : dict
|
|
148
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
149
|
+
|
|
150
|
+
Returns
|
|
151
|
+
-------
|
|
152
|
+
Device
|
|
153
|
+
A Device object containing the elliptical shape.
|
|
154
|
+
"""
|
|
155
|
+
if height is None:
|
|
156
|
+
height = width
|
|
157
|
+
radius_x = width // 2
|
|
158
|
+
radius_y = height // 2
|
|
159
|
+
y, x = np.ogrid[-radius_y:radius_y, -radius_x:radius_x]
|
|
160
|
+
mask = (x**2 / radius_x**2) + (y**2 / radius_y**2) <= 1
|
|
161
|
+
ellipse = np.zeros((height, width))
|
|
162
|
+
ellipse[mask] = 1
|
|
163
|
+
return Device(device_array=ellipse, **kwargs)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def ring(
|
|
167
|
+
width: int = 200, height: Optional[int] = None, border_width: int = 60, **kwargs
|
|
168
|
+
) -> Device:
|
|
169
|
+
"""
|
|
170
|
+
Create a Device object with a ring shape (hollow ellipse).
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
width : int
|
|
175
|
+
The overall width of the ring. Defaults to 200.
|
|
176
|
+
height : Optional[int]
|
|
177
|
+
The overall height of the ring. Defaults to the value of width.
|
|
178
|
+
border_width : int
|
|
179
|
+
The width of the ring border. Defaults to 60.
|
|
180
|
+
**kwargs : dict
|
|
181
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
Device
|
|
186
|
+
A Device object containing the ring shape.
|
|
187
|
+
"""
|
|
188
|
+
if height is None:
|
|
189
|
+
height = width
|
|
190
|
+
radius_x = width // 2
|
|
191
|
+
radius_y = height // 2
|
|
192
|
+
inner_radius_x = radius_x - border_width
|
|
193
|
+
inner_radius_y = radius_y - border_width
|
|
194
|
+
y, x = np.ogrid[-radius_y:radius_y, -radius_x:radius_x]
|
|
195
|
+
outer_mask = x**2 / radius_x**2 + y**2 / radius_y**2 <= 1
|
|
196
|
+
inner_mask = x**2 / inner_radius_x**2 + y**2 / inner_radius_y**2 <= 1
|
|
197
|
+
ring = np.zeros((height, width))
|
|
198
|
+
ring[outer_mask & ~inner_mask] = 1
|
|
199
|
+
return Device(device_array=ring, **kwargs)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def disk_wavy(
|
|
203
|
+
width: int = 200, wave_amplitude: float = 10, wave_frequency: float = 10, **kwargs
|
|
204
|
+
) -> Device:
|
|
205
|
+
"""
|
|
206
|
+
Create a Device object with a circular shape with wavy edges.
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
width : int
|
|
211
|
+
The overall width and height of the wavy circle. Defaults to 200.
|
|
212
|
+
wave_amplitude : float
|
|
213
|
+
The amplitude of the waves. Defaults to 10.
|
|
214
|
+
wave_frequency : float
|
|
215
|
+
The frequency of the waves. Defaults to 10.
|
|
216
|
+
**kwargs : dict
|
|
217
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
Device
|
|
222
|
+
A Device object containing the wavy circular shape.
|
|
223
|
+
"""
|
|
224
|
+
effective_radius = (width // 2) - wave_amplitude
|
|
225
|
+
y, x = np.ogrid[-width // 2 : width // 2, -width // 2 : width // 2]
|
|
226
|
+
distance_from_center = np.sqrt(x**2 + y**2)
|
|
227
|
+
sinusoidal_boundary = effective_radius + wave_amplitude * np.sin(
|
|
228
|
+
wave_frequency * np.arctan2(y, x)
|
|
229
|
+
)
|
|
230
|
+
mask = distance_from_center <= sinusoidal_boundary
|
|
231
|
+
circle_wavy = np.zeros((width, width))
|
|
232
|
+
circle_wavy[mask] = 1
|
|
233
|
+
return Device(device_array=circle_wavy, **kwargs)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def pie(
|
|
237
|
+
width: int = 200, height: Optional[int] = None, arc_angle: float = 270, **kwargs
|
|
238
|
+
) -> Device:
|
|
239
|
+
"""
|
|
240
|
+
Create a Device object with a pie shape.
|
|
241
|
+
|
|
242
|
+
Parameters
|
|
243
|
+
----------
|
|
244
|
+
width : int
|
|
245
|
+
The width of the pie. Defaults to 200.
|
|
246
|
+
height : Optional[int]
|
|
247
|
+
The height of the pie. Defaults to the value of width.
|
|
248
|
+
arc_angle : float
|
|
249
|
+
The angle of the pie slice in degrees. Defaults to 270.
|
|
250
|
+
**kwargs : dict
|
|
251
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
252
|
+
|
|
253
|
+
Returns
|
|
254
|
+
-------
|
|
255
|
+
Device
|
|
256
|
+
A Device object containing the pie shape.
|
|
257
|
+
"""
|
|
258
|
+
if height is None:
|
|
259
|
+
height = width
|
|
260
|
+
radius_x = width // 2
|
|
261
|
+
radius_y = height // 2
|
|
262
|
+
y, x = np.ogrid[-radius_y:radius_y, -radius_x:radius_x]
|
|
263
|
+
angle = np.arctan2(y, x) * 180 / np.pi
|
|
264
|
+
angle = (angle + 360) % 360
|
|
265
|
+
mask = (x**2 / radius_x**2 + y**2 / radius_y**2 <= 1) & (angle <= arc_angle)
|
|
266
|
+
pie = np.zeros((height, width))
|
|
267
|
+
pie[mask] = 1
|
|
268
|
+
return Device(device_array=pie, **kwargs)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def grating(
|
|
272
|
+
height: int = 200,
|
|
273
|
+
pitch: int = 120,
|
|
274
|
+
duty_cycle: float = 0.5,
|
|
275
|
+
num_gratings: int = 3,
|
|
276
|
+
**kwargs,
|
|
277
|
+
) -> Device:
|
|
278
|
+
"""
|
|
279
|
+
Create a Device object with a grating pattern.
|
|
280
|
+
|
|
281
|
+
Parameters
|
|
282
|
+
----------
|
|
283
|
+
height : int
|
|
284
|
+
The height of the grating. Defaults to 200.
|
|
285
|
+
pitch : int
|
|
286
|
+
The pitch (period) of the grating. Defaults to 120.
|
|
287
|
+
duty_cycle : float
|
|
288
|
+
The duty cycle of the grating. Defaults to 0.5.
|
|
289
|
+
num_gratings : int
|
|
290
|
+
The number of grating periods. Defaults to 3.
|
|
291
|
+
**kwargs : dict
|
|
292
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
293
|
+
|
|
294
|
+
Returns
|
|
295
|
+
-------
|
|
296
|
+
Device
|
|
297
|
+
A Device object containing the grating pattern.
|
|
298
|
+
"""
|
|
299
|
+
width = pitch * num_gratings - pitch // 2
|
|
300
|
+
grating = np.zeros((height, width))
|
|
301
|
+
grating_width = int(pitch * duty_cycle)
|
|
302
|
+
for i in range(num_gratings):
|
|
303
|
+
start = i * pitch
|
|
304
|
+
grating[:, start : start + grating_width] = 1
|
|
305
|
+
return Device(device_array=grating, **kwargs)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def star(width: int = 200, num_points: int = 5, **kwargs) -> Device:
|
|
309
|
+
"""
|
|
310
|
+
Create a Device object with a star shape.
|
|
311
|
+
|
|
312
|
+
Parameters
|
|
313
|
+
----------
|
|
314
|
+
width : int
|
|
315
|
+
The overall width and height of the star. Defaults to 200.
|
|
316
|
+
num_points : int
|
|
317
|
+
The number of points on the star. Defaults to 5.
|
|
318
|
+
**kwargs : dict
|
|
319
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
320
|
+
|
|
321
|
+
Returns
|
|
322
|
+
-------
|
|
323
|
+
Device
|
|
324
|
+
A Device object containing the star shape.
|
|
325
|
+
"""
|
|
326
|
+
radius_outer = width // 2
|
|
327
|
+
radius_inner = radius_outer // 2
|
|
328
|
+
angles_outer = np.linspace(0, 2 * np.pi, num_points, endpoint=False) - np.pi / 2
|
|
329
|
+
angles_inner = angles_outer + np.pi / num_points
|
|
330
|
+
x_outer = (radius_outer * np.cos(angles_outer) + radius_outer).astype(int)
|
|
331
|
+
y_outer = (radius_outer * np.sin(angles_outer) + radius_outer).astype(int)
|
|
332
|
+
x_inner = (radius_inner * np.cos(angles_inner) + radius_outer).astype(int)
|
|
333
|
+
y_inner = (radius_inner * np.sin(angles_inner) + radius_outer).astype(int)
|
|
334
|
+
x = np.empty(2 * num_points, dtype=int)
|
|
335
|
+
y = np.empty(2 * num_points, dtype=int)
|
|
336
|
+
x[0::2] = x_outer
|
|
337
|
+
x[1::2] = x_inner
|
|
338
|
+
y[0::2] = y_outer
|
|
339
|
+
y[1::2] = y_inner
|
|
340
|
+
star = np.zeros((width, width))
|
|
341
|
+
rr, cc = polygon(y, x)
|
|
342
|
+
rr = np.clip(rr, 0, width - 1)
|
|
343
|
+
cc = np.clip(cc, 0, width - 1)
|
|
344
|
+
star[rr, cc] = 1
|
|
345
|
+
return Device(device_array=star, **kwargs)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def poly(width: int = 200, num_points: int = 5, **kwargs) -> Device:
|
|
349
|
+
"""
|
|
350
|
+
Create a Device object with a regular polygon shape.
|
|
351
|
+
|
|
352
|
+
Parameters
|
|
353
|
+
----------
|
|
354
|
+
width : int
|
|
355
|
+
The overall width and height of the polygon. Defaults to 200.
|
|
356
|
+
num_points : int
|
|
357
|
+
The number of sides of the polygon. Defaults to 5.
|
|
358
|
+
**kwargs : dict
|
|
359
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
360
|
+
|
|
361
|
+
Returns
|
|
362
|
+
-------
|
|
363
|
+
Device
|
|
364
|
+
A Device object containing the regular polygon shape.
|
|
365
|
+
"""
|
|
366
|
+
radius = width // 2
|
|
367
|
+
angles = np.linspace(0, 2 * np.pi, num_points, endpoint=False) - np.pi / 2
|
|
368
|
+
x = (radius * np.cos(angles) + radius).astype(int)
|
|
369
|
+
y = (radius * np.sin(angles) + radius).astype(int)
|
|
370
|
+
poly = np.zeros((width, width))
|
|
371
|
+
rr, cc = polygon(y, x)
|
|
372
|
+
rr = np.clip(rr, 0, width - 1)
|
|
373
|
+
cc = np.clip(cc, 0, width - 1)
|
|
374
|
+
poly[rr, cc] = 1
|
|
375
|
+
return Device(device_array=poly, **kwargs)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def radial_grating(
|
|
379
|
+
width: int = 200, grating_skew: int = 0, num_gratings: int = 6, **kwargs
|
|
380
|
+
) -> Device:
|
|
381
|
+
"""
|
|
382
|
+
Create a Device object with a radial grating pattern.
|
|
383
|
+
|
|
384
|
+
Parameters
|
|
385
|
+
----------
|
|
386
|
+
width : int
|
|
387
|
+
The overall width and height of the radial grating. Defaults to 200.
|
|
388
|
+
grating_skew : int
|
|
389
|
+
The skew angle of the grating arms. Defaults to 0.
|
|
390
|
+
num_gratings : int
|
|
391
|
+
The number of grating arms. Defaults to 6.
|
|
392
|
+
**kwargs : dict
|
|
393
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
394
|
+
|
|
395
|
+
Returns
|
|
396
|
+
-------
|
|
397
|
+
Device
|
|
398
|
+
A Device object containing the radial grating pattern.
|
|
399
|
+
"""
|
|
400
|
+
radial_grating = np.zeros((width, width))
|
|
401
|
+
center = width // 2
|
|
402
|
+
radius = center
|
|
403
|
+
theta = np.linspace(0, 2 * np.pi, num_gratings, endpoint=False)
|
|
404
|
+
for angle in theta:
|
|
405
|
+
x0, y0 = center, center
|
|
406
|
+
x1 = int(center + radius * np.cos(angle))
|
|
407
|
+
y1 = int(center + radius * np.sin(angle))
|
|
408
|
+
x2 = int(
|
|
409
|
+
center + (radius - grating_skew) * np.cos(angle + np.pi / num_gratings)
|
|
410
|
+
)
|
|
411
|
+
y2 = int(
|
|
412
|
+
center + (radius - grating_skew) * np.sin(angle + np.pi / num_gratings)
|
|
413
|
+
)
|
|
414
|
+
rr, cc = polygon([y0, y1, y2], [x0, x1, x2])
|
|
415
|
+
rr = np.clip(rr, 0, width - 1)
|
|
416
|
+
cc = np.clip(cc, 0, width - 1)
|
|
417
|
+
radial_grating[rr, cc] = 1
|
|
418
|
+
return Device(device_array=radial_grating, **kwargs)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def offset_grating(
|
|
422
|
+
height: int = 200,
|
|
423
|
+
pitch: int = 120,
|
|
424
|
+
duty_cycle: float = 0.5,
|
|
425
|
+
num_gratings: int = 3,
|
|
426
|
+
**kwargs,
|
|
427
|
+
) -> Device:
|
|
428
|
+
"""
|
|
429
|
+
Create a Device object with an offset grating pattern (alternating rows).
|
|
430
|
+
|
|
431
|
+
Parameters
|
|
432
|
+
----------
|
|
433
|
+
height : int
|
|
434
|
+
The height of the grating. Defaults to 200.
|
|
435
|
+
pitch : int
|
|
436
|
+
The pitch (period) of the grating. Defaults to 120.
|
|
437
|
+
duty_cycle : float
|
|
438
|
+
The duty cycle of the grating. Defaults to 0.5.
|
|
439
|
+
num_gratings : int
|
|
440
|
+
The number of grating periods. Defaults to 3.
|
|
441
|
+
**kwargs : dict
|
|
442
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
443
|
+
|
|
444
|
+
Returns
|
|
445
|
+
-------
|
|
446
|
+
Device
|
|
447
|
+
A Device object containing the offset grating pattern.
|
|
448
|
+
"""
|
|
449
|
+
width = pitch * num_gratings
|
|
450
|
+
grating = np.zeros((height, width))
|
|
451
|
+
grating_width = int(pitch * duty_cycle)
|
|
452
|
+
half_height = height // 2
|
|
453
|
+
for i in range(num_gratings):
|
|
454
|
+
start = i * pitch
|
|
455
|
+
grating[half_height:, start : start + grating_width] = 1
|
|
456
|
+
for i in range(num_gratings):
|
|
457
|
+
start = i * pitch + pitch // 2
|
|
458
|
+
grating[:half_height, start : start + grating_width] = 1
|
|
459
|
+
return Device(device_array=grating, **kwargs)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def l_grating(
|
|
463
|
+
width: int = 200,
|
|
464
|
+
height: Optional[int] = None,
|
|
465
|
+
pitch: int = 100,
|
|
466
|
+
duty_cycle: float = 0.5,
|
|
467
|
+
**kwargs,
|
|
468
|
+
) -> Device:
|
|
469
|
+
"""
|
|
470
|
+
Create a Device object with an L-shaped grating pattern.
|
|
471
|
+
|
|
472
|
+
Parameters
|
|
473
|
+
----------
|
|
474
|
+
width : int
|
|
475
|
+
The width of the L-grating. Defaults to 200.
|
|
476
|
+
height : Optional[int]
|
|
477
|
+
The height of the L-grating. Defaults to the value of width.
|
|
478
|
+
pitch : int
|
|
479
|
+
The pitch (period) of the L-shapes. Defaults to 100.
|
|
480
|
+
duty_cycle : float
|
|
481
|
+
The duty cycle of the L-shapes. Defaults to 0.5.
|
|
482
|
+
**kwargs : dict
|
|
483
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
484
|
+
|
|
485
|
+
Returns
|
|
486
|
+
-------
|
|
487
|
+
Device
|
|
488
|
+
A Device object containing the L-shaped grating pattern.
|
|
489
|
+
"""
|
|
490
|
+
if height is None:
|
|
491
|
+
height = width
|
|
492
|
+
L_grating = np.zeros((height, width))
|
|
493
|
+
num_L_shapes = min(height, width) // pitch
|
|
494
|
+
L_width = int(pitch * duty_cycle)
|
|
495
|
+
for i in range(num_L_shapes):
|
|
496
|
+
start = i * pitch
|
|
497
|
+
L_grating[start : start + L_width, start:] = 1
|
|
498
|
+
L_grating[start:, start : start + L_width] = 1
|
|
499
|
+
return Device(device_array=L_grating, **kwargs)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def disks(
|
|
503
|
+
rows: int = 5, cols: int = 5, disk_radius: int = 30, spacing: int = 60, **kwargs
|
|
504
|
+
) -> Device:
|
|
505
|
+
"""
|
|
506
|
+
Create a Device object with a grid of uniform disks.
|
|
507
|
+
|
|
508
|
+
Parameters
|
|
509
|
+
----------
|
|
510
|
+
rows : int
|
|
511
|
+
The number of rows in the grid. Defaults to 5.
|
|
512
|
+
cols : int
|
|
513
|
+
The number of columns in the grid. Defaults to 5.
|
|
514
|
+
disk_radius : int
|
|
515
|
+
The radius of each disk. Defaults to 30.
|
|
516
|
+
spacing : int
|
|
517
|
+
The spacing between disk centers. Defaults to 60.
|
|
518
|
+
**kwargs : dict
|
|
519
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
520
|
+
|
|
521
|
+
Returns
|
|
522
|
+
-------
|
|
523
|
+
Device
|
|
524
|
+
A Device object containing a grid of disks.
|
|
525
|
+
"""
|
|
526
|
+
grid_height = rows * (2 * disk_radius + spacing) - spacing
|
|
527
|
+
grid_width = cols * (2 * disk_radius + spacing) - spacing
|
|
528
|
+
disks = np.zeros((grid_height, grid_width))
|
|
529
|
+
y, x = np.ogrid[-disk_radius:disk_radius, -disk_radius:disk_radius]
|
|
530
|
+
mask = x**2 + y**2 <= disk_radius**2
|
|
531
|
+
for row in range(rows):
|
|
532
|
+
for col in range(cols):
|
|
533
|
+
center_y = row * (2 * disk_radius + spacing) + disk_radius
|
|
534
|
+
center_x = col * (2 * disk_radius + spacing) + disk_radius
|
|
535
|
+
disks[
|
|
536
|
+
center_y - disk_radius : center_y + disk_radius,
|
|
537
|
+
center_x - disk_radius : center_x + disk_radius,
|
|
538
|
+
][mask] = 1
|
|
539
|
+
return Device(device_array=disks, **kwargs)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def disks_offset(
|
|
543
|
+
rows: int = 5, cols: int = 5, disk_radius: int = 30, spacing: int = 30, **kwargs
|
|
544
|
+
) -> Device:
|
|
545
|
+
"""
|
|
546
|
+
Create a Device object with an offset grid of disks.
|
|
547
|
+
|
|
548
|
+
Parameters
|
|
549
|
+
----------
|
|
550
|
+
rows : int
|
|
551
|
+
The number of rows in the grid. Defaults to 5.
|
|
552
|
+
cols : int
|
|
553
|
+
The number of columns in the grid. Defaults to 5.
|
|
554
|
+
disk_radius : int
|
|
555
|
+
The radius of each disk. Defaults to 30.
|
|
556
|
+
spacing : int
|
|
557
|
+
The spacing between disk centers. Defaults to 30.
|
|
558
|
+
**kwargs : dict
|
|
559
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
560
|
+
|
|
561
|
+
Returns
|
|
562
|
+
-------
|
|
563
|
+
Device
|
|
564
|
+
A Device object containing an offset grid of disks.
|
|
565
|
+
"""
|
|
566
|
+
grid_height = rows * (2 * disk_radius + spacing) - spacing
|
|
567
|
+
grid_width = (
|
|
568
|
+
cols * (2 * disk_radius + spacing) - spacing + (disk_radius + spacing // 2)
|
|
569
|
+
)
|
|
570
|
+
disks_offset = np.zeros((grid_height, grid_width))
|
|
571
|
+
y, x = np.ogrid[-disk_radius:disk_radius, -disk_radius:disk_radius]
|
|
572
|
+
mask = x**2 + y**2 <= disk_radius**2
|
|
573
|
+
for row in range(rows):
|
|
574
|
+
for col in range(cols):
|
|
575
|
+
center_y = row * (2 * disk_radius + spacing) + disk_radius
|
|
576
|
+
center_x = (
|
|
577
|
+
col * (2 * disk_radius + spacing)
|
|
578
|
+
+ disk_radius
|
|
579
|
+
+ (disk_radius + spacing // 2 if row % 2 == 1 else 0)
|
|
580
|
+
)
|
|
581
|
+
disks_offset[
|
|
582
|
+
center_y - disk_radius : center_y + disk_radius,
|
|
583
|
+
center_x - disk_radius : center_x + disk_radius,
|
|
584
|
+
][mask] = 1
|
|
585
|
+
return Device(device_array=disks_offset, **kwargs)
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def disks_varying(
|
|
589
|
+
rows: int = 5,
|
|
590
|
+
cols: int = 5,
|
|
591
|
+
min_disk_radius: int = 10,
|
|
592
|
+
max_disk_radius: int = 30,
|
|
593
|
+
spacing: int = 30,
|
|
594
|
+
**kwargs,
|
|
595
|
+
) -> Device:
|
|
596
|
+
"""
|
|
597
|
+
Create a Device object with a grid of disks with varying radii.
|
|
598
|
+
|
|
599
|
+
Parameters
|
|
600
|
+
----------
|
|
601
|
+
rows : int
|
|
602
|
+
The number of rows in the grid. Defaults to 5.
|
|
603
|
+
cols : int
|
|
604
|
+
The number of columns in the grid. Defaults to 5.
|
|
605
|
+
min_disk_radius : int
|
|
606
|
+
The minimum radius of the disks. Defaults to 10.
|
|
607
|
+
max_disk_radius : int
|
|
608
|
+
The maximum radius of the disks. Defaults to 30.
|
|
609
|
+
spacing : int
|
|
610
|
+
The spacing between disk centers. Defaults to 30.
|
|
611
|
+
**kwargs : dict
|
|
612
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
613
|
+
|
|
614
|
+
Returns
|
|
615
|
+
-------
|
|
616
|
+
Device
|
|
617
|
+
A Device object containing a grid of disks with varying radii.
|
|
618
|
+
"""
|
|
619
|
+
grid_height = rows * (2 * max_disk_radius + spacing) - spacing
|
|
620
|
+
grid_width = cols * (2 * max_disk_radius + spacing) - spacing
|
|
621
|
+
disks_varying = np.zeros((grid_height, grid_width))
|
|
622
|
+
radius_range = np.linspace(min_disk_radius, max_disk_radius, rows * cols).reshape(
|
|
623
|
+
rows, cols
|
|
624
|
+
)
|
|
625
|
+
for row in range(rows):
|
|
626
|
+
for col in range(cols):
|
|
627
|
+
disk_radius = int(radius_range[row, col])
|
|
628
|
+
y, x = np.ogrid[-disk_radius:disk_radius, -disk_radius:disk_radius]
|
|
629
|
+
mask = x**2 + y**2 <= disk_radius**2
|
|
630
|
+
center_y = row * (2 * max_disk_radius + spacing) + max_disk_radius
|
|
631
|
+
center_x = col * (2 * max_disk_radius + spacing) + max_disk_radius
|
|
632
|
+
disks_varying[
|
|
633
|
+
center_y - disk_radius : center_y + disk_radius,
|
|
634
|
+
center_x - disk_radius : center_x + disk_radius,
|
|
635
|
+
][mask] = 1
|
|
636
|
+
return Device(device_array=disks_varying, **kwargs)
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def holes(
|
|
640
|
+
rows: int = 5, cols: int = 5, hole_radius: int = 30, spacing: int = 30, **kwargs
|
|
641
|
+
) -> Device:
|
|
642
|
+
"""
|
|
643
|
+
Create a Device object with a grid of uniform circular holes.
|
|
644
|
+
|
|
645
|
+
Parameters
|
|
646
|
+
----------
|
|
647
|
+
rows : int
|
|
648
|
+
The number of rows in the grid. Defaults to 5.
|
|
649
|
+
cols : int
|
|
650
|
+
The number of columns in the grid. Defaults to 5.
|
|
651
|
+
hole_radius : int
|
|
652
|
+
The radius of each hole. Defaults to 30.
|
|
653
|
+
spacing : int
|
|
654
|
+
The spacing between hole centers. Defaults to 30.
|
|
655
|
+
**kwargs : dict
|
|
656
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
657
|
+
|
|
658
|
+
Returns
|
|
659
|
+
-------
|
|
660
|
+
Device
|
|
661
|
+
A Device object containing a grid of circular holes.
|
|
662
|
+
"""
|
|
663
|
+
grid_height = rows * (2 * hole_radius + spacing) - spacing
|
|
664
|
+
grid_width = cols * (2 * hole_radius + spacing) - spacing
|
|
665
|
+
holes = np.ones((grid_height, grid_width))
|
|
666
|
+
y, x = np.ogrid[-hole_radius:hole_radius, -hole_radius:hole_radius]
|
|
667
|
+
mask = x**2 + y**2 <= hole_radius**2
|
|
668
|
+
for row in range(rows):
|
|
669
|
+
for col in range(cols):
|
|
670
|
+
center_y = row * (2 * hole_radius + spacing) + hole_radius
|
|
671
|
+
center_x = col * (2 * hole_radius + spacing) + hole_radius
|
|
672
|
+
holes[
|
|
673
|
+
center_y - hole_radius : center_y + hole_radius,
|
|
674
|
+
center_x - hole_radius : center_x + hole_radius,
|
|
675
|
+
][mask] = 0
|
|
676
|
+
return Device(device_array=holes, **kwargs)
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def holes_offset(
|
|
680
|
+
rows: int = 5, cols: int = 5, hole_radius: int = 30, spacing: int = 30, **kwargs
|
|
681
|
+
) -> Device:
|
|
682
|
+
"""
|
|
683
|
+
Create a Device object with an offset grid of circular holes.
|
|
684
|
+
|
|
685
|
+
Parameters
|
|
686
|
+
----------
|
|
687
|
+
rows : int
|
|
688
|
+
The number of rows in the grid. Defaults to 5.
|
|
689
|
+
cols : int
|
|
690
|
+
The number of columns in the grid. Defaults to 5.
|
|
691
|
+
hole_radius : int
|
|
692
|
+
The radius of each hole. Defaults to 30.
|
|
693
|
+
spacing : int
|
|
694
|
+
The spacing between hole centers. Defaults to 30.
|
|
695
|
+
**kwargs : dict
|
|
696
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
697
|
+
|
|
698
|
+
Returns
|
|
699
|
+
-------
|
|
700
|
+
Device
|
|
701
|
+
A Device object containing an offset grid of circular holes.
|
|
702
|
+
"""
|
|
703
|
+
grid_height = rows * (2 * hole_radius + spacing) - spacing
|
|
704
|
+
grid_width = (
|
|
705
|
+
cols * (2 * hole_radius + spacing) - spacing + (hole_radius + spacing // 2)
|
|
706
|
+
)
|
|
707
|
+
holes_offset = np.ones((grid_height, grid_width))
|
|
708
|
+
y, x = np.ogrid[-hole_radius:hole_radius, -hole_radius:hole_radius]
|
|
709
|
+
mask = x**2 + y**2 <= hole_radius**2
|
|
710
|
+
for row in range(rows):
|
|
711
|
+
for col in range(cols):
|
|
712
|
+
center_y = row * (2 * hole_radius + spacing) + hole_radius
|
|
713
|
+
center_x = (
|
|
714
|
+
col * (2 * hole_radius + spacing)
|
|
715
|
+
+ hole_radius
|
|
716
|
+
+ (hole_radius + spacing // 2 if row % 2 == 1 else 0)
|
|
717
|
+
)
|
|
718
|
+
holes_offset[
|
|
719
|
+
center_y - hole_radius : center_y + hole_radius,
|
|
720
|
+
center_x - hole_radius : center_x + hole_radius,
|
|
721
|
+
][mask] = 0
|
|
722
|
+
return Device(device_array=holes_offset, **kwargs)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def holes_varying(
|
|
726
|
+
rows: int = 5,
|
|
727
|
+
cols: int = 5,
|
|
728
|
+
min_hole_radius: int = 10,
|
|
729
|
+
max_hole_radius: int = 30,
|
|
730
|
+
spacing: int = 30,
|
|
731
|
+
**kwargs,
|
|
732
|
+
) -> Device:
|
|
733
|
+
"""
|
|
734
|
+
Create a Device object with a grid of circular holes with varying radii.
|
|
735
|
+
|
|
736
|
+
Parameters
|
|
737
|
+
----------
|
|
738
|
+
rows : int
|
|
739
|
+
The number of rows in the grid. Defaults to 5.
|
|
740
|
+
cols : int
|
|
741
|
+
The number of columns in the grid. Defaults to 5.
|
|
742
|
+
min_hole_radius : int
|
|
743
|
+
The minimum radius of the holes. Defaults to 10.
|
|
744
|
+
max_hole_radius : int
|
|
745
|
+
The maximum radius of the holes. Defaults to 30.
|
|
746
|
+
spacing : int
|
|
747
|
+
The spacing between hole centers. Defaults to 30.
|
|
748
|
+
**kwargs : dict
|
|
749
|
+
Additional keyword arguments to be passed to the Device constructor.
|
|
750
|
+
|
|
751
|
+
Returns
|
|
752
|
+
-------
|
|
753
|
+
Device
|
|
754
|
+
A Device object containing a grid of circular holes with varying radii.
|
|
755
|
+
"""
|
|
756
|
+
grid_height = rows * (2 * max_hole_radius + spacing) - spacing
|
|
757
|
+
grid_width = cols * (2 * max_hole_radius + spacing) - spacing
|
|
758
|
+
holes_varying = np.ones((grid_height, grid_width))
|
|
759
|
+
radius_range = np.linspace(min_hole_radius, max_hole_radius, rows * cols).reshape(
|
|
760
|
+
rows, cols
|
|
761
|
+
)
|
|
762
|
+
for row in range(rows):
|
|
763
|
+
for col in range(cols):
|
|
764
|
+
hole_radius = int(radius_range[row, col])
|
|
765
|
+
y, x = np.ogrid[-hole_radius:hole_radius, -hole_radius:hole_radius]
|
|
766
|
+
mask = x**2 + y**2 <= hole_radius**2
|
|
767
|
+
center_y = row * (2 * max_hole_radius + spacing) + max_hole_radius
|
|
768
|
+
center_x = col * (2 * max_hole_radius + spacing) + max_hole_radius
|
|
769
|
+
holes_varying[
|
|
770
|
+
center_y - hole_radius : center_y + hole_radius,
|
|
771
|
+
center_x - hole_radius : center_x + hole_radius,
|
|
772
|
+
][mask] = 0
|
|
773
|
+
return Device(device_array=holes_varying, **kwargs)
|