Skip to content

Commit e5369e0

Browse files
committed
[test] add plotmesh tests, add hide option to plotmesh, bump 0.3.2
1 parent a2a5d4e commit e5369e0

File tree

6 files changed

+110
-39
lines changed

6 files changed

+110
-39
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
* Copyright: (C) Qianqian Fang (2024-2025) <q.fang at neu.edu>, Edward Xu (2024) <xu.ed at northeastern.edu>
66
* License: GNU Public License V3 or later
7-
* Version: 0.3.1
7+
* Version: 0.3.2
88
* URL: [https://pypi.org/project/iso2mesh/](https://pypi.org/project/iso2mesh/)
99
* Github: [https://github.com/NeuroJSON/pyiso2mesh](https://github.com/NeuroJSON/pyiso2mesh)
1010

iso2mesh/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
barycentricgrid,
138138
)
139139

140-
__version__ = "0.3.1"
140+
__version__ = "0.3.2"
141141
__all__ = [
142142
"advancefront",
143143
"barycentricgrid",

iso2mesh/plot.py

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import matplotlib.pyplot as plt
2121
from iso2mesh.trait import volface, meshcentroid
2222

23+
COLOR_OFFSET = 3
2324
# _________________________________________________________________________________________________________
2425

2526

@@ -30,7 +31,7 @@ def plotsurf(node, face, *args, **kwargs):
3031
rngstate = np.random.get_state()
3132
h = []
3233

33-
randseed = int("623F9A9E", 16)
34+
randseed = int("623F9A9E", 16) + COLOR_OFFSET
3435

3536
if "ISO2MESH_RANDSEED" in globals():
3637
randseed = globals()["ISO2MESH_RANDSEED"]
@@ -119,6 +120,9 @@ def plotsurf(node, face, *args, **kwargs):
119120
norm = Normalize(vmin=face_values.min(), vmax=face_values.max())
120121
kwargs["facecolors"] = kwargs["cmap"](norm(face_values))
121122

123+
if not "linewidth" in kwargs:
124+
kwargs["linewidth"] = 0.3
125+
122126
patch = Poly3DCollection(polydata, edgecolors="k", **kwargs)
123127

124128
ax.add_collection3d(patch)
@@ -170,7 +174,7 @@ def plottetra(node, elem, *args, **kwargs):
170174
rngstate = np.random.get_state()
171175

172176
# Set deterministic seed for consistent coloring
173-
randseed = int("623F9A9E", 16)
177+
randseed = int("623F9A9E", 16) + COLOR_OFFSET
174178

175179
if "ISO2MESH_RANDSEED" in globals():
176180
randseed = globals()["ISO2MESH_RANDSEED"]
@@ -215,6 +219,9 @@ def plottetra(node, elem, *args, **kwargs):
215219
norm = Normalize(vmin=face_values.min(), vmax=face_values.max())
216220
kwargs["facecolors"] = kwargs["cmap"](norm(face_values))
217221

222+
if not "linewidth" in kwargs:
223+
kwargs["linewidth"] = 0.3
224+
218225
patch = Poly3DCollection(polydata, edgecolors="k", **kwargs)
219226
ax.add_collection3d(patch)
220227

@@ -263,7 +270,7 @@ def plotedges(node, edges, *args, **kwargs):
263270

264271
if edges.ndim == 1 or edges.shape[1] == 1:
265272
# Loop: NaN-separated index list
266-
randseed = int("623F9A9E", 16)
273+
randseed = int("623F9A9E", 16) + COLOR_OFFSET
267274
if "iso2mesh_randseed" in kwargs:
268275
randseed = kwargs["iso2mesh_randseed"]
269276
np.random.seed(randseed)
@@ -368,6 +375,10 @@ def plotmesh(node, *args, **kwargs):
368375
face = args[0]
369376
elem = a
370377

378+
extraarg = {}
379+
if len(opt) > 1 and len(opt) % 2 == 0:
380+
extraarg = dict(zip(opt[::2], opt[1::2]))
381+
371382
handles = []
372383

373384
ax = kwargs.get("parent", None)
@@ -389,9 +400,10 @@ def plotmesh(node, *args, **kwargs):
389400
if getattr(idx, "size", None) == 0:
390401
print("Warning: nothing to plot")
391402
return None
392-
ax.plot(x[idx], y[idx], z[idx], *opt)
403+
ax.plot(x[idx], y[idx], z[idx], **kwargs)
393404
_autoscale_3d(ax, node)
394-
plt.show(block=False)
405+
if "hide" in extraarg and not extraarg["hide"]:
406+
plt.show(block=False)
395407
return ax
396408

397409
# Plot surface mesh
@@ -428,22 +440,10 @@ def plotmesh(node, *args, **kwargs):
428440
ax = plottetra(node, elem[idx, :], opt, *args, **kwargs)
429441
handles.append(ax)
430442

431-
plt.show(block=False)
432-
return handles if len(handles) > 1 else handles[0]
433-
443+
if "hide" in extraarg and not extraarg["hide"]:
444+
plt.show(block=False)
434445

435-
def _get_face_triangles(node, face, selector):
436-
"""Convert 1-based faces to triangles and apply selector filter."""
437-
face = np.asarray(face)
438-
face3 = face[:, :3].astype(int) - 1
439-
tris = node[face3, :3]
440-
if selector:
441-
cent = tris.mean(axis=1)
442-
idx = np.where(
443-
eval(selector, {"x": cent[:, 0], "y": cent[:, 1], "z": cent[:, 2]})
444-
)[0]
445-
tris = tris[idx]
446-
return tris
446+
return handles if len(handles) > 1 else handles[0]
447447

448448

449449
def _autoscale_3d(ax, points):
@@ -453,17 +453,3 @@ def _autoscale_3d(ax, points):
453453
ax.set_zlim([z.min(), z.max()])
454454
boxas = [x.max() - x.min(), y.max() - y.min(), z.max() - z.min()]
455455
ax.set_box_aspect(boxas)
456-
457-
458-
def _extract_poly_opts(opt):
459-
"""Extract facecolor/edgecolor options for Poly3DCollection."""
460-
d = {}
461-
if "facecolor" in opt:
462-
d["facecolor"] = opt[opt.index("facecolor") + 1]
463-
else:
464-
d["facecolor"] = "white"
465-
if "edgecolor" in opt:
466-
d["edgecolor"] = opt[opt.index("edgecolor") + 1]
467-
else:
468-
d["edgecolor"] = "k"
469-
return d

iso2mesh/trait.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ def surfedge(f, junction=None):
142142
openedge = edges[ix[qx], :]
143143

144144
elemid = None
145-
if junction is not None:
146-
elemid, iy = np.unravel_index(ix[qx], f.shape)
145+
elemid, _ = np.unravel_index(ix[qx], f.shape)
147146

148147
return openedge, elemid
149148

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
setup(
77
name="iso2mesh",
88
packages=["iso2mesh"],
9-
version="0.3.1",
9+
version="0.3.2",
1010
license='GPLv3+',
1111
description="Image-based 3D Surface and Volumetric Mesh Generator",
1212
long_description=readme,

test/run_test.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,5 +1092,91 @@ def test_s2m_plc(self):
10921092
)
10931093

10941094

1095+
class Test_plot(unittest.TestCase):
1096+
def __init__(self, *args, **kwargs):
1097+
super(Test_plot, self).__init__(*args, **kwargs)
1098+
self.no, self.el = meshgrid6(
1099+
np.arange(1, 3), np.arange(-1, 1), np.arange(2, 3.5, 0.5)
1100+
)
1101+
self.fc = volface(self.el)[0]
1102+
1103+
def test_plotmesh_face(self):
1104+
patch = plotmesh(self.no, self.fc, "hide", True)
1105+
facecolors = np.array(patch[0].get_fc())
1106+
expected_fc = [9.0, 8.6803, 9.0, 20.0]
1107+
1108+
self.assertEqual(len(facecolors), 20)
1109+
self.assertEqual(np.round(np.sum(facecolors, axis=0), 4).tolist(), expected_fc)
1110+
1111+
def test_plotmesh_elem(self):
1112+
patch = plotmesh(self.no, self.el, "hide", True)
1113+
facecolors = np.array(patch[0].get_fc())
1114+
expected_fc = [9.0, 8.6803, 9.0, 20.0]
1115+
1116+
self.assertEqual(len(facecolors), 20)
1117+
self.assertEqual(np.round(np.sum(facecolors, axis=0), 4).tolist(), expected_fc)
1118+
1119+
def test_plotmesh_elemlabel(self):
1120+
patch = plotmesh(
1121+
self.no,
1122+
np.hstack((self.el, np.ones(self.el.shape[0], dtype=int).reshape(-1, 1))),
1123+
"hide",
1124+
True,
1125+
)
1126+
facecolors = np.array(patch[0].get_fc())
1127+
expected_fc = (1, 4)
1128+
1129+
self.assertEqual(len(facecolors), 20)
1130+
self.assertEqual(np.unique(facecolors, axis=0).shape, expected_fc)
1131+
1132+
def test_plotmesh_facelabel(self):
1133+
patch = plotmesh(
1134+
self.no,
1135+
np.hstack((self.fc, np.array([1, 2] * 10).reshape(-1, 1))),
1136+
None,
1137+
"hide",
1138+
True,
1139+
)
1140+
facecolors = np.array(patch[0].get_fc())
1141+
expected_fc = (2, 4)
1142+
1143+
self.assertEqual(len(facecolors), 20)
1144+
self.assertEqual(np.unique(facecolors, axis=0).shape, expected_fc)
1145+
1146+
def test_plotmesh_elemnodeval(self):
1147+
patch = plotmesh(self.no[:, [0, 1, 2, 0]], self.el, "hide", True)
1148+
facecolors = np.array(patch[0].get_fc())
1149+
expected_fc = [8.0, 10.4074, 8.0, 20.0]
1150+
1151+
self.assertEqual(len(facecolors), 20)
1152+
self.assertEqual(np.round(np.sum(facecolors, axis=0), 4).tolist(), expected_fc)
1153+
1154+
def test_plotmesh_facenodeval(self):
1155+
patch = plotmesh(self.no[:, [0, 1, 2, 0]], self.fc, "z < 3", "hide", True)
1156+
facecolors = np.array(patch[0].get_fc())
1157+
expected_fc = [7.0, 8.6728, 7.0, 18.0]
1158+
1159+
self.assertEqual(len(facecolors), 18)
1160+
self.assertEqual(np.round(np.sum(facecolors, axis=0), 4).tolist(), expected_fc)
1161+
1162+
def test_plotmesh_selector(self):
1163+
patch = plotmesh(
1164+
self.no[:, [0, 1, 2, 0]], self.fc, "(z < 3) & (x < 2)", "hide", True
1165+
)
1166+
facecolors = np.array(patch[0].get_fc())
1167+
expected_fc = [4.8877, 5.0, 4.451, 14.0]
1168+
1169+
self.assertEqual(len(facecolors), 14)
1170+
self.assertEqual(np.round(np.sum(facecolors, axis=0), 4).tolist(), expected_fc)
1171+
1172+
def test_plotmesh_elemselector(self):
1173+
patch = plotmesh(self.no, self.fc, "z < 2.5", "hide", True)
1174+
facecolors = np.array(patch[0].get_fc())
1175+
expected_fc = [3.9102, 4.0, 2.9608, 10.0]
1176+
1177+
self.assertEqual(len(facecolors), 10)
1178+
self.assertEqual(np.round(np.sum(facecolors, axis=0), 4).tolist(), expected_fc)
1179+
1180+
10951181
if __name__ == "__main__":
10961182
unittest.main()

0 commit comments

Comments
 (0)