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/tridemo.py ADDED
@@ -0,0 +1,723 @@
1
+ import os
2
+ import sys
3
+
4
+ import matplotlib.pyplot as plt
5
+ import numpy as np
6
+
7
+ from bluemesh2d.hfun_util.trihfn import trihfn
8
+ from bluemesh2d.hfun_util.lfshfn import lfshfn
9
+ from bluemesh2d.mesh_util.tridiv import tridiv
10
+ from bluemesh2d.mesh_util.idxtri import idxtri
11
+ from bluemesh2d.smooth import smooth
12
+ from bluemesh2d.refine import refine
13
+ from bluemesh2d.triread import triread
14
+ from bluemesh2d.tricost import tricost
15
+
16
+
17
+ def tridemo(demo):
18
+ """Run a MESH2D-style 2D triangulation demo by index.
19
+
20
+ Parameters
21
+ ----------
22
+ demo : int
23
+ Demo index (0–10).
24
+
25
+ Notes
26
+ -----
27
+ Available demos:
28
+
29
+ - 0 : Square with a central square hole (basic PSLG and size constraints).
30
+ - 1 : Effect of the radius–edge threshold ``rho2`` on mesh density/quality.
31
+ - 2 : Compare Delaunay-refinement vs. Frontal-Delaunay ``kind``.
32
+ - 3 : User-defined mesh-size constraints (airfoil, ``lfshfn`` / ``trihfn``).
33
+ - 4 : Hill-climbing smoothing and quality histograms.
34
+ - 5 : Multi-part geometry definitions.
35
+ - 6 : Geometries with internal edge constraints.
36
+ - 7 : Quadtree-style refinement via :func:`~bluemesh2d.mesh_util.tridiv.tridiv`.
37
+ - 8 : Custom mesh-size function (``hfun8``).
38
+ - 9 : Large-scale mesh refinement and optimization (islands).
39
+ - 10 : Medium-scale mesh refinement and optimization (river).
40
+
41
+ References
42
+ ----------
43
+ Translation of the MESH2D function ``TRIDEMO``.
44
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
45
+ """
46
+
47
+ if demo == 0:
48
+ demo0()
49
+ elif demo == 1:
50
+ demo1()
51
+ elif demo == 2:
52
+ demo2()
53
+ elif demo == 3:
54
+ demo3()
55
+ elif demo == 4:
56
+ demo4()
57
+ elif demo == 5:
58
+ demo5()
59
+ elif demo == 6:
60
+ demo6()
61
+ elif demo == 7:
62
+ demo7()
63
+ elif demo == 8:
64
+ demo8()
65
+ elif demo == 9:
66
+ demo9()
67
+ elif demo == 10:
68
+ demo10()
69
+ else:
70
+ raise ValueError("tridemo:invalidSelection - Invalid selection!")
71
+
72
+
73
+ def demo0():
74
+ """Mesh a square domain with a central square hole."""
75
+
76
+ print(
77
+ " A very simple example to start with -- construct a mesh for \n"
78
+ " a simple square domain with a square hole cut from its cen- \n"
79
+ " tre. The geometry is specified as a Planar Straight-Line \n"
80
+ " Graph (PSLG) -- a list of xy coordinates, or 'nodes', and a \n"
81
+ " list of straight-line connections between nodes, or 'edges'.\n"
82
+ " The refine routine is used to build a triangulation of the \n"
83
+ " domain that: (a) conforms to the geometry, and (b) contains \n"
84
+ " only 'nicely' shaped triangles. In the second panel, a mesh \n"
85
+ " that additionally satisfies 'mesh-size' constrains is cons- \n"
86
+ " structed -- "
87
+ )
88
+
89
+ node = np.array(
90
+ [ # list of xy "node" coordinates
91
+ [0, 0], # outer square
92
+ [9, 0],
93
+ [9, 9],
94
+ [0, 9],
95
+ [4, 4], # inner square
96
+ [5, 4],
97
+ [5, 5],
98
+ [4, 5],
99
+ ]
100
+ )
101
+
102
+ edge = (
103
+ np.array(
104
+ [ # list of "edges" between nodes
105
+ [1, 2], # outer square
106
+ [2, 3],
107
+ [3, 4],
108
+ [4, 1],
109
+ [5, 6], # inner square
110
+ [6, 7],
111
+ [7, 8],
112
+ [8, 5],
113
+ ]
114
+ )
115
+ - 1
116
+ )
117
+ opts = {}
118
+ vert, etri, tria, tnum = refine(node, edge, [], opts)
119
+
120
+ plt.figure()
121
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, 0:3], color=[0.2, 0.2, 0.2])
122
+ plt.gca().set_aspect("equal")
123
+ plt.axis("off")
124
+
125
+ hfun = 0.5 # uniform "target" edge-lengths
126
+ vert, etri, tria, tnum = refine(node, edge, [], opts, hfun)
127
+
128
+ plt.figure()
129
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, 0:3], color=[0.2, 0.2, 0.2])
130
+ plt.gca().set_aspect("equal")
131
+ plt.axis("off")
132
+
133
+ plt.draw()
134
+ plt.show()
135
+
136
+
137
+ def demo1():
138
+ """Demonstrate the effect of ``rho2`` on mesh density and quality."""
139
+
140
+ filepath = os.path.dirname(os.path.abspath(__file__))
141
+ meshfile = os.path.join(filepath, "poly_data", "lake.msh")
142
+
143
+ node, edge, _, _ = triread(meshfile)
144
+
145
+ print(
146
+ " The refine routine can be used to build guaranteed-quality \n"
147
+ " Delaunay triangulations for general polygonal geometries in \n"
148
+ " the two-dimensional plane. The 'quality' of elements in the \n"
149
+ " triangulation can be controlled using the 'radius-edge' bo- \n"
150
+ " und RHO2. \n"
151
+ )
152
+
153
+ print("\n")
154
+ print(
155
+ " Setting large values for RHO2, (RHO2 = 1.50 here) generates \n"
156
+ " sparse triangulations with poor worst-case angle bounds. \n"
157
+ )
158
+
159
+ opts = {"kind": "delaunay", "rho2": 1.50}
160
+
161
+ vert, etri, tria, tnum = refine(node, edge, [], opts)
162
+
163
+ plt.figure()
164
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, 0:3], color=[0.2, 0.2, 0.2])
165
+ plt.gca().set_aspect("equal")
166
+ plt.axis("off")
167
+
168
+ plt.title(f"TRIA-MESH: RHO2<=+1.50, |TRIA|={tria.shape[0]}")
169
+
170
+ print(
171
+ " Setting small values for RHO2, (RHO2 = 1.00 here) generates \n"
172
+ " dense triangulations with good worst-case angle bounds. \n"
173
+ )
174
+
175
+ opts = {"kind": "delaunay", "rho2": 1.00}
176
+
177
+ vert, etri, tria, tnum = refine(node, edge, [], opts)
178
+
179
+ plt.figure()
180
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, 0:3], color=[0.2, 0.2, 0.2])
181
+ plt.gca().set_aspect("equal")
182
+ plt.axis("off")
183
+
184
+ plt.title(f"TRIA-MESH: RHO2<=+1.00, |TRIA|={tria.shape[0]}")
185
+
186
+ plt.draw()
187
+ plt.show()
188
+
189
+
190
+ def demo2():
191
+ """Compare Delaunay-refinement and Frontal-Delaunay ``kind`` options."""
192
+
193
+ filepath = os.path.dirname(os.path.abspath(__file__))
194
+ meshfile = os.path.join(filepath, "poly_data", "lake.msh")
195
+
196
+ node, edge, _, _ = triread(meshfile)
197
+
198
+ print(
199
+ " The refine routine supports two Delaunay-based refinement \n"
200
+ " algorithms: a 'standard' Delaunay-refinement type approach, \n"
201
+ " and a 'Frontal-Delaunay' technique. For problems constrain- \n"
202
+ " ed by element 'quality' alone, the Frontal-Delaunay approa- \n"
203
+ " ch typically produces significantly sparser meshes. In both \n"
204
+ " cases, the same worst-case element quality bounds are sati- \n"
205
+ " sfied in a guaranteed manner. \n"
206
+ )
207
+
208
+ opts = {"kind": "delaunay", "rho2": 1.00}
209
+
210
+ vert, etri, tria, tnum = refine(node, edge, [], opts)
211
+
212
+ plt.figure()
213
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, 0:3], color=[0.2, 0.2, 0.2])
214
+ plt.gca().set_aspect("equal")
215
+ plt.axis("off")
216
+
217
+ plt.title(f"TRIA-MESH: KIND=DELAUNAY, |TRIA|={tria.shape[0]}")
218
+
219
+ opts = {"kind": "delfront", "rho2": 1.00}
220
+
221
+ vert, etri, tria, tnum = refine(node, edge, [], opts)
222
+
223
+ plt.figure()
224
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, 0:3], color=[0.2, 0.2, 0.2])
225
+ plt.gca().set_aspect("equal")
226
+ plt.axis("off")
227
+
228
+ plt.title(f"TRIA-MESH: KIND=DELFRONT, |TRIA|={tria.shape[0]}")
229
+
230
+ plt.draw()
231
+ plt.show()
232
+
233
+
234
+ def demo3():
235
+ """Demonstrate user-defined mesh-size constraints on an airfoil geometry."""
236
+
237
+ # équivalent de mfilename('fullpath')
238
+ filepath = os.path.dirname(os.path.abspath(__file__))
239
+ meshfile = os.path.join(filepath, "poly_data", "airfoil.msh")
240
+
241
+ node, edge, _, _ = triread(meshfile)
242
+
243
+ print(
244
+ " Additionally, the refine routine supports size-driven ref- \n"
245
+ " inement, producing meshes that satisfy constraints on elem- \n"
246
+ " ent edge-lengths. The lfshfn routine can be used to create \n"
247
+ " mesh-size functions based on an estimate of the 'local-fea- \n"
248
+ " ture-size' associated with a polygonal domain. The Frontal- \n"
249
+ " Delaunay refinement algorithm discussed in DEMO-2 is espec- \n"
250
+ " ially good at generating high-quality triangulations in the \n"
251
+ " presence of mesh-size constraints. \n"
252
+ )
253
+
254
+ olfs = {"dhdx": 0.15}
255
+
256
+ vlfs, tlfs, hlfs = lfshfn(node, edge, [], olfs)
257
+ slfs = idxtri(vlfs, tlfs)
258
+
259
+ facecolors = np.mean(hlfs[tlfs[:, 0:3]], axis=1)
260
+ plt.figure()
261
+ plt.tripcolor(
262
+ vlfs[:, 0],
263
+ vlfs[:, 1],
264
+ tlfs[:, 0:3],
265
+ facecolors=facecolors,
266
+ shading="flat",
267
+ cmap="viridis",
268
+ )
269
+ plt.gca().set_aspect("equal")
270
+ plt.axis("off")
271
+ plt.title(f"MESH-SIZE: KIND=DELAUNAY, |TRIA|={tlfs.shape[0]}")
272
+
273
+ hfun = trihfn
274
+
275
+ vert, etri, tria, tnum = refine(node, edge, [], {}, hfun, vlfs, tlfs, slfs, hlfs)
276
+
277
+ plt.figure()
278
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, 0:3], color=[0.2, 0.2, 0.2])
279
+ plt.gca().set_aspect("equal")
280
+ plt.axis("off")
281
+
282
+ plt.title(f"TRIA-MESH: KIND=DELFRONT, |TRIA|={tria.shape[0]}")
283
+
284
+ plt.draw()
285
+ plt.show()
286
+
287
+
288
+ def demo4():
289
+ """Demonstrate hill-climbing smoothing and quality histograms."""
290
+
291
+ # Équivalent MATLAB mfilename / fileparts
292
+ filepath = os.path.dirname(os.path.abspath(__file__))
293
+ meshfile = os.path.join(filepath, "poly_data", "airfoil.msh")
294
+
295
+ node, edge, _, _ = triread(meshfile)
296
+
297
+ print(
298
+ " The smooth routine provides iterative mesh 'smoothing' ca- \n"
299
+ " pabilities, seeking to improve triangulation quality by ad- \n"
300
+ " justing the vertex positions and mesh topology. Specifical- \n"
301
+ " ly, a 'hill-climbing' type optimisation is implemented, gu- \n"
302
+ " aranteeing that mesh-quality is improved monotonically. The \n"
303
+ " DRAWSCR routine provides detailed analysis of triangulation \n"
304
+ " quality, plotting histograms of various quality metrics. \n"
305
+ )
306
+
307
+ olfs = {"dhdx": 0.15}
308
+
309
+ vlfs, tlfs, hlfs = lfshfn(node, edge, [], olfs)
310
+ slfs = idxtri(vlfs, tlfs)
311
+
312
+ hfun = trihfn
313
+
314
+ vert, etri, tria, tnum = refine(node, edge, [], {}, hfun, vlfs, tlfs, slfs, hlfs)
315
+
316
+ plt.figure()
317
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, 0:3], color=[0.2, 0.2, 0.2])
318
+ plt.gca().set_aspect("equal")
319
+ plt.axis("off")
320
+
321
+ plt.title(f"MESH-REF.: KIND=DELFRONT, |TRIA|={tria.shape[0]}")
322
+
323
+ vnew, enew, tnew, tnum = smooth(vert, etri, tria, tnum)
324
+
325
+ plt.figure()
326
+ plt.triplot(vnew[:, 0], vnew[:, 1], tnew[:, 0:3], color=[0.2, 0.2, 0.2])
327
+ plt.gca().set_aspect("equal")
328
+ plt.axis("off")
329
+
330
+ plt.title(f"MESH-OPT.: KIND=DELFRONT, |TRIA|={tnew.shape[0]}")
331
+
332
+ hvrt = trihfn(vert, vlfs, tlfs, slfs, hlfs)
333
+ hnew = trihfn(vnew, vlfs, tlfs, slfs, hlfs)
334
+
335
+ tricost(vert, etri, tria, tnum, hvrt)
336
+ tricost(vnew, enew, tnew, tnum, hnew)
337
+
338
+ plt.show()
339
+
340
+
341
+ def demo5():
342
+ """Mesh a multi-part geometry with conforming triangulation."""
343
+
344
+ print(
345
+ "Both refine and smooth routines support multi-part geometry\n"
346
+ "definitions, generating conforming triangulations that respect\n"
347
+ "internal and external constraints.\n"
348
+ )
349
+
350
+
351
+ nod1 = np.array([[-1.0, -1.0], [1.0, -1.0], [1.0, 1.0], [-1.0, 1.0]])
352
+
353
+ edg1 = np.array([[0, 1], [1, 2], [2, 3], [3, 0]], dtype=int)
354
+ tag1 = np.zeros((edg1.shape[0], 1), dtype=int)
355
+
356
+ nod2 = np.array([[0.1, 0.0], [0.8, 0.0], [0.8, 0.8], [0.1, 0.8]])
357
+
358
+ edg2 = np.array([[0, 1], [1, 2], [2, 3], [3, 0]], dtype=int)
359
+ tag2 = np.ones((edg2.shape[0], 1), dtype=int)
360
+
361
+ # Circle geometry
362
+ adel = 2.0 * np.pi / 64.0
363
+ amin = 0.0
364
+ amax = 2.0 * np.pi - adel
365
+
366
+ ang = np.arange(amin, amax + adel / 2, adel)
367
+ xcir = 0.33 * np.cos(ang) - 0.33
368
+ ycir = 0.33 * np.sin(ang) - 0.25
369
+ ncir = np.column_stack((xcir, ycir))
370
+
371
+ numc = ncir.shape[0]
372
+ ecir = np.column_stack((np.arange(numc - 1), np.arange(1, numc)))
373
+ ecir = np.vstack((ecir, [numc - 1, 0]))
374
+ tagc = np.full((ecir.shape[0], 1), 2, dtype=int)
375
+
376
+
377
+ # offset inner square indices
378
+ edg2 = edg2 + nod1.shape[0]
379
+ edge = np.vstack((np.hstack((edg1, tag1)), np.hstack((edg2, tag2))))
380
+ node = np.vstack((nod1, nod2))
381
+
382
+ # offset circle indices
383
+ ecir = ecir + node.shape[0]
384
+ edge = np.vstack((edge, np.hstack((ecir, tagc))))
385
+ node = np.vstack((node, ncir))
386
+
387
+
388
+ edge_tag = edge[:, 2].astype(int)
389
+ part = [
390
+ np.where((edge_tag == 0) | (edge_tag == 1) | (edge_tag == 2))[0],
391
+ np.where(edge_tag == 1)[0],
392
+ np.where(edge_tag == 2)[0],
393
+ ]
394
+
395
+ edge = edge[:, :2].astype(int)
396
+
397
+
398
+ hmax = 0.045
399
+ vlfs, tlfs, hlfs = lfshfn(node, edge, part)
400
+ hlfs = np.minimum(hmax, hlfs)
401
+ slfs = idxtri(vlfs, tlfs)
402
+
403
+
404
+ hfun = trihfn
405
+
406
+ vert, etri, tria, tnum = refine(node, edge, part, {}, hfun, vlfs, tlfs, slfs, hlfs)
407
+
408
+
409
+ vert, etri, tria, tnum = smooth(vert, etri, tria, tnum)
410
+
411
+
412
+ plt.figure()
413
+ for k, color in zip([1, 2, 3], [[0, 0, 0], [0.1, 0.1, 0.1], [0.2, 0.2, 0.2]]):
414
+ mask = tnum == k
415
+ plt.triplot(vert[:, 0], vert[:, 1], tria[mask, :], color=color, linewidth=0.2)
416
+ plt.axis("equal")
417
+ plt.axis("off")
418
+ plt.title(f"MESH-OPT.: KIND=DELFRONT, |TRIA|={tria.shape[0]}")
419
+
420
+ plt.figure()
421
+ tpc = plt.tripcolor(vlfs[:, 0], vlfs[:, 1], tlfs, hlfs, shading="flat")
422
+ plt.colorbar(tpc, label="h")
423
+ plt.axis("equal")
424
+ plt.axis("off")
425
+ plt.title(f"MESH-SIZE: KIND=DELAUNAY, |TRIA|={tlfs.shape[0]}")
426
+
427
+ tricost(vert, etri, tria, tnum)
428
+
429
+ plt.show()
430
+
431
+
432
+ def demo6():
433
+ """Mesh a domain with internal edge constraints."""
434
+
435
+ print(
436
+ " Both the refine and smooth routines also support geometr- \n"
437
+ " ies containing 'internal' constraints. \n"
438
+ )
439
+
440
+ node = np.array(
441
+ [
442
+ [-1.0, -1.0],
443
+ [1.0, -1.0],
444
+ [1.0, 1.0],
445
+ [-1.0, 1.0],
446
+ [0.0, 0.0],
447
+ [0.2, 0.7],
448
+ [0.6, 0.2],
449
+ [0.4, 0.8],
450
+ [0.0, 0.5],
451
+ [-0.7, 0.3],
452
+ [-0.1, 0.1],
453
+ [-0.6, 0.5],
454
+ [-0.9, -0.8],
455
+ [-0.6, -0.7],
456
+ [-0.3, -0.6],
457
+ [0.0, -0.5],
458
+ [0.3, -0.4],
459
+ [-0.3, 0.4],
460
+ [-0.1, 0.3],
461
+ ]
462
+ )
463
+
464
+ edge = np.array(
465
+ [
466
+ [0, 1],
467
+ [1, 2],
468
+ [2, 3],
469
+ [3, 0],
470
+ [4, 5],
471
+ [4, 6],
472
+ [4, 7],
473
+ [4, 8],
474
+ [4, 9],
475
+ [4, 10],
476
+ [4, 11],
477
+ [4, 12],
478
+ [4, 13],
479
+ [4, 14],
480
+ [4, 15],
481
+ [4, 16],
482
+ [4, 17],
483
+ [4, 18],
484
+ ]
485
+ )
486
+ """
487
+ the geometry must be split into its "exterior" and "int-
488
+ erior" components using the optional PART argument. Each
489
+ PART{I} specified should define the "exterior" boundary
490
+ of a polygonal region. "Interior" constraints should not
491
+ be referenced by any polygon in PART -- they are imposed
492
+ as isolated edge constraints.
493
+ """
494
+ part = [np.array([0, 1, 2, 3])]
495
+
496
+ hmax = 0.175
497
+
498
+ opts = {"kind": "delaunay"}
499
+
500
+ vert, etri, tria, tnum = refine(node, edge, part, opts, hmax)
501
+
502
+ vert, etri, tria, tnum = smooth(vert, etri, tria, tnum)
503
+
504
+ plt.figure()
505
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, :3], color="k", linewidth=0.2)
506
+ for e in edge:
507
+ plt.plot(node[e, 0], node[e, 1], "k", linewidth=1)
508
+
509
+ plt.gca().set_aspect("equal")
510
+ plt.axis("off")
511
+ plt.title(f"MESH-OPT.: KIND=DELAUNAY, |TRIA|={tria.shape[0]}")
512
+
513
+ tricost(vert, etri, tria, tnum)
514
+
515
+ plt.show()
516
+
517
+
518
+ def demo7():
519
+ """Demonstrate quadtree-style refinement via :func:`~bluemesh2d.mesh_util.tridiv.tridiv`."""
520
+
521
+ filename = __file__
522
+ filepath = "/".join(filename.split("/")[:-1])
523
+ meshfile = f"{filepath}/poly_data/channel.msh"
524
+
525
+ node, edge, _, _ = triread(meshfile)
526
+
527
+ print(
528
+ " The tridiv routine can also be used to refine existing tr- \n"
529
+ " angulations. Each triangle is split into four new sub-tria- \n"
530
+ " ngles, such that element shape is preserved. Combining the \n"
531
+ " tridiv and smooth routines allows for hierarchies of high \n"
532
+ " quality triangulations to be generated. \n"
533
+ )
534
+
535
+ vlfs, tlfs, hlfs = lfshfn(node, edge)
536
+ slfs = idxtri(vlfs, tlfs)
537
+
538
+ pmax = np.max(node, axis=0)
539
+ pmin = np.min(node, axis=0)
540
+
541
+ hmax = np.mean(pmax - pmin) / 17.0
542
+ hlfs = np.minimum(hmax, hlfs)
543
+
544
+ hfun = trihfn
545
+
546
+ vert, etri, tria, tnum = refine(node, edge, [], {}, hfun, vlfs, tlfs, slfs, hlfs)
547
+
548
+ vert, etri, tria, tnum = smooth(vert, etri, tria, tnum)
549
+ vnew, enew, tnew, tnum = tridiv(vert, etri, tria, tnum)
550
+ vnew, enew, tnew, tnum = smooth(vnew, enew, tnew, tnum)
551
+
552
+ plt.figure()
553
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, :3], color="k")
554
+
555
+ plt.gca().set_aspect("equal")
556
+ plt.axis("off")
557
+ plt.title(f"MESH-OPT.: KIND=DELFRONT, |TRIA|={tria.shape[0]}")
558
+
559
+ plt.figure()
560
+ plt.triplot(vnew[:, 0], vnew[:, 1], tnew[:, :3], color="k")
561
+
562
+ plt.gca().set_aspect("equal")
563
+ plt.axis("off")
564
+ plt.title(f"MESH-OPT.: KIND=DELFRONT, |TRIA|={tnew.shape[0]}")
565
+
566
+ tricost(vert, etri, tria, tnum)
567
+ tricost(vnew, enew, tnew, tnum)
568
+
569
+ plt.show()
570
+
571
+
572
+ def demo8():
573
+ """Demonstrate a custom Gaussian mesh-size function."""
574
+
575
+ node = np.array([[-1.0, -1.0], [3.0, -1.0], [3.0, 1.0], [-1.0, 1.0]])
576
+ edge = np.array([[0, 1], [1, 2], [2, 3], [3, 0]])
577
+
578
+ adel = 2.0 * np.pi / 64.0
579
+ amin = 0.0 * np.pi
580
+ amax = 2.0 * np.pi - adel
581
+
582
+ angles = np.arange(amin, amax + adel, adel)
583
+ xcir = 0.20 * np.cos(angles)
584
+ ycir = 0.20 * np.sin(angles)
585
+ ncir = np.column_stack([xcir, ycir])
586
+ numc = ncir.shape[0]
587
+
588
+ ecir = np.zeros((numc, 2), dtype=int)
589
+ ecir[:, 0] = np.arange(numc)
590
+ ecir[:, 1] = np.roll(ecir[:, 0], -1)
591
+
592
+ ecir = ecir + node.shape[0]
593
+ edge = np.vstack([edge, ecir])
594
+ node = np.vstack([node, ncir])
595
+
596
+ hfun = hfun8
597
+
598
+ vert, etri, tria, tnum = refine(node, edge, [], {}, hfun)
599
+
600
+ vert, etri, tria, tnum = smooth(vert, etri, tria, tnum)
601
+
602
+ plt.figure()
603
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, :3], color="k")
604
+
605
+ plt.gca().set_aspect("equal")
606
+ plt.axis("off")
607
+ plt.title(f"MESH-OPT.: KIND=DELFRONT, |TRIA|={tria.shape[0]}")
608
+
609
+ plt.figure()
610
+ facecolors = np.mean(hfun8(vert)[tria[:, :3]], axis=1)
611
+ plt.tripcolor(
612
+ vert[:, 0],
613
+ vert[:, 1],
614
+ tria[:, :3],
615
+ facecolors=facecolors,
616
+ edgecolors="none",
617
+ cmap="viridis",
618
+ )
619
+ plt.gca().set_aspect("equal")
620
+ plt.axis("off")
621
+ plt.title("MESH-SIZE function.")
622
+
623
+ hvrt = hfun(vert)
624
+ tricost(vert, etri, tria, tnum, hvrt)
625
+
626
+ plt.show()
627
+
628
+
629
+ def hfun8(test):
630
+ """Return a Gaussian mesh-size field for demo 8.
631
+
632
+ Parameters
633
+ ----------
634
+ test : ndarray of shape (N, 2)
635
+ Evaluation coordinates.
636
+
637
+ Returns
638
+ -------
639
+ hfun : ndarray of shape (N,)
640
+ Mesh-size values at ``test``.
641
+ """
642
+ hmax = 0.05
643
+ hmin = 0.01
644
+
645
+ xmid = 0.0
646
+ ymid = 0.0
647
+
648
+ hcir = np.exp(-0.5 * (test[:, 0] - xmid) ** 2 - 2.0 * (test[:, 1] - ymid) ** 2)
649
+
650
+ hfun = hmax - (hmax - hmin) * hcir
651
+
652
+ return hfun
653
+
654
+
655
+ def demo9():
656
+ """Run large-scale mesh refinement and optimization on the islands geometry."""
657
+
658
+ filename = __file__ # current file path
659
+ filepath = "/".join(filename.split("/")[:-1])
660
+ meshfile = f"{filepath}/poly_data/islands.msh"
661
+
662
+ # Load input mesh geometry
663
+ node, edge, _, _ = triread(meshfile)
664
+
665
+ vlfs, tlfs, hlfs = lfshfn(node, edge)
666
+ slfs = idxtri(vlfs, tlfs)
667
+
668
+ hfun = trihfn
669
+ vert, etri, tria, tnum = refine(node, edge, [], {}, hfun, vlfs, tlfs, slfs, hlfs)
670
+
671
+ vert, etri, tria, tnum = smooth(vert, etri, tria, tnum)
672
+
673
+ plt.figure()
674
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, :3], color="k")
675
+ plt.gca().set_aspect("equal")
676
+ plt.axis("off")
677
+ plt.title(f"MESH-OPT.: KIND=DELFRONT, |TRIA|={tria.shape[0]}")
678
+
679
+ tricost(vert, etri, tria, tnum)
680
+
681
+ plt.show()
682
+
683
+
684
+ def demo10():
685
+ """Run medium-scale mesh refinement and optimization on the river geometry."""
686
+
687
+ filename = __file__
688
+ filepath = "/".join(filename.split("/")[:-1])
689
+ meshfile = f"{filepath}/poly_data/river.msh"
690
+
691
+ node, edge, _, _ = triread(meshfile)
692
+
693
+ vlfs, tlfs, hlfs = lfshfn(node, edge)
694
+ slfs = idxtri(vlfs, tlfs)
695
+
696
+ hfun = trihfn
697
+ vert, etri, tria, tnum = refine(node, edge, [], {}, hfun, vlfs, tlfs, slfs, hlfs)
698
+
699
+ vert, etri, tria, tnum = smooth(vert, etri, tria, tnum)
700
+
701
+ plt.figure()
702
+ plt.triplot(vert[:, 0], vert[:, 1], tria[:, :3], color="k")
703
+ plt.gca().set_aspect("equal")
704
+ plt.axis("off")
705
+ plt.title(f"MESH-OPT.: KIND=DELFRONT, |TRIA|={tria.shape[0]}")
706
+
707
+ tricost(vert, etri, tria, tnum)
708
+
709
+ plt.show()
710
+
711
+
712
+ if __name__ == "__main__":
713
+ if len(sys.argv) < 2:
714
+ print("Usage: python -m examples.tridemo <demo_number>")
715
+ sys.exit(1)
716
+
717
+ try:
718
+ demo_number = int(sys.argv[1])
719
+ except ValueError:
720
+ print("Error: <demo_number> must be an integer.")
721
+ sys.exit(1)
722
+
723
+ tridemo(demo_number)