bluemesh2d 0.1.1.dev0__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.
Files changed (74) hide show
  1. bluemesh2d/_version.py +24 -0
  2. bluemesh2d/aabb_tree/findball.py +121 -0
  3. bluemesh2d/aabb_tree/findtria.py +299 -0
  4. bluemesh2d/aabb_tree/maketree.py +147 -0
  5. bluemesh2d/aabb_tree/mapvert.py +72 -0
  6. bluemesh2d/aabb_tree/queryset.py +83 -0
  7. bluemesh2d/aabb_tree/scantree.py +124 -0
  8. bluemesh2d/dependencies.py +83 -0
  9. bluemesh2d/feedback.py +142 -0
  10. bluemesh2d/geom_util/boundary_util.py +216 -0
  11. bluemesh2d/geom_util/getiso.py +194 -0
  12. bluemesh2d/geom_util/poly_util.py +795 -0
  13. bluemesh2d/geom_util/proj_util.py +250 -0
  14. bluemesh2d/geomesh_util/border_util.py +283 -0
  15. bluemesh2d/geomesh_util/depth_field.py +333 -0
  16. bluemesh2d/geomesh_util/grd_util.py +1000 -0
  17. bluemesh2d/geomesh_util/interpolation_mesh.py +318 -0
  18. bluemesh2d/geomesh_util/merge_circumcenters.py +268 -0
  19. bluemesh2d/geomesh_util/water_polygon.py +406 -0
  20. bluemesh2d/hfun_util/build_hfun.py +589 -0
  21. bluemesh2d/hfun_util/hfun_dispersion.py +96 -0
  22. bluemesh2d/hfun_util/lfshfn.py +110 -0
  23. bluemesh2d/hfun_util/limhfn.py +65 -0
  24. bluemesh2d/hfun_util/make_constant_hfun.py +32 -0
  25. bluemesh2d/hfun_util/make_depth_hfun.py +51 -0
  26. bluemesh2d/hfun_util/smooth_and_precomput.py +188 -0
  27. bluemesh2d/hfun_util/trihfn.py +84 -0
  28. bluemesh2d/hjac_util/limgrad.py +105 -0
  29. bluemesh2d/mesh_ball/cdtbal1.py +38 -0
  30. bluemesh2d/mesh_ball/cdtbal2.py +104 -0
  31. bluemesh2d/mesh_ball/inv_2x2.py +61 -0
  32. bluemesh2d/mesh_ball/inv_3x3.py +72 -0
  33. bluemesh2d/mesh_ball/pwrbal2.py +134 -0
  34. bluemesh2d/mesh_ball/tribal2.py +27 -0
  35. bluemesh2d/mesh_cost/relhfn.py +62 -0
  36. bluemesh2d/mesh_cost/triang.py +59 -0
  37. bluemesh2d/mesh_cost/triarea.py +51 -0
  38. bluemesh2d/mesh_cost/trideg.py +44 -0
  39. bluemesh2d/mesh_cost/triscr.py +43 -0
  40. bluemesh2d/mesh_file/bnd_util.py +664 -0
  41. bluemesh2d/mesh_file/loadmsh.py +165 -0
  42. bluemesh2d/mesh_file/ugrid.py +308 -0
  43. bluemesh2d/mesh_util/cfmtri.py +118 -0
  44. bluemesh2d/mesh_util/deltri.py +131 -0
  45. bluemesh2d/mesh_util/idxtri.py +53 -0
  46. bluemesh2d/mesh_util/isfeat.py +90 -0
  47. bluemesh2d/mesh_util/minlen.py +46 -0
  48. bluemesh2d/mesh_util/setset.py +49 -0
  49. bluemesh2d/mesh_util/tricon.py +90 -0
  50. bluemesh2d/mesh_util/tridiv.py +187 -0
  51. bluemesh2d/meshgen.py +202 -0
  52. bluemesh2d/ortho_merge/constants.py +27 -0
  53. bluemesh2d/ortho_merge/geometry.py +64 -0
  54. bluemesh2d/ortho_merge/ortho_merge_iter.py +812 -0
  55. bluemesh2d/ortho_merge/orthogonalize.py +2989 -0
  56. bluemesh2d/pipeline.py +277 -0
  57. bluemesh2d/poly_data/airfoil.msh +958 -0
  58. bluemesh2d/poly_data/channel.msh +212 -0
  59. bluemesh2d/poly_data/islands.msh +13819 -0
  60. bluemesh2d/poly_data/lake.msh +612 -0
  61. bluemesh2d/poly_data/river.msh +690 -0
  62. bluemesh2d/poly_test/inpoly.py +105 -0
  63. bluemesh2d/poly_test/inpoly_mat.py +104 -0
  64. bluemesh2d/refine.py +972 -0
  65. bluemesh2d/smood.py +623 -0
  66. bluemesh2d/smooth.py +522 -0
  67. bluemesh2d/tricost.py +426 -0
  68. bluemesh2d/tridemo.py +723 -0
  69. bluemesh2d/triread.py +53 -0
  70. bluemesh2d-0.1.1.dev0.dist-info/METADATA +139 -0
  71. bluemesh2d-0.1.1.dev0.dist-info/RECORD +74 -0
  72. bluemesh2d-0.1.1.dev0.dist-info/WHEEL +5 -0
  73. bluemesh2d-0.1.1.dev0.dist-info/licenses/LICENSE +674 -0
  74. bluemesh2d-0.1.1.dev0.dist-info/top_level.txt +1 -0
