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.
- bluemesh2d/_version.py +24 -0
- bluemesh2d/aabb_tree/findball.py +121 -0
- bluemesh2d/aabb_tree/findtria.py +299 -0
- bluemesh2d/aabb_tree/maketree.py +147 -0
- bluemesh2d/aabb_tree/mapvert.py +72 -0
- bluemesh2d/aabb_tree/queryset.py +83 -0
- bluemesh2d/aabb_tree/scantree.py +124 -0
- bluemesh2d/dependencies.py +83 -0
- bluemesh2d/feedback.py +142 -0
- bluemesh2d/geom_util/boundary_util.py +216 -0
- bluemesh2d/geom_util/getiso.py +194 -0
- bluemesh2d/geom_util/poly_util.py +795 -0
- bluemesh2d/geom_util/proj_util.py +250 -0
- bluemesh2d/geomesh_util/border_util.py +283 -0
- bluemesh2d/geomesh_util/depth_field.py +333 -0
- bluemesh2d/geomesh_util/grd_util.py +1000 -0
- bluemesh2d/geomesh_util/interpolation_mesh.py +318 -0
- bluemesh2d/geomesh_util/merge_circumcenters.py +268 -0
- bluemesh2d/geomesh_util/water_polygon.py +406 -0
- bluemesh2d/hfun_util/build_hfun.py +589 -0
- bluemesh2d/hfun_util/hfun_dispersion.py +96 -0
- bluemesh2d/hfun_util/lfshfn.py +110 -0
- bluemesh2d/hfun_util/limhfn.py +65 -0
- bluemesh2d/hfun_util/make_constant_hfun.py +32 -0
- bluemesh2d/hfun_util/make_depth_hfun.py +51 -0
- bluemesh2d/hfun_util/smooth_and_precomput.py +188 -0
- bluemesh2d/hfun_util/trihfn.py +84 -0
- bluemesh2d/hjac_util/limgrad.py +105 -0
- bluemesh2d/mesh_ball/cdtbal1.py +38 -0
- bluemesh2d/mesh_ball/cdtbal2.py +104 -0
- bluemesh2d/mesh_ball/inv_2x2.py +61 -0
- bluemesh2d/mesh_ball/inv_3x3.py +72 -0
- bluemesh2d/mesh_ball/pwrbal2.py +134 -0
- bluemesh2d/mesh_ball/tribal2.py +27 -0
- bluemesh2d/mesh_cost/relhfn.py +62 -0
- bluemesh2d/mesh_cost/triang.py +59 -0
- bluemesh2d/mesh_cost/triarea.py +51 -0
- bluemesh2d/mesh_cost/trideg.py +44 -0
- bluemesh2d/mesh_cost/triscr.py +43 -0
- bluemesh2d/mesh_file/bnd_util.py +664 -0
- bluemesh2d/mesh_file/loadmsh.py +165 -0
- bluemesh2d/mesh_file/ugrid.py +308 -0
- bluemesh2d/mesh_util/cfmtri.py +118 -0
- bluemesh2d/mesh_util/deltri.py +131 -0
- bluemesh2d/mesh_util/idxtri.py +53 -0
- bluemesh2d/mesh_util/isfeat.py +90 -0
- bluemesh2d/mesh_util/minlen.py +46 -0
- bluemesh2d/mesh_util/setset.py +49 -0
- bluemesh2d/mesh_util/tricon.py +90 -0
- bluemesh2d/mesh_util/tridiv.py +187 -0
- bluemesh2d/meshgen.py +202 -0
- bluemesh2d/ortho_merge/constants.py +27 -0
- bluemesh2d/ortho_merge/geometry.py +64 -0
- bluemesh2d/ortho_merge/ortho_merge_iter.py +812 -0
- bluemesh2d/ortho_merge/orthogonalize.py +2989 -0
- bluemesh2d/pipeline.py +277 -0
- bluemesh2d/poly_data/airfoil.msh +958 -0
- bluemesh2d/poly_data/channel.msh +212 -0
- bluemesh2d/poly_data/islands.msh +13819 -0
- bluemesh2d/poly_data/lake.msh +612 -0
- bluemesh2d/poly_data/river.msh +690 -0
- bluemesh2d/poly_test/inpoly.py +105 -0
- bluemesh2d/poly_test/inpoly_mat.py +104 -0
- bluemesh2d/refine.py +972 -0
- bluemesh2d/smood.py +623 -0
- bluemesh2d/smooth.py +522 -0
- bluemesh2d/tricost.py +426 -0
- bluemesh2d/tridemo.py +723 -0
- bluemesh2d/triread.py +53 -0
- bluemesh2d-0.1.1.dev0.dist-info/METADATA +139 -0
- bluemesh2d-0.1.1.dev0.dist-info/RECORD +74 -0
- bluemesh2d-0.1.1.dev0.dist-info/WHEEL +5 -0
- bluemesh2d-0.1.1.dev0.dist-info/licenses/LICENSE +674 -0
- 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
|