pyafv 0.3.1__cp313-cp313-musllinux_1_2_i686.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.
- pyafv/__init__.py +11 -0
- pyafv/backend.py +16 -0
- pyafv/cell_geom.cpython-313-i386-linux-musl.so +0 -0
- pyafv/cell_geom.pyx +508 -0
- pyafv/finite_voronoi_fallback.py +989 -0
- pyafv/finite_voronoi_fast.py +802 -0
- pyafv/physical_params.py +117 -0
- pyafv/simulator.py +37 -0
- pyafv-0.3.1.dist-info/METADATA +83 -0
- pyafv-0.3.1.dist-info/RECORD +13 -0
- pyafv-0.3.1.dist-info/WHEEL +5 -0
- pyafv-0.3.1.dist-info/licenses/LICENSE +21 -0
- pyafv-0.3.1.dist-info/top_level.txt +1 -0
pyafv/__init__.py
ADDED
pyafv/backend.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# chooses fast vs fallback implementation
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
from . import finite_voronoi_fast as _impl
|
|
5
|
+
_BACKEND_NAME = "cython"
|
|
6
|
+
except ImportError: # pragma: no cover
|
|
7
|
+
_BACKEND_NAME = "python"
|
|
8
|
+
from . import finite_voronoi_fallback as _impl
|
|
9
|
+
|
|
10
|
+
# ---- for explicit API ----
|
|
11
|
+
backend_simulator = _impl.FiniteVoronoiSimulator
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"backend_simulator",
|
|
15
|
+
"_BACKEND_NAME",
|
|
16
|
+
]
|
|
Binary file
|
pyafv/cell_geom.pyx
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
# cython: boundscheck=False, wraparound=False, nonecheck=False, cdivision=True, initializedcheck=False
|
|
2
|
+
# distutils: language = c++
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
cimport numpy as cnp
|
|
6
|
+
from libc.math cimport atan2, cos
|
|
7
|
+
cimport cython
|
|
8
|
+
|
|
9
|
+
# Routine used in _build_voronoi_with_extensions()
|
|
10
|
+
def build_vertexpair_and_vertexpoints_cy(object ridge_vertices_all,
|
|
11
|
+
object ridge_points,
|
|
12
|
+
long num_vertices,
|
|
13
|
+
long N):
|
|
14
|
+
# --- accept any int dtype and make sure it's contiguous int64 ---
|
|
15
|
+
cdef cnp.ndarray[cnp.int64_t, ndim=2] rv = np.ascontiguousarray(ridge_vertices_all, dtype=np.int64)
|
|
16
|
+
cdef cnp.ndarray[cnp.int64_t, ndim=2] rp = np.ascontiguousarray(ridge_points, dtype=np.int64)
|
|
17
|
+
# ----------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
cdef Py_ssize_t R = rv.shape[0]
|
|
20
|
+
cdef Py_ssize_t k, v1, v2, v_id, ridge_id
|
|
21
|
+
cdef Py_ssize_t i_pt, j_pt
|
|
22
|
+
|
|
23
|
+
cdef dict vertex_incident_ridges = {}
|
|
24
|
+
cdef dict vertexpair2ridge = {}
|
|
25
|
+
cdef dict vertex_points = {}
|
|
26
|
+
cdef list ridges_for_v
|
|
27
|
+
cdef set s
|
|
28
|
+
|
|
29
|
+
for k in range(R):
|
|
30
|
+
v1 = <Py_ssize_t> rv[k, 0]
|
|
31
|
+
v2 = <Py_ssize_t> rv[k, 1]
|
|
32
|
+
|
|
33
|
+
if v1 not in vertex_incident_ridges:
|
|
34
|
+
vertex_incident_ridges[v1] = []
|
|
35
|
+
if v2 not in vertex_incident_ridges:
|
|
36
|
+
vertex_incident_ridges[v2] = []
|
|
37
|
+
|
|
38
|
+
(<list>vertex_incident_ridges[v1]).append(k)
|
|
39
|
+
(<list>vertex_incident_ridges[v2]).append(k)
|
|
40
|
+
|
|
41
|
+
vertexpair2ridge[(<int>v1, <int>v2)] = <int>k
|
|
42
|
+
vertexpair2ridge[(<int>v2, <int>v1)] = <int>k
|
|
43
|
+
|
|
44
|
+
if N > 2:
|
|
45
|
+
for v_id in range(num_vertices):
|
|
46
|
+
if v_id not in vertex_incident_ridges:
|
|
47
|
+
continue
|
|
48
|
+
ridges_for_v = <list>vertex_incident_ridges[v_id]
|
|
49
|
+
s = set()
|
|
50
|
+
for ridge_id in ridges_for_v:
|
|
51
|
+
i_pt = <int> rp[ridge_id, 0]
|
|
52
|
+
j_pt = <int> rp[ridge_id, 1]
|
|
53
|
+
s.add(i_pt); s.add(j_pt)
|
|
54
|
+
vertex_points[v_id] = list(s)
|
|
55
|
+
|
|
56
|
+
return vertexpair2ridge, vertex_points
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------------------
|
|
60
|
+
# Part 1
|
|
61
|
+
# ---------------------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
def pad_regions_cy(object regions):
|
|
64
|
+
cdef Py_ssize_t n = len(regions)
|
|
65
|
+
cdef Py_ssize_t i, j, m, kmax = 0
|
|
66
|
+
cdef list r
|
|
67
|
+
|
|
68
|
+
for i in range(n):
|
|
69
|
+
m = len(<list>regions[i])
|
|
70
|
+
if m > kmax:
|
|
71
|
+
kmax = m
|
|
72
|
+
|
|
73
|
+
cdef cnp.ndarray[cnp.int64_t, ndim=2] out = \
|
|
74
|
+
np.empty((n, kmax), dtype=np.int64)
|
|
75
|
+
|
|
76
|
+
# fill with -1
|
|
77
|
+
for i in range(n):
|
|
78
|
+
for j in range(kmax):
|
|
79
|
+
out[i, j] = -1
|
|
80
|
+
|
|
81
|
+
# copy rows
|
|
82
|
+
for i in range(n):
|
|
83
|
+
r = <list>regions[i]
|
|
84
|
+
m = len(r)
|
|
85
|
+
for j in range(m):
|
|
86
|
+
out[i, j] = <long long>r[j]
|
|
87
|
+
|
|
88
|
+
return out
|
|
89
|
+
|
|
90
|
+
@cython.cfunc
|
|
91
|
+
cdef inline long long pack_pair_ll(long a, long b) nogil:
|
|
92
|
+
return ((<long long>a) << 32) | (<unsigned long long>b)
|
|
93
|
+
|
|
94
|
+
@cython.boundscheck(False)
|
|
95
|
+
@cython.wraparound(False)
|
|
96
|
+
def build_point_edges_cy(
|
|
97
|
+
cnp.int64_t[:, :] vor_regions,
|
|
98
|
+
cnp.int64_t[:] point_region,
|
|
99
|
+
cnp.float64_t[:, :] vertices_all,
|
|
100
|
+
cnp.float64_t[:, :] pts,
|
|
101
|
+
long num_vertices,
|
|
102
|
+
dict vertexpair2ridge_py,
|
|
103
|
+
cnp.int64_t[:] p1,
|
|
104
|
+
cnp.int64_t[:, :] p1_edges_pack,
|
|
105
|
+
cnp.int64_t[:, :] p1_verts_pack,
|
|
106
|
+
cnp.int64_t[:] p2,
|
|
107
|
+
cnp.int64_t[:, :] p2_edges_pack,
|
|
108
|
+
cnp.int64_t[:, :] p2_verts_pack
|
|
109
|
+
):
|
|
110
|
+
cdef Py_ssize_t N = pts.shape[0]
|
|
111
|
+
cdef list point_edges_type = [None] * N
|
|
112
|
+
cdef list point_vertices_f_idx = [None] * N
|
|
113
|
+
|
|
114
|
+
# packed (v1,v2) -> ridge_id
|
|
115
|
+
cdef dict pair2ridge = {}
|
|
116
|
+
cdef object k, rid_val_py
|
|
117
|
+
for k, rid_val_py in vertexpair2ridge_py.items():
|
|
118
|
+
pair2ridge[ ((<long long>(<tuple>k)[0])<<32) | (<unsigned long long>(<tuple>k)[1]) ] = <int>rid_val_py
|
|
119
|
+
|
|
120
|
+
# declarations
|
|
121
|
+
cdef Py_ssize_t idx, j, m, ecount, n_valid
|
|
122
|
+
cdef int rid
|
|
123
|
+
cdef long region_id, v1, v2
|
|
124
|
+
cdef cnp.int64_t[:] region_row
|
|
125
|
+
cdef cnp.float64_t cx, cy, vx, vy
|
|
126
|
+
cdef int Rmax = vor_regions.shape[1]
|
|
127
|
+
cdef bint use_p1_flag
|
|
128
|
+
cdef long val
|
|
129
|
+
cdef list edges_type
|
|
130
|
+
cdef list vertices_f_idx
|
|
131
|
+
|
|
132
|
+
# scratch arrays as Python objects + memoryviews
|
|
133
|
+
cdef object angles_np = np.empty(Rmax, dtype=np.float64)
|
|
134
|
+
cdef cnp.float64_t[:] angles = angles_np
|
|
135
|
+
cdef object valid_np = np.empty(Rmax, dtype=np.uint8)
|
|
136
|
+
cdef cnp.uint8_t[:] valid = valid_np
|
|
137
|
+
|
|
138
|
+
cdef object v_ids_np
|
|
139
|
+
cdef object v2_ids_np
|
|
140
|
+
cdef cnp.int64_t[:] v_ids
|
|
141
|
+
cdef cnp.int64_t[:] v2_ids
|
|
142
|
+
|
|
143
|
+
cdef cnp.int64_t[:, :] pack_e
|
|
144
|
+
cdef cnp.int64_t[:, :] pack_v
|
|
145
|
+
|
|
146
|
+
for idx in range(N):
|
|
147
|
+
region_id = <long>point_region[idx]
|
|
148
|
+
if region_id < 0:
|
|
149
|
+
point_edges_type[idx] = []
|
|
150
|
+
point_vertices_f_idx[idx] = []
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
# 1) collect region vertex ids (stop at -1)
|
|
154
|
+
region_row = vor_regions[region_id]
|
|
155
|
+
m = 0
|
|
156
|
+
while m < Rmax and region_row[m] != -1:
|
|
157
|
+
m += 1
|
|
158
|
+
if m == 0:
|
|
159
|
+
point_edges_type[idx] = []
|
|
160
|
+
point_vertices_f_idx[idx] = []
|
|
161
|
+
continue
|
|
162
|
+
|
|
163
|
+
v_ids_np = np.empty(m, dtype=np.int64)
|
|
164
|
+
v_ids = v_ids_np
|
|
165
|
+
for j in range(m):
|
|
166
|
+
v_ids[j] = region_row[j]
|
|
167
|
+
|
|
168
|
+
# 2) angles around cell center
|
|
169
|
+
cx = pts[idx, 0]; cy = pts[idx, 1]
|
|
170
|
+
for j in range(m):
|
|
171
|
+
vx = vertices_all[v_ids[j], 0] - cx
|
|
172
|
+
vy = vertices_all[v_ids[j], 1] - cy
|
|
173
|
+
angles[j] = atan2(vy, vx)
|
|
174
|
+
|
|
175
|
+
# 3) sort clockwise (descending angle)
|
|
176
|
+
order = np.argsort(np.asarray(angles_np)[:m]) # Python object
|
|
177
|
+
v_ids_np = np.asarray(v_ids_np)[order[::-1]] # new array
|
|
178
|
+
v_ids = v_ids_np # rebind memoryview to the new array
|
|
179
|
+
|
|
180
|
+
# 4) consecutive pairs with wrap
|
|
181
|
+
v2_ids_np = np.empty(m, dtype=np.int64)
|
|
182
|
+
v2_ids = v2_ids_np
|
|
183
|
+
for j in range(m-1):
|
|
184
|
+
v2_ids[j] = v_ids[j+1]
|
|
185
|
+
v2_ids[m-1] = v_ids[0]
|
|
186
|
+
|
|
187
|
+
# 5) mask: skip ray-ray
|
|
188
|
+
n_valid = 0
|
|
189
|
+
for j in range(m):
|
|
190
|
+
valid[j] = not ((v_ids[j] >= num_vertices) and (v2_ids[j] >= num_vertices))
|
|
191
|
+
if valid[j]:
|
|
192
|
+
n_valid += 1
|
|
193
|
+
if n_valid == 0:
|
|
194
|
+
point_edges_type[idx] = []
|
|
195
|
+
point_vertices_f_idx[idx] = []
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
# 6) gather packs
|
|
199
|
+
pack_e = np.empty((n_valid, 3), dtype=np.int64)
|
|
200
|
+
pack_v = np.empty((n_valid, 3), dtype=np.int64)
|
|
201
|
+
ecount = 0
|
|
202
|
+
|
|
203
|
+
for j in range(m):
|
|
204
|
+
if not valid[j]:
|
|
205
|
+
continue
|
|
206
|
+
v1 = <long>v_ids[j]
|
|
207
|
+
v2 = <long>v2_ids[j]
|
|
208
|
+
|
|
209
|
+
rid = <int>pair2ridge.get(((<long long>v1)<<32) | (<unsigned long long>v2), -1)
|
|
210
|
+
if rid < 0:
|
|
211
|
+
rid = <int>vertexpair2ridge_py.get((v1, v2), -1)
|
|
212
|
+
if rid < 0:
|
|
213
|
+
raise KeyError(f"Missing ridge id for pair ({v1}, {v2})")
|
|
214
|
+
|
|
215
|
+
use_p1_flag = (p1[rid] == idx)
|
|
216
|
+
if use_p1_flag:
|
|
217
|
+
pack_e[ecount, 0] = p1_edges_pack[rid, 0]
|
|
218
|
+
pack_e[ecount, 1] = p1_edges_pack[rid, 1]
|
|
219
|
+
pack_e[ecount, 2] = p1_edges_pack[rid, 2]
|
|
220
|
+
pack_v[ecount, 0] = p1_verts_pack[rid, 0]
|
|
221
|
+
pack_v[ecount, 1] = p1_verts_pack[rid, 1]
|
|
222
|
+
pack_v[ecount, 2] = p1_verts_pack[rid, 2]
|
|
223
|
+
else:
|
|
224
|
+
pack_e[ecount, 0] = p2_edges_pack[rid, 0]
|
|
225
|
+
pack_e[ecount, 1] = p2_edges_pack[rid, 1]
|
|
226
|
+
pack_e[ecount, 2] = p2_edges_pack[rid, 2]
|
|
227
|
+
pack_v[ecount, 0] = p2_verts_pack[rid, 0]
|
|
228
|
+
pack_v[ecount, 1] = p2_verts_pack[rid, 1]
|
|
229
|
+
pack_v[ecount, 2] = p2_verts_pack[rid, 2]
|
|
230
|
+
ecount += 1
|
|
231
|
+
|
|
232
|
+
# 7) flatten valid entries
|
|
233
|
+
edges_type = []
|
|
234
|
+
vertices_f_idx = []
|
|
235
|
+
for j in range(n_valid):
|
|
236
|
+
val = pack_e[j, 0]
|
|
237
|
+
if val >= 0:
|
|
238
|
+
edges_type.append(<int>val)
|
|
239
|
+
vertices_f_idx.append(<int>pack_v[j, 0])
|
|
240
|
+
val = pack_e[j, 1]
|
|
241
|
+
if val >= 0:
|
|
242
|
+
edges_type.append(<int>val)
|
|
243
|
+
vertices_f_idx.append(<int>pack_v[j, 1])
|
|
244
|
+
val = pack_e[j, 2]
|
|
245
|
+
if val >= 0:
|
|
246
|
+
edges_type.append(<int>val)
|
|
247
|
+
vertices_f_idx.append(<int>pack_v[j, 2])
|
|
248
|
+
|
|
249
|
+
if len(edges_type) != len(vertices_f_idx):
|
|
250
|
+
raise ValueError("Vertex and edge number not equal!")
|
|
251
|
+
|
|
252
|
+
point_edges_type[idx] = edges_type
|
|
253
|
+
point_vertices_f_idx[idx] = vertices_f_idx
|
|
254
|
+
|
|
255
|
+
return point_edges_type, point_vertices_f_idx
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# ---------------------------------------------------------------------------------------
|
|
259
|
+
# Part 2
|
|
260
|
+
# ---------------------------------------------------------------------------------------
|
|
261
|
+
|
|
262
|
+
@cython.cfunc
|
|
263
|
+
cdef inline double cross2(double ax, double ay, double bx, double by) nogil:
|
|
264
|
+
return ax*by - ay*bx
|
|
265
|
+
|
|
266
|
+
@cython.cfunc
|
|
267
|
+
cdef inline void perp(double ux, double uy, double* outx, double* outy) noexcept nogil:
|
|
268
|
+
outx[0] = uy
|
|
269
|
+
outy[0] = -ux
|
|
270
|
+
|
|
271
|
+
@cython.boundscheck(False)
|
|
272
|
+
@cython.wraparound(False)
|
|
273
|
+
def compute_vertex_derivatives_cy(
|
|
274
|
+
object point_edges_type, # list of 1D int arrays/lists
|
|
275
|
+
object point_vertices_f_idx, # list of 1D int arrays/lists
|
|
276
|
+
cnp.float64_t[:, :] vertices_all, # (num_vertices_ext + 2*num_ridges, 2)
|
|
277
|
+
cnp.float64_t[:, :] pts, # (N, 2)
|
|
278
|
+
double r,
|
|
279
|
+
double A0,
|
|
280
|
+
double P0,
|
|
281
|
+
long num_vertices_ext,
|
|
282
|
+
long num_ridges,
|
|
283
|
+
cnp.int64_t[:, :] vertex_out_points # (2*num_ridges, 2), rows sorted [i,j], col1=max(i,j)
|
|
284
|
+
):
|
|
285
|
+
cdef Py_ssize_t N = pts.shape[0]
|
|
286
|
+
cdef Py_ssize_t i, j, E
|
|
287
|
+
cdef double two_pi = 6.2831853071795864769
|
|
288
|
+
|
|
289
|
+
# outputs
|
|
290
|
+
cdef object vertex_out_da_dtheta_np = np.zeros((2*num_ridges, 2), dtype=np.float64)
|
|
291
|
+
cdef object vertex_out_dl_dtheta_np = np.zeros((2*num_ridges, 2), dtype=np.float64)
|
|
292
|
+
cdef object dA_poly_dh_np = np.zeros((num_vertices_ext + 2*num_ridges, 2), dtype=np.float64)
|
|
293
|
+
cdef object dP_poly_dh_np = np.zeros((num_vertices_ext + 2*num_ridges, 2), dtype=np.float64)
|
|
294
|
+
cdef object area_list_np = np.zeros(N, dtype=np.float64)
|
|
295
|
+
cdef object perimeter_list_np = np.zeros(N, dtype=np.float64)
|
|
296
|
+
|
|
297
|
+
cdef cnp.float64_t[:, :] vertex_out_da_dtheta = vertex_out_da_dtheta_np
|
|
298
|
+
cdef cnp.float64_t[:, :] vertex_out_dl_dtheta = vertex_out_dl_dtheta_np
|
|
299
|
+
cdef cnp.float64_t[:, :] dA_poly_dh = dA_poly_dh_np
|
|
300
|
+
cdef cnp.float64_t[:, :] dP_poly_dh = dP_poly_dh_np
|
|
301
|
+
cdef cnp.float64_t[:] area_list = area_list_np
|
|
302
|
+
cdef cnp.float64_t[:] perimeter_list = perimeter_list_np
|
|
303
|
+
|
|
304
|
+
# declarations used inside loop (declare here, assign in loop)
|
|
305
|
+
cdef object edges_type_obj
|
|
306
|
+
cdef object v_idx_obj
|
|
307
|
+
cdef cnp.int64_t[:] edges_type
|
|
308
|
+
cdef cnp.int64_t[:] vertices_f_idx
|
|
309
|
+
|
|
310
|
+
# scalars
|
|
311
|
+
cdef double Pi_straight, Ai_straight, Pi_arc, Ai_arc, Pi, Ai
|
|
312
|
+
cdef double cx, cy
|
|
313
|
+
cdef double v1x, v1y, v2x, v2y, v0x, v0y
|
|
314
|
+
cdef double v1mx, v1my, v2mx, v2my, v0mx, v0my
|
|
315
|
+
cdef double s12x, s12y, s10x, s10y
|
|
316
|
+
cdef double l12, l10
|
|
317
|
+
cdef double a1, a2, dangle, da1_full, da2_full
|
|
318
|
+
cdef double dAi_v1_x, dAi_v1_y
|
|
319
|
+
cdef double dPi_v1_x, dPi_v1_y
|
|
320
|
+
cdef long vidx, vprev
|
|
321
|
+
cdef long outer_row, which_col
|
|
322
|
+
cdef long k1, k2
|
|
323
|
+
|
|
324
|
+
# moved here (was causing your error inside the loop)
|
|
325
|
+
cdef double p2x, p2y, p0x, p0y
|
|
326
|
+
|
|
327
|
+
# small boolean arrays per cell
|
|
328
|
+
cdef object mask_str_np
|
|
329
|
+
cdef cnp.uint8_t[:] mask_str
|
|
330
|
+
cdef object mask_prev_str_np
|
|
331
|
+
cdef cnp.uint8_t[:] mask_prev_str
|
|
332
|
+
cdef object mask_arc_np
|
|
333
|
+
cdef cnp.uint8_t[:] mask_arc
|
|
334
|
+
|
|
335
|
+
# arrays per cell
|
|
336
|
+
cdef object v1_idx_np
|
|
337
|
+
cdef object v2_idx_np
|
|
338
|
+
cdef object v0_idx_np
|
|
339
|
+
cdef cnp.int64_t[:] v1_idx
|
|
340
|
+
cdef cnp.int64_t[:] v2_idx
|
|
341
|
+
cdef cnp.int64_t[:] v0_idx
|
|
342
|
+
|
|
343
|
+
cdef object a1_full_np
|
|
344
|
+
cdef object a2_full_np
|
|
345
|
+
cdef cnp.float64_t[:] a1_full
|
|
346
|
+
cdef cnp.float64_t[:] a2_full
|
|
347
|
+
|
|
348
|
+
cdef object dangle_full_np
|
|
349
|
+
cdef cnp.float64_t[:] dangle_full
|
|
350
|
+
|
|
351
|
+
for i in range(N):
|
|
352
|
+
edges_type_obj = point_edges_type[i]
|
|
353
|
+
v_idx_obj = point_vertices_f_idx[i]
|
|
354
|
+
|
|
355
|
+
# ensure ndarray[int64]
|
|
356
|
+
if not isinstance(edges_type_obj, np.ndarray) or (<cnp.ndarray>edges_type_obj).dtype.num != cnp.NPY_INT64:
|
|
357
|
+
edges_type_obj = np.asarray(edges_type_obj, dtype=np.int64)
|
|
358
|
+
if not isinstance(v_idx_obj, np.ndarray) or (<cnp.ndarray>v_idx_obj).dtype.num != cnp.NPY_INT64:
|
|
359
|
+
v_idx_obj = np.asarray(v_idx_obj, dtype=np.int64)
|
|
360
|
+
|
|
361
|
+
# assign to memoryviews (no cdef here)
|
|
362
|
+
edges_type = edges_type_obj
|
|
363
|
+
vertices_f_idx = v_idx_obj
|
|
364
|
+
E = vertices_f_idx.shape[0]
|
|
365
|
+
|
|
366
|
+
if E < 2:
|
|
367
|
+
area_list[i] = 3.14159265358979323846 * (r * r)
|
|
368
|
+
perimeter_list[i] = 2.0 * 3.14159265358979323846 * r
|
|
369
|
+
continue
|
|
370
|
+
|
|
371
|
+
# ring indices
|
|
372
|
+
v1_idx_np = np.empty(E, dtype=np.int64)
|
|
373
|
+
v2_idx_np = np.empty(E, dtype=np.int64)
|
|
374
|
+
v0_idx_np = np.empty(E, dtype=np.int64)
|
|
375
|
+
v1_idx = v1_idx_np; v2_idx = v2_idx_np; v0_idx = v0_idx_np
|
|
376
|
+
|
|
377
|
+
for j in range(E):
|
|
378
|
+
v1_idx[j] = vertices_f_idx[j]
|
|
379
|
+
v2_idx[j] = vertices_f_idx[(j+1) % E]
|
|
380
|
+
v0_idx[j] = vertices_f_idx[(j-1+E) % E]
|
|
381
|
+
|
|
382
|
+
cx = pts[i, 0]; cy = pts[i, 1]
|
|
383
|
+
|
|
384
|
+
# masks
|
|
385
|
+
mask_str_np = np.empty(E, dtype=np.uint8)
|
|
386
|
+
mask_arc_np = np.empty(E, dtype=np.uint8)
|
|
387
|
+
mask_prev_str_np = np.empty(E, dtype=np.uint8)
|
|
388
|
+
mask_str = mask_str_np; mask_arc = mask_arc_np; mask_prev_str = mask_prev_str_np
|
|
389
|
+
|
|
390
|
+
for j in range(E):
|
|
391
|
+
mask_str[j] = 1 if edges_type[j] == 1 else 0
|
|
392
|
+
mask_arc[j] = 0 if mask_str[j] else 1
|
|
393
|
+
|
|
394
|
+
# ----- perimeter & area -----
|
|
395
|
+
Pi_straight = 0.0
|
|
396
|
+
Ai_straight = 0.0
|
|
397
|
+
|
|
398
|
+
for j in range(E):
|
|
399
|
+
if mask_str[j]:
|
|
400
|
+
v1x = vertices_all[v1_idx[j], 0]; v1y = vertices_all[v1_idx[j], 1]
|
|
401
|
+
v2x = vertices_all[v2_idx[j], 0]; v2y = vertices_all[v2_idx[j], 1]
|
|
402
|
+
v1mx = v1x - cx; v1my = v1y - cy
|
|
403
|
+
v2mx = v2x - cx; v2my = v2y - cy
|
|
404
|
+
s12x = v1x - v2x; s12y = v1y - v2y
|
|
405
|
+
l12 = (s12x*s12x + s12y*s12y) ** 0.5
|
|
406
|
+
Pi_straight += l12
|
|
407
|
+
Ai_straight += -0.5 * cross2(v1mx, v1my, v2mx, v2my)
|
|
408
|
+
|
|
409
|
+
Pi_arc = 0.0
|
|
410
|
+
Ai_arc = 0.0
|
|
411
|
+
|
|
412
|
+
a1_full_np = np.empty(E, dtype=np.float64)
|
|
413
|
+
a2_full_np = np.empty(E, dtype=np.float64)
|
|
414
|
+
dangle_full_np = np.empty(E, dtype=np.float64)
|
|
415
|
+
a1_full = a1_full_np; a2_full = a2_full_np; dangle_full = dangle_full_np
|
|
416
|
+
|
|
417
|
+
for j in range(E):
|
|
418
|
+
v1x = vertices_all[v1_idx[j], 0]; v1y = vertices_all[v1_idx[j], 1]
|
|
419
|
+
v2x = vertices_all[v2_idx[j], 0]; v2y = vertices_all[v2_idx[j], 1]
|
|
420
|
+
v1mx = v1x - cx; v1my = v1y - cy
|
|
421
|
+
v2mx = v2x - cx; v2my = v2y - cy
|
|
422
|
+
a1 = atan2(v1my, v1mx)
|
|
423
|
+
a2 = atan2(v2my, v2mx)
|
|
424
|
+
dangle = a1 - a2
|
|
425
|
+
if dangle < 0.0:
|
|
426
|
+
dangle += two_pi
|
|
427
|
+
a1_full[j] = a1
|
|
428
|
+
a2_full[j] = a2
|
|
429
|
+
dangle_full[j] = dangle
|
|
430
|
+
if mask_arc[j]:
|
|
431
|
+
Pi_arc += r * dangle
|
|
432
|
+
Ai_arc += 0.5 * (r * r) * dangle
|
|
433
|
+
|
|
434
|
+
Pi = Pi_straight + Pi_arc
|
|
435
|
+
Ai = Ai_straight + Ai_arc
|
|
436
|
+
perimeter_list[i] = Pi
|
|
437
|
+
area_list[i] = Ai
|
|
438
|
+
|
|
439
|
+
# ----- dA_poly/dh, dP_poly/dh for v1 -----
|
|
440
|
+
for j in range(E):
|
|
441
|
+
# V2-R, V0-R
|
|
442
|
+
v2x = vertices_all[v2_idx[j], 0]; v2y = vertices_all[v2_idx[j], 1]
|
|
443
|
+
v0x = vertices_all[v0_idx[j], 0]; v0y = vertices_all[v0_idx[j], 1]
|
|
444
|
+
v2mx = v2x - cx; v2my = v2y - cy
|
|
445
|
+
v0mx = v0x - cx; v0my = v0y - cy
|
|
446
|
+
|
|
447
|
+
# use predeclared p2x,p2y,p0x,p0y
|
|
448
|
+
perp(v2mx, v2my, &p2x, &p2y)
|
|
449
|
+
perp(v0mx, v0my, &p0x, &p0y)
|
|
450
|
+
dAi_v1_x = -0.5 * p2x + 0.5 * p0x
|
|
451
|
+
dAi_v1_y = -0.5 * p2y + 0.5 * p0y
|
|
452
|
+
|
|
453
|
+
dPi_v1_x = 0.0; dPi_v1_y = 0.0
|
|
454
|
+
|
|
455
|
+
# current edge j: between v1 and v2
|
|
456
|
+
if mask_str[j]:
|
|
457
|
+
v1x = vertices_all[v1_idx[j], 0]; v1y = vertices_all[v1_idx[j], 1]
|
|
458
|
+
s12x = v1x - v2x; s12y = v1y - v2y
|
|
459
|
+
l12 = (s12x*s12x + s12y*s12y) ** 0.5
|
|
460
|
+
if l12 > 0.0:
|
|
461
|
+
dPi_v1_x += s12x / l12
|
|
462
|
+
dPi_v1_y += s12y / l12
|
|
463
|
+
|
|
464
|
+
# previous edge j-1: between v0 and v1
|
|
465
|
+
vprev = (j - 1 + E) % E
|
|
466
|
+
mask_prev_str[j] = mask_str[vprev]
|
|
467
|
+
if mask_prev_str[j]:
|
|
468
|
+
v0x = vertices_all[v0_idx[j], 0]; v0y = vertices_all[v0_idx[j], 1]
|
|
469
|
+
v1x = vertices_all[v1_idx[j], 0]; v1y = vertices_all[v1_idx[j], 1]
|
|
470
|
+
s10x = v1x - v0x; s10y = v1y - v0y
|
|
471
|
+
l10 = (s10x*s10x + s10y*s10y) ** 0.5
|
|
472
|
+
if l10 > 0.0:
|
|
473
|
+
dPi_v1_x += s10x / l10
|
|
474
|
+
dPi_v1_y += s10y / l10
|
|
475
|
+
|
|
476
|
+
vidx = v1_idx[j]
|
|
477
|
+
dA_poly_dh[vidx, 0] += (Ai - A0) * dAi_v1_x
|
|
478
|
+
dA_poly_dh[vidx, 1] += (Ai - A0) * dAi_v1_y
|
|
479
|
+
dP_poly_dh[vidx, 0] += (Pi - P0) * dPi_v1_x
|
|
480
|
+
dP_poly_dh[vidx, 1] += (Pi - P0) * dPi_v1_y
|
|
481
|
+
|
|
482
|
+
# ----- arc endpoint sensitivities -----
|
|
483
|
+
for j in range(E):
|
|
484
|
+
if mask_arc[j]:
|
|
485
|
+
# v1 endpoint
|
|
486
|
+
k1 = v1_idx[j] - num_vertices_ext
|
|
487
|
+
if k1 >= 0:
|
|
488
|
+
da1_full = 0.5 * (r*r) * (1.0 - cos(dangle_full[j]))
|
|
489
|
+
outer_row = k1
|
|
490
|
+
which_col = 1 if vertex_out_points[outer_row, 1] <= i else 0
|
|
491
|
+
vertex_out_da_dtheta[outer_row, which_col] = da1_full
|
|
492
|
+
vertex_out_dl_dtheta[outer_row, which_col] = r
|
|
493
|
+
|
|
494
|
+
# v2 endpoint
|
|
495
|
+
k2 = v2_idx[j] - num_vertices_ext
|
|
496
|
+
if k2 >= 0:
|
|
497
|
+
da2_full = -0.5 * (r*r) * (1.0 - cos(dangle_full[j]))
|
|
498
|
+
outer_row = k2
|
|
499
|
+
which_col = 1 if vertex_out_points[outer_row, 1] <= i else 0
|
|
500
|
+
vertex_out_da_dtheta[outer_row, which_col] = da2_full
|
|
501
|
+
vertex_out_dl_dtheta[outer_row, which_col] = -r
|
|
502
|
+
|
|
503
|
+
return (vertex_out_da_dtheta_np,
|
|
504
|
+
vertex_out_dl_dtheta_np,
|
|
505
|
+
dA_poly_dh_np,
|
|
506
|
+
dP_poly_dh_np,
|
|
507
|
+
area_list_np,
|
|
508
|
+
perimeter_list_np)
|