bluemesh2d/_version.py ADDED
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.1.1.dev0'
22
+ __version_tuple__ = version_tuple = (0, 1, 1, 'dev0')
23
+
24
+ __commit_id__ = commit_id = 'gaa72c9ba7'
@@ -0,0 +1,121 @@
1
+ import numpy as np
2
+
3
+ from .maketree import maketree
4
+ from .mapvert import mapvert
5
+ from .queryset import queryset
6
+
7
+
8
+ def findball(bb, pp, tr=None, op=None):
9
+ """Find d-dimensional balls intersecting query points.
10
+
11
+ Parameters
12
+ ----------
13
+ bb : ndarray of shape (M, ND + 1)
14
+ Ball definitions: center coordinates in the first ``ND`` columns and
15
+ squared radius in the last column.
16
+ pp : ndarray of shape (P, ND)
17
+ Query points.
18
+ tr : dict, optional
19
+ Precomputed AABB tree from a prior call; reuse only if ``bb`` is
20
+ unchanged.
21
+ op : dict, optional
22
+ Options passed to :func:`maketree` when building the tree.
23
+
24
+ Returns
25
+ -------
26
+ bp : ndarray of shape (P, 2)
27
+ Pointer ranges into ``bj`` for each query point; ``bp[i, 0] == 0``
28
+ when point ``i`` lies outside all balls.
29
+ bj : ndarray
30
+ Flattened list of intersecting ball indices.
31
+ tr : dict
32
+ AABB tree used for the query; may be reused on later calls.
33
+
34
+ References
35
+ ----------
36
+ Translation of the MESH2D function ``FINDBALL``.
37
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
38
+ """
39
+
40
+ bp, bj = np.array([]), np.array([])
41
+
42
+ if bb is None or pp is None:
43
+ raise ValueError("findball:incorrectNumInputs (need at least bb, pp)")
44
+
45
+ bb = np.asarray(bb, dtype=float)
46
+ pp = np.asarray(pp, dtype=float)
47
+
48
+ if bb.ndim != 2 or bb.shape[1] < 3:
49
+ raise ValueError("findball:incorrectDimensions (bb must be (B,ND+1))")
50
+ if pp.ndim != 2 or bb.shape[1] != pp.shape[1] + 1:
51
+ raise ValueError("findball:incorrectDimensions (pp must be (P,ND))")
52
+
53
+ if tr is not None and not isinstance(tr, dict):
54
+ raise TypeError("findball:incorrectInputClass (tr must be struct/dict)")
55
+ if op is not None and not isinstance(op, dict):
56
+ raise TypeError("findball:incorrectInputClass (op must be struct/dict)")
57
+
58
+ if bb.size == 0:
59
+ return bp, bj, tr
60
+
61
+ if tr is None:
62
+ nd = pp.shape[1]
63
+ rs = np.sqrt(bb[:, nd])[:, None] # radii
64
+ rs = np.tile(rs, (1, nd))
65
+ ab = np.hstack([bb[:, :nd] - rs, bb[:, :nd] + rs]) # aabb
66
+ tr = maketree(ab, op)
67
+
68
+ tm, _ = mapvert(tr, pp)
69
+
70
+ bi, ip, bj = queryset(tr, tm, ballkern, pp, bb)
71
+
72
+ bp = np.zeros((pp.shape[0], 2), dtype=int)
73
+ bp[:, 1] = -1
74
+ if bi.size > 0:
75
+ bp[bi, :] = ip
76
+
77
+ return bp, bj, tr
78
+
79
+
80
+ def ballkern(pk, bk, pp, bb):
81
+ """Test ball-point intersections within an AABB tree tile.
82
+
83
+ Parameters
84
+ ----------
85
+ pk : ndarray
86
+ Indices of query points in the tile.
87
+ bk : ndarray
88
+ Indices of candidate balls in the tile.
89
+ pp : ndarray of shape (P, ND)
90
+ Query point coordinates.
91
+ bb : ndarray of shape (B, ND + 1)
92
+ Ball centers and squared radii.
93
+
94
+ Returns
95
+ -------
96
+ ip : ndarray
97
+ Query point indices with an intersection.
98
+ ib : ndarray
99
+ Matching ball indices.
100
+
101
+ References
102
+ ----------
103
+ Translation of the MESH2D function ``BALLKERN``.
104
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
105
+ """
106
+ mp = len(pk)
107
+ mb = len(bk)
108
+ nd = pp.shape[1]
109
+
110
+ bk_tiled = np.tile(bk, mp)
111
+ pk_tiled = np.repeat(pk, mb)
112
+
113
+ diff = pp[pk_tiled, :] - bb[bk_tiled, :nd]
114
+ dd = np.sum(diff**2, axis=1)
115
+
116
+ inside = dd <= bb[bk_tiled, nd]
117
+
118
+ ip = pk_tiled[inside]
119
+ ib = bk_tiled[inside]
120
+
121
+ return ip, ib
@@ -0,0 +1,299 @@
1
+ import numpy as np
2
+
3
+ from .maketree import maketree
4
+ from .mapvert import mapvert
5
+ from .queryset import queryset
6
+
7
+
8
+ def findtria(pp, tt, pj, tree=None, options=None):
9
+ """Find d-dimensional simplexes intersecting query points.
10
+
11
+ Parameters
12
+ ----------
13
+ pp : ndarray of shape (N, ND)
14
+ Vertex coordinates.
15
+ tt : ndarray of shape (NS, M)
16
+ Simplex connectivity; ``M=3`` for triangles, ``M=4`` for tetrahedra.
17
+ pj : ndarray of shape (NP, ND)
18
+ Query points.
19
+ tree : dict, optional
20
+ Precomputed AABB tree from a prior call; reuse only if ``pp`` and
21
+ ``tt`` are unchanged.
22
+ options : dict, optional
23
+ Options passed to :func:`maketree` when building the tree.
24
+
25
+ Returns
26
+ -------
27
+ tp : ndarray of shape (NP, 2)
28
+ Pointer ranges into ``tj`` for each query point; ``tp[i, 0] == 0``
29
+ when point ``i`` has no match.
30
+ tj : ndarray
31
+ Flattened list of intersecting simplex indices.
32
+ tree : dict
33
+ AABB tree used for the query; may be reused on later calls.
34
+
35
+ Notes
36
+ -----
37
+ The simplex collection need not form a conforming triangulation. To pick
38
+ one simplex per query point, take ``tj[tp[mask, 0]]`` where
39
+ ``mask = tp[:, 0] > 0``.
40
+
41
+ References
42
+ ----------
43
+ Translation of the MESH2D function ``FINDTRIA``.
44
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
45
+ """
46
+
47
+ if not (
48
+ isinstance(pp, np.ndarray)
49
+ and isinstance(tt, np.ndarray)
50
+ and isinstance(pj, np.ndarray)
51
+ ):
52
+ raise TypeError("pp, tt, and pj must be numpy arrays.")
53
+
54
+ if pj.size == 0:
55
+ return np.zeros((0, 2), dtype=int), np.array([], dtype=int), tree
56
+
57
+ if pp.ndim != 2 or tt.ndim != 2 or pj.ndim != 2:
58
+ raise ValueError("Inputs must be 2D arrays.")
59
+
60
+ if pp.shape[1] < 2 or pp.shape[1] > tt.shape[1]:
61
+ raise ValueError("Incorrect input dimensions.")
62
+
63
+ if tt.shape[1] < 3:
64
+ raise ValueError("Triangles must have at least 3 vertices.")
65
+
66
+ if pj.shape[1] != pp.shape[1]:
67
+ raise ValueError("pj and pp must have the same dimensionality.")
68
+
69
+ if tree is None:
70
+ bi = pp[tt[:, 0], :].copy()
71
+ bj = pp[tt[:, 0], :].copy()
72
+ for ii in range(1, tt.shape[1]):
73
+ bi = np.minimum(bi, pp[tt[:, ii], :])
74
+ bj = np.maximum(bj, pp[tt[:, ii], :])
75
+ bb = np.hstack([bi, bj])
76
+ tree = maketree(bb, options) # compute aabb-tree
77
+
78
+ tm, _ = mapvert(tree, pj)
79
+
80
+ x0 = np.min(pp, axis=0)
81
+ x1 = np.max(pp, axis=0)
82
+ rt = np.prod(x1 - x0) * np.finfo(float).eps ** 0.8
83
+
84
+ ti, ip, tj = queryset(tree, tm, triakern, pj, pp, tt, rt)
85
+
86
+ tp = np.zeros((pj.shape[0], 2), dtype=int)
87
+ tp[:, 1] = -1
88
+ if ti.size == 0:
89
+ return tp, tj, tree
90
+ tp[ti, :] = ip
91
+
92
+ return tp, tj, tree
93
+
94
+
95
+ def triakern(pk, tk, pi, pp, tt, rt):
96
+ """Test point/simplex intersections within an AABB tree tile.
97
+
98
+ Parameters
99
+ ----------
100
+ pk : ndarray
101
+ Indices of query points in the tile.
102
+ tk : ndarray
103
+ Indices of candidate simplexes in the tile.
104
+ pi : ndarray of shape (P, ND)
105
+ Query point coordinates.
106
+ pp : ndarray of shape (V, ND)
107
+ Vertex coordinates.
108
+ tt : ndarray of shape (T, nv)
109
+ Simplex connectivity (0-based vertex indices).
110
+ rt : float
111
+ Relative tolerance for point-in-simplex tests.
112
+
113
+ Returns
114
+ -------
115
+ ip : ndarray
116
+ Query point indices with an intersection.
117
+ it : ndarray
118
+ Matching simplex indices.
119
+
120
+ References
121
+ ----------
122
+ Translation of the MESH2D function ``TRIAKERN``.
123
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
124
+ """
125
+ mp = len(pk)
126
+ mt = len(tk)
127
+
128
+ pk = np.repeat(pk, mt)
129
+ tk = np.tile(tk, mp)
130
+
131
+ n_vertices = tt.shape[1]
132
+
133
+ if n_vertices == 3:
134
+ inside = intria2(pp, tt[tk, :], pi[pk, :], rt)
135
+ elif n_vertices == 4:
136
+ inside = intria3(pp, tt[tk, :], pi[pk, :], rt)
137
+ else:
138
+ ii, jj = intrian(pp, tt[tk, :], pi[pk, :])
139
+ ip = pk[ii]
140
+ it = tk[jj]
141
+ return ip, it
142
+
143
+ ip = pk[inside]
144
+ it = tk[inside]
145
+ return ip, it
146
+
147
+
148
+ def intria2(pp, tt, pi, rt):
149
+ """Test whether query points lie inside 2-simplexes (triangles).
150
+
151
+ Parameters
152
+ ----------
153
+ pp : ndarray of shape (V, 2)
154
+ Vertex coordinates.
155
+ tt : ndarray of shape (T, 3)
156
+ Triangle connectivity (0-based vertex indices).
157
+ pi : ndarray of shape (P, 2)
158
+ Query points (one per triangle row in ``tt``).
159
+ rt : float
160
+ Relative tolerance for point-in-triangle tests.
161
+
162
+ Returns
163
+ -------
164
+ inside : ndarray of bool, shape (T,)
165
+ ``True`` where the corresponding query point lies inside the triangle.
166
+
167
+ References
168
+ ----------
169
+ Translation of the MESH2D function ``INTRIA2``.
170
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
171
+ """
172
+ t1, t2, t3 = tt[:, 0], tt[:, 1], tt[:, 2]
173
+ vi = pp[t1, :] - pi
174
+ vj = pp[t2, :] - pi
175
+ vk = pp[t3, :] - pi
176
+ aa = np.zeros((tt.shape[0], 3))
177
+ aa[:, 0] = vi[:, 0] * vj[:, 1] - vj[:, 0] * vi[:, 1]
178
+ aa[:, 1] = vj[:, 0] * vk[:, 1] - vk[:, 0] * vj[:, 1]
179
+ aa[:, 2] = vk[:, 0] * vi[:, 1] - vi[:, 0] * vk[:, 1]
180
+ rt2 = rt**2
181
+ inside = (
182
+ (aa[:, 0] * aa[:, 1] >= -rt2)
183
+ & (aa[:, 1] * aa[:, 2] >= -rt2)
184
+ & (aa[:, 2] * aa[:, 0] >= -rt2)
185
+ )
186
+
187
+ return inside
188
+
189
+
190
+ def intria3(pp, tt, pi, rt):
191
+ """Test whether query points lie inside 3-simplexes (tetrahedra).
192
+
193
+ Parameters
194
+ ----------
195
+ pp : ndarray of shape (V, 3)
196
+ Vertex coordinates.
197
+ tt : ndarray of shape (T, 4)
198
+ Tetrahedron connectivity (0-based vertex indices).
199
+ pi : ndarray of shape (P, 3)
200
+ Query points (one per tetrahedron row in ``tt``).
201
+ rt : float
202
+ Relative tolerance for point-in-tetrahedron tests.
203
+
204
+ Returns
205
+ -------
206
+ inside : ndarray of bool, shape (T,)
207
+ ``True`` where the corresponding query point lies inside the tetrahedron.
208
+
209
+ References
210
+ ----------
211
+ Translation of the MESH2D function ``INTRIA3``.
212
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
213
+ """
214
+ t1, t2, t3, t4 = tt[:, 0], tt[:, 1], tt[:, 2], tt[:, 3]
215
+ v1 = pi - pp[t1, :]
216
+ v2 = pi - pp[t2, :]
217
+ v3 = pi - pp[t3, :]
218
+ v4 = pi - pp[t4, :]
219
+ aa = np.zeros((tt.shape[0], 4))
220
+ aa[:, 0] = (
221
+ v1[:, 0] * (v2[:, 1] * v3[:, 2] - v2[:, 2] * v3[:, 1])
222
+ - v1[:, 1] * (v2[:, 0] * v3[:, 2] - v2[:, 2] * v3[:, 0])
223
+ + v1[:, 2] * (v2[:, 0] * v3[:, 1] - v2[:, 1] * v3[:, 0])
224
+ )
225
+ aa[:, 1] = (
226
+ v1[:, 0] * (v4[:, 1] * v2[:, 2] - v4[:, 2] * v2[:, 1])
227
+ - v1[:, 1] * (v4[:, 0] * v2[:, 2] - v4[:, 2] * v2[:, 0])
228
+ + v1[:, 2] * (v4[:, 0] * v2[:, 1] - v4[:, 1] * v2[:, 0])
229
+ )
230
+ aa[:, 2] = (
231
+ v2[:, 0] * (v4[:, 1] * v3[:, 2] - v4[:, 2] * v3[:, 1])
232
+ - v2[:, 1] * (v4[:, 0] * v3[:, 2] - v4[:, 2] * v3[:, 0])
233
+ + v2[:, 2] * (v4[:, 0] * v3[:, 1] - v4[:, 1] * v3[:, 0])
234
+ )
235
+ aa[:, 3] = (
236
+ v3[:, 0] * (v4[:, 1] * v1[:, 2] - v4[:, 2] * v1[:, 1])
237
+ - v3[:, 1] * (v4[:, 0] * v1[:, 2] - v4[:, 2] * v1[:, 0])
238
+ + v3[:, 2] * (v4[:, 0] * v1[:, 1] - v4[:, 1] * v1[:, 0])
239
+ )
240
+ rt2 = rt**2
241
+ inside = (
242
+ (aa[:, 0] * aa[:, 1] >= -rt2)
243
+ & (aa[:, 0] * aa[:, 2] >= -rt2)
244
+ & (aa[:, 0] * aa[:, 3] >= -rt2)
245
+ & (aa[:, 1] * aa[:, 2] >= -rt2)
246
+ & (aa[:, 1] * aa[:, 3] >= -rt2)
247
+ & (aa[:, 2] * aa[:, 3] >= -rt2)
248
+ )
249
+
250
+ return inside
251
+
252
+
253
+ def intrian(pp, tt, pi):
254
+ """Locate query points in general n-simplexes via barycentric coordinates.
255
+
256
+ Parameters
257
+ ----------
258
+ pp : ndarray of shape (V, ND)
259
+ Vertex coordinates.
260
+ tt : ndarray of shape (T, nv)
261
+ Simplex connectivity (0-based vertex indices).
262
+ pi : ndarray of shape (P, ND)
263
+ Query points.
264
+
265
+ Returns
266
+ -------
267
+ ii : ndarray
268
+ Indices of query points inside a simplex.
269
+ jj : ndarray
270
+ Indices of simplexes containing those points.
271
+
272
+ References
273
+ ----------
274
+ Translation of the MESH2D function ``INTRIAN``.
275
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
276
+ """
277
+ np_, pd = pi.shape
278
+ nt, td = tt.shape
279
+ # Coefficient matrices for barycentric coord.
280
+ mm = np.zeros((pd, pd, nt))
281
+ for id_ in range(pd):
282
+ for jd in range(pd):
283
+ mm[id_, jd, :] = pp[tt[:, jd], id_] - pp[tt[:, td - 1], id_]
284
+ # Solve linear systems for barycentric coord.
285
+ xx = np.zeros((pd, np_, nt))
286
+ vp = np.zeros((pd, np_))
287
+
288
+ for ti in range(nt):
289
+ for id_ in range(pd):
290
+ vp[id_, :] = pi[:, id_] - pp[tt[ti, td - 1], id_]
291
+ # Solve linear systems (LU equivalent)
292
+ xx[:, :, ti] = np.linalg.solve(mm[:, :, ti], vp)
293
+ # PI is internal if coord. have same sign
294
+ in_mask = np.all(xx >= -(np.finfo(float).eps ** 0.8), axis=0) & (
295
+ np.sum(xx, axis=0) <= 1.0 + np.finfo(float).eps ** 0.8
296
+ )
297
+ # Find lists of matching points/simplexes
298
+ ii, jj = np.where(in_mask.T)
299
+ return ii, jj
@@ -0,0 +1,147 @@
1
+ import numpy as np
2
+
3
+
4
+ def maketree(rp, op=None):
5
+ """Assemble an AABB search tree for a collection of (hyper-)rectangles.
6
+
7
+ Parameters
8
+ ----------
9
+ rp : ndarray of shape (NR, 2*NDIM)
10
+ Rectangles defined as ``[pmin, pmax]``.
11
+ op : dict, optional
12
+ Options: ``"nobj"`` (max rectangles per node, default 32),
13
+ ``"long"`` (relative length tolerance, default 0.75), and ``"vtol"``
14
+ (volume tolerance, default 0.55).
15
+
16
+ Returns
17
+ -------
18
+ tr : dict
19
+ Tree with keys ``"xx"`` (node bounding boxes), ``"ii"`` (parent/child
20
+ indexing), and ``"ll"`` (rectangle indices per node).
21
+
22
+ References
23
+ ----------
24
+ Translation of the MESH2D function ``MAKETREE``.
25
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
26
+ """
27
+
28
+ tr = {"xx": [], "ii": [], "ll": []}
29
+
30
+ if rp is None or len(rp) == 0:
31
+ return tr
32
+
33
+ if not isinstance(rp, np.ndarray):
34
+ raise TypeError("maketree: incorrect input class")
35
+
36
+ if rp.ndim != 2 or rp.shape[1] % 2 != 0:
37
+ raise ValueError("maketree: incorrect input dimensions")
38
+
39
+ NOBJ = 32
40
+ if op is None:
41
+ op = {"nobj": NOBJ, "long": 0.75, "vtol": 0.55}
42
+ else:
43
+ op.setdefault("nobj", NOBJ)
44
+ op.setdefault("long", 0.75)
45
+ op.setdefault("vtol", 0.55)
46
+ nd = rp.shape[1] // 2
47
+ ni = rp.shape[0]
48
+ xl = np.zeros((ni, nd))
49
+ xr = np.zeros((ni, nd))
50
+ ii = np.zeros((ni, 2), dtype=int)
51
+ ll = [None] * ni
52
+ ss = np.zeros(ni, dtype=int)
53
+ lv = np.zeros(rp.shape[1], dtype=bool)
54
+ rv = np.zeros(rp.shape[1], dtype=bool)
55
+ lv[:nd] = True
56
+ rv[nd:] = True
57
+ r0 = np.min(rp[:, lv], axis=0)
58
+ r1 = np.max(rp[:, rv], axis=0)
59
+ rd = np.tile(r1 - r0, (ni, 1))
60
+ rp[:, lv] -= rd * np.power(np.finfo(float).eps, 0.8)
61
+ rp[:, rv] += rd * np.power(np.finfo(float).eps, 0.8)
62
+ rc = 0.5 * (rp[:, lv] + rp[:, rv])
63
+ rd = rp[:, rv] - rp[:, lv]
64
+ ll[0] = np.arange(ni)
65
+ ii[0, :] = 0
66
+ xl[0, :] = np.min(rp[:, lv], axis=0)
67
+ xr[0, :] = np.max(rp[:, rv], axis=0)
68
+ # -- main loop : divide nodes until all constraints satisfied
69
+ ss[0] = 0
70
+ ns = 1
71
+ nn = 1
72
+
73
+ while ns != 0:
74
+ ni_node = ss[ns - 1]
75
+ ns -= 1
76
+ n1 = nn
77
+ n2 = nn + 1
78
+ li = ll[ni_node]
79
+ dd = xr[ni_node, :] - xl[ni_node, :]
80
+ ia = np.argsort(dd)
81
+
82
+ for id in range(nd - 1, -1, -1):
83
+ ax = ia[id]
84
+ mx = dd[ax]
85
+
86
+ il = rd[li, ax] > op["long"] * mx
87
+ lp = li[il] # "long" rectangles
88
+ ls = li[~il] # "short" rectangles
89
+
90
+ if len(lp) < 0.5 * len(ls) and len(lp) < 0.5 * op["nobj"]:
91
+ break
92
+
93
+ # select the split position: take the mean of the set of
94
+ # (non-"long") rectangle centres along axis AX
95
+ if len(ls) == 0:
96
+ continue
97
+
98
+ sp = np.mean(rc[ls, ax])
99
+ i2 = rc[ls, ax] > sp
100
+ l1 = ls[~i2] # "left" rectangles
101
+ l2 = ls[i2] # "right" rectangles
102
+
103
+ if len(l1) <= 1 or len(l2) <= 1:
104
+ continue
105
+
106
+ xl[n1, :] = np.min(rp[l1[:, None], lv], axis=0)
107
+ xr[n1, :] = np.max(rp[l1[:, None], rv], axis=0)
108
+ xl[n2, :] = np.min(rp[l2[:, None], lv], axis=0)
109
+ xr[n2, :] = np.max(rp[l2[:, None], rv], axis=0)
110
+ if len(li) <= op["nobj"]:
111
+ vi = np.prod(xr[ni_node, :] - xl[ni_node, :]) # upper d-dim "vol."
112
+ v1 = np.prod(xr[n1, :] - xl[n1, :]) # lower d-dim "vol."
113
+ v2 = np.prod(xr[n2, :] - xl[n2, :])
114
+
115
+ if v1 + v2 < op["vtol"] * vi:
116
+ ii[n1, 0] = ni_node
117
+ ii[n2, 0] = ni_node
118
+ ii[ni_node, 1] = n1
119
+ ll[ni_node] = lp
120
+ ll[n1] = l1
121
+ ll[n2] = l2
122
+
123
+ ss[ns] = n1
124
+ ss[ns + 1] = n2
125
+ ns += 2
126
+ nn += 2
127
+ else:
128
+ ii[n1, 0] = ni_node
129
+ ii[n2, 0] = ni_node
130
+ ii[ni_node, 1] = n1
131
+ ll[ni_node] = lp
132
+ ll[n1] = l1
133
+ ll[n2] = l2
134
+
135
+ ss[ns] = n1
136
+ ss[ns + 1] = n2
137
+ ns += 2
138
+ nn += 2
139
+ xl = xl[:nn, :]
140
+ xr = xr[:nn, :]
141
+ ii = ii[:nn, :]
142
+ ll = ll[:nn]
143
+ tr["xx"] = np.hstack((xl, xr))
144
+ tr["ii"] = ii
145
+ tr["ll"] = ll
146
+
147
+ return tr
@@ -0,0 +1,72 @@
1
+ import numpy as np
2
+
3
+ from .scantree import scantree
4
+
5
+
6
+ def mapvert(tr, pi):
7
+ """Compute tree-to-vertex and vertex-to-tree mappings for an AABB tree.
8
+
9
+ Parameters
10
+ ----------
11
+ tr : dict
12
+ AABB tree from :func:`maketree`, with keys ``"xx"``, ``"ii"``, and
13
+ ``"ll"``.
14
+ pi : ndarray of shape (NP, NDIM)
15
+ Vertex coordinates.
16
+
17
+ Returns
18
+ -------
19
+ tm : dict
20
+ Tree-to-vertex mapping with keys ``"ii"`` (node indices) and ``"ll"``
21
+ (vertex indices per node).
22
+ im : dict
23
+ Vertex-to-tree mapping with keys ``"ii"`` (vertex indices) and ``"ll"``
24
+ (tree node indices per vertex).
25
+
26
+ References
27
+ ----------
28
+ Translation of the MESH2D function ``MAPVERT``.
29
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
30
+ """
31
+
32
+
33
+ # Call SCANTREE to do the actual work
34
+ results = scantree(tr, pi, partvert)
35
+ if len(results) == 1:
36
+ return results[0], None
37
+ else:
38
+ return results
39
+
40
+
41
+ def partvert(pi, b1, b2):
42
+ """Partition points between two bounding boxes for :func:`scantree`.
43
+
44
+ Parameters
45
+ ----------
46
+ pi : ndarray of shape (N, ND)
47
+ Query points.
48
+ b1, b2 : ndarray of shape (2*ND,)
49
+ Bounding boxes as ``[pmin, pmax]``.
50
+
51
+ Returns
52
+ -------
53
+ j1, j2 : ndarray of bool, shape (N,)
54
+ Masks of points inside ``b1`` and ``b2``, respectively.
55
+
56
+ References
57
+ ----------
58
+ Translation of the MESH2D function ``PARTVERT``.
59
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
60
+ """
61
+ nd = b1.shape[0] // 2
62
+
63
+ j1 = np.ones(pi.shape[0], dtype=bool)
64
+ j2 = np.ones(pi.shape[0], dtype=bool)
65
+
66
+ for ax in range(nd):
67
+ # Remains TRUE if inside bounds along axis AX
68
+ j1 &= (pi[:, ax] >= b1[ax]) & (pi[:, ax] <= b1[ax + nd])
69
+ # Remains TRUE if inside bounds along axis AX
70
+ j2 &= (pi[:, ax] >= b2[ax]) & (pi[:, ax] <= b2[ax + nd])
71
+
72
+ return j1, j2