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
@@ -0,0 +1,194 @@
1
+ """Iso-contour extraction from structured 2D scalar fields.
2
+
3
+ Utilities for extracting contour polylines and polygon regions from gridded
4
+ data when building mesh domains from bathymetry or other scalar fields."""
5
+ import matplotlib.pyplot as plt
6
+ import numpy as np
7
+ from scipy.interpolate import griddata
8
+ from shapely.geometry import Polygon
9
+
10
+
11
+ def getiso(xpos, ypos, zdat, ilev, filt=0.0):
12
+ """An iso-contour from a structured 2D dataset.
13
+
14
+ Parameters
15
+ ----------
16
+ xpos, ypos : ndarray of shape (N, M)
17
+ Grid coordinates (same shape as ``zdat``).
18
+ zdat : ndarray of shape (N, M)
19
+ Scalar field values.
20
+ ilev : float
21
+ Iso-contour level.
22
+ filt : float, optional
23
+ Minimum bounding-box extent in x and y for a contour segment to be
24
+ kept. Segments smaller than ``filt`` in either direction are dropped.
25
+ Default is 0 (keep all).
26
+
27
+ Returns
28
+ -------
29
+ node : ndarray of shape (K, 2)
30
+ Vertex coordinates of contour polylines.
31
+ edge : ndarray of shape (E, 2), dtype int
32
+ Edge list (vertex indices) defining the contour PSLG.
33
+
34
+ References
35
+ ----------
36
+ Translation of the MESH2D function ``getiso``.
37
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
38
+ """
39
+ fig, ax = plt.subplots()
40
+ cs = ax.contour(xpos, ypos, zdat, levels=[ilev])
41
+
42
+ # Matplotlib < 3.9 exposes line contours via cs.collections; >= 3.9 uses
43
+ # cs.allsegs (collections was removed in 3.10).
44
+ try:
45
+ collections = cs.collections
46
+ except AttributeError:
47
+ class DummyCollection:
48
+ def __init__(self, segs):
49
+ self.paths = [type("PathLike", (), {"vertices": seg})() for seg in segs]
50
+
51
+ def get_paths(self):
52
+ return self.paths
53
+
54
+ collections = [DummyCollection(segs) for segs in cs.allsegs if segs]
55
+
56
+ plt.close(fig)
57
+
58
+ node = []
59
+ edge = []
60
+ for collection in collections:
61
+ for path in collection.get_paths():
62
+ ppts = path.vertices
63
+ numc = ppts.shape[0]
64
+
65
+ pmin = ppts.min(axis=0)
66
+ pmax = ppts.max(axis=0)
67
+ pdel = pmax - pmin
68
+
69
+ if np.min(pdel) >= filt:
70
+ if np.allclose(ppts[0], ppts[-1]):
71
+ enew = np.vstack(
72
+ [
73
+ np.column_stack(
74
+ [np.arange(0, numc - 1), np.arange(1, numc)]
75
+ ),
76
+ [numc - 1, 0],
77
+ ]
78
+ )
79
+ else:
80
+ enew = np.column_stack([np.arange(0, numc - 1), np.arange(1, numc)])
81
+
82
+ offset = len(node)
83
+ enew = enew + offset
84
+
85
+ node.extend(ppts.tolist())
86
+ edge.extend(enew.tolist())
87
+
88
+ node = np.array(node)
89
+ edge = np.array(edge, dtype=int)
90
+
91
+ return node, edge
92
+
93
+
94
+ def getiso_polygon(x, y, z, zmax=None, grid_res=None):
95
+ """Polygon regions from a 2D scalar field by thresholding.
96
+
97
+ Builds filled contours for regions where ``z <= zmax`` (or the zero level
98
+ when ``zmax`` is ``None``) and returns valid Shapely polygons with holes
99
+ resolved.
100
+
101
+ Parameters
102
+ ----------
103
+ x, y : ndarray
104
+ 1D or 2D arrays defining grid coordinates (must match ``z``).
105
+ z : ndarray
106
+ Scalar field values.
107
+ zmax : float, optional
108
+ Threshold value. Polygons enclose regions where ``z <= zmax``. If
109
+ ``None``, the zero level is used.
110
+ grid_res : int, optional
111
+ Grid resolution used when ``x`` and ``y`` are 1D scattered points.
112
+ If ``None``, inferred from ``len(z)``.
113
+
114
+ Returns
115
+ -------
116
+ polygons : list of shapely.geometry.Polygon or None
117
+ Polygons sorted by area with hole geometry attached. ``None`` if no
118
+ valid region is found.
119
+
120
+ Notes
121
+ -----
122
+ Filled-contour paths are compound: one path holds the outer boundary plus
123
+ hole rings as separate closed sub-rings. ``Path.to_polygons()`` splits
124
+ those sub-rings; using ``path.vertices`` alone would concatenate rings
125
+ into an invalid polygon.
126
+
127
+ References
128
+ ----------
129
+ Translation of the MESH2D function ``getiso``.
130
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
131
+ """
132
+ if x.ndim == 1 and y.ndim == 1 and z.ndim == 1:
133
+ if grid_res is None:
134
+ grid_res = int(np.sqrt(len(z)))
135
+
136
+ dx = (np.max(x) - np.min(x)) / (grid_res - 1)
137
+ dy = (np.max(y) - np.min(y)) / (grid_res - 1)
138
+
139
+ xi = np.arange(np.min(x) - dx / 2, np.max(x) + dx / 2 + dx, dx)
140
+ yi = np.arange(np.min(y) - dy / 2, np.max(y) + dy / 2 + dy, dy)
141
+
142
+ X, Y = np.meshgrid(xi, yi)
143
+
144
+ Z = griddata((x, y), z, (X, Y), method="linear")
145
+
146
+ z = np.nan_to_num(Z, nan=np.nanmedian(z))
147
+ else:
148
+ X, Y = x, y
149
+
150
+ if z.ndim == 2 and X.ndim == 1 and Y.ndim == 1:
151
+ X, Y = np.meshgrid(X, Y)
152
+
153
+ if X.shape != Y.shape or X.shape != z.shape:
154
+ raise ValueError("Inconsistent array shapes between x, y, z.")
155
+
156
+ new_mask = np.full(z.shape, 1)
157
+ if zmax is not None:
158
+ new_mask[z > zmax] = -1
159
+
160
+ fig, ax = plt.subplots()
161
+ cs = ax.contourf(X, Y, new_mask, levels=[0, 1])
162
+
163
+ # Matplotlib < 3.9: one PathCollection per level (cs.collections, removed
164
+ # in 3.10). >= 3.9: cs.get_paths() returns one compound path per level.
165
+ try:
166
+ paths = [p for c in cs.collections for p in c.get_paths()]
167
+ except AttributeError:
168
+ paths = list(cs.get_paths())
169
+
170
+ plt.close(fig)
171
+
172
+ polygons = []
173
+ for path in paths:
174
+ for pts in path.to_polygons():
175
+ pts = np.asarray(pts)
176
+ if pts.shape[0] < 4:
177
+ continue
178
+ poly = Polygon(pts)
179
+ if poly.is_valid and not poly.is_empty and poly.area > 0:
180
+ polygons.append(poly)
181
+
182
+ if not polygons:
183
+ return None
184
+
185
+ polygons.sort(key=lambda p: p.area, reverse=True)
186
+
187
+ final_polys = []
188
+ while polygons:
189
+ outer = polygons.pop(0)
190
+ holes = [p.exterior.coords for p in polygons if p.within(outer)]
191
+ polygons = [p for p in polygons if not p.within(outer)]
192
+ final_polys.append(Polygon(outer.exterior.coords, holes))
193
+
194
+ return final_polys