Skip to content

Commit 5cf18e4

Browse files
committed
[test] add tests for m2v, mesh2vol, mesh2mask, mask has bias
the bias is due to matplotlib rendering under antialiased=False is sensitive to patch orders, reported at matplotlib/matplotlib#26827
1 parent 7650d96 commit 5cf18e4

File tree

4 files changed

+66
-27
lines changed

4 files changed

+66
-27
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,21 +202,21 @@ tracked in https://github.com/NeuroJSON/pyiso2mesh/issues/1
202202
|`v2s.m` | ✅ tested | |`savedxf.m` | ⭕️ tested |
203203
|`s2m.m` | ✅ tested | |`savestl.m` | ⭕️ tested |
204204
|`s2v.m` | ✅ tested | |`savebinstl.m` | ⭕️ tested |
205-
|`m2v.m` | ️ tested | |`saveinr.m` | ⭕️ tested |
205+
|`m2v.m` | ️ tested | |`saveinr.m` | ⭕️ tested |
206206
|`sms.m` | ✅ tested | |`saveoff.m` | ✅ tested |
207207
| > Streamlined mesh generation| | | ⭕️ `savesmf.m` | ⭕️ tested |
208208
|`vol2mesh.m` | ✅ tested | |`savesurfpoly.m` | ✅ tested |
209209
|`vol2surf.m` | ✅ tested | | ⭕️ `savegts.m` | ⭕️ tested |
210210
|`surf2mesh.m` | ✅ tested | | ⭕️ `readgts.m` | ⭕️ tested |
211211
|`surf2vol.m` | ✅ tested | | ⭕️ `savemsh.m` | ⭕️ tested |
212-
|`mesh2vol.m` | ️ tested | | ⭕️ `savevrml.m` | ⭕️ tested |
212+
|`mesh2vol.m` | ️ tested | | ⭕️ `savevrml.m` | ⭕️ tested |
213213
| > Iso2mesh main function backend| | |`readasc.m` | ⭕️ tested |
214214
|`binsurface.m` | ✅ tested | | ⭕️ `readinr.m` | ⭕️ tested |
215215
|`cgalv2m.m` | ✅ tested | |`readmedit.m` | ⭕️ tested |
216216
|`cgals2m.m` | ✅ tested | |`readoff.m` | ✅ tested |
217217
|`vol2restrictedtri.m` | ✅ tested | | ⭕️ `readsmf.m` | ⭕️ tested |
218218
|`surf2volz.m` | ✅ tested | |`readtetgen.m` | ✅ tested |
219-
|`mesh2mask.m` | ️ tested | |`deletemeshfile.m` | ✅ tested |
219+
|`mesh2mask.m` | ️ tested | |`deletemeshfile.m` | ✅ tested |
220220
| > Iso2mesh primitive meshing| | |`mcpath.m` | ✅ tested |
221221
|`meshabox.m` | ✅ tested | |`mwpath.m` | ✅ tested |
222222
|`meshasphere.m` | ✅ tested | |`savemedit.m` | ✅ tested |

iso2mesh/modify.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ def qmeshcut(elem, node, value, cutat):
253253
cutweight = np.abs(cutweight / totalweight[:, np.newaxis])
254254

255255
nodeid = edges[cutedges] - 1
256-
nodeid = np.column_stack([nodeid, cutweight[:, 1]])
256+
nodeid = np.column_stack([nodeid, cutweight[:, 1]]) + 1
257257

258258
cutpos = (
259259
node[edges[cutedges, 0] - 1] * cutweight[:, [1]]
@@ -283,7 +283,7 @@ def qmeshcut(elem, node, value, cutat):
283283
elemid = linecut
284284
if value.shape[0] == elem.shape[0] and "cutvalue" not in locals():
285285
cutvalue = value[elemid]
286-
return cutpos, cutvalue, facedata, elemid, nodeid
286+
return cutpos, cutvalue, facedata, elemid + 1, nodeid
287287

288288
tricut = np.where(etag == 3)[0]
289289
quadcut = np.where(etag == 4)[0]
@@ -300,7 +300,7 @@ def qmeshcut(elem, node, value, cutat):
300300

301301
facedata = np.vstack([tripatch[:, [0, 1, 2, 2]], quadpatch[:, [0, 1, 3, 2]]])
302302

303-
return cutpos, cutvalue, facedata, elemid, nodeid
303+
return cutpos, cutvalue, facedata, elemid + 1, nodeid
304304

305305

306306
def meshcheckrepair(node, elem, opt=None, **kwargs):

iso2mesh/raster.py

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
##====================================================================================
2020

2121

22-
def m2v(*args):
22+
def m2v(*args, **kwargs):
2323
"""
2424
Shortcut for mesh2vol, rasterizing a tetrahedral mesh to a volume.
2525
@@ -29,10 +29,10 @@ def m2v(*args):
2929
Returns:
3030
Volumetric representation of the mesh.
3131
"""
32-
return mesh2vol(*args)
32+
return mesh2vol(*args, **kwargs)
3333

3434

35-
def mesh2vol(node, elem, xi, yi=None, zi=None):
35+
def mesh2vol(node, elem, xi, yi=None, zi=None, **kwargs):
3636
"""
3737
mesh2vol(node, elem, xi, yi=None, zi=None)
3838
@@ -108,10 +108,10 @@ def mesh2vol(node, elem, xi, yi=None, zi=None):
108108
continue
109109

110110
if weight is not None:
111-
maskz, weightz = mesh2mask(cutpos, facedata, xi, yi)
111+
maskz, weightz = mesh2mask(cutpos, facedata, xi, yi, **kwargs)
112112
weight[:, :, :, i] = weightz
113113
else:
114-
maskz = mesh2mask(cutpos, facedata, xi, yi)[0]
114+
maskz = mesh2mask(cutpos, facedata, xi, yi, **kwargs)[0]
115115

116116
idx = ~np.isnan(maskz)
117117
if nodeval is not None:
@@ -131,7 +131,7 @@ def mesh2vol(node, elem, xi, yi=None, zi=None):
131131
return mask, weight
132132

133133

134-
def mesh2mask(node, face, xi, yi=None, hf=None):
134+
def mesh2mask(node, face, xi, yi=None, hf=None, **kwargs):
135135
"""
136136
Fast rasterization of a 2D mesh to an image with triangle index labels.
137137
@@ -173,9 +173,7 @@ def mesh2mask(node, face, xi, yi=None, hf=None):
173173
)
174174

175175
fig = (
176-
plt.figure(
177-
figsize=(xi.size * 0.01, yi.size * 0.01), dpi=100, layout="compressed"
178-
)
176+
plt.figure(figsize=(xi.size * 0.01, yi.size * 0.01), dpi=100)
179177
if hf is None
180178
else hf
181179
)
@@ -185,30 +183,28 @@ def mesh2mask(node, face, xi, yi=None, hf=None):
185183
ax.set_ylim(mn[1], mx[1])
186184
ax.set_axis_off()
187185

188-
colors = cm.gray(np.linspace(0, 1, len(face)))
186+
colors = cm.jet(np.linspace(0, 1, len(face)))
189187

190188
patches = []
191189
for i, f in enumerate(face[:, :3]):
192-
polygon = Polygon(
193-
node[f - 1, :2],
194-
closed=True,
195-
edgecolor="none",
196-
linewidth=0,
197-
linestyle="none",
198-
)
190+
polygon = Polygon(node[f - 1, :2], closed=True, zorder=1)
199191
patches.append(polygon)
200192

201193
collection = PatchCollection(
202-
patches, facecolors=colors, linewidths=0.01, edgecolors="none", edgecolor="face"
194+
patches,
195+
facecolors=colors,
196+
linewidths=0,
197+
edgecolor="face",
198+
antialiased=(not kwargs.get("edge", True)),
203199
)
204200
ax.add_collection(collection)
205201

206202
plt.draw()
207203
fig.canvas.draw()
208204
img = np.array(fig.canvas.renderer.buffer_rgba())
209-
mask_raw = img[:, :, 0]
210-
mask = np.zeros(mask_raw.shape, dtype=np.int32)
211-
color_vals = (colors[:, :3] * 255).astype(np.uint8)
205+
206+
mask = np.zeros(img.shape[:2], dtype=np.int32) * np.nan
207+
color_vals = np.floor(colors[:, :3] * 255 + 0.5).astype(np.uint8)
212208

213209
for idx, cval in enumerate(color_vals):
214210
match = np.all(img[:, :, :3] == cval, axis=-1)

test/run_test.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,49 @@ def test_s2v(self):
12781278
vol = s2v(self.no, self.fc, 100, fill=1)
12791279
self.assertAlmostEqual(np.sum(vol.astype(np.float32)) * 0.00001, 6.0827, 2)
12801280

1281+
def test_mesh2mask(self):
1282+
node = np.array([[0, 0], [2, 0], [2, 1], [0, 1], [1, 0.5]])
1283+
face = np.array([[1, 2, 5], [2, 3, 5], [3, 4, 5], [4, 1, 5]])
1284+
xi = np.arange(-0.1, 2.2, 0.1)
1285+
yi = np.arange(-0.1, 1.2, 0.1)
1286+
mask, _ = mesh2mask(node, face, xi, yi)
1287+
counts = np.histogram(mask, bins=np.arange(1, 6) - 0.5)[0]
1288+
1289+
self.assertEqual(mask.shape, (xi.size, yi.size))
1290+
self.assertEqual(np.count_nonzero(np.isnan(mask)), (xi.size + yi.size) * 2 - 4)
1291+
self.assertEqual(counts.tolist(), [45, 55, 60, 71])
1292+
1293+
mask, _ = mesh2mask(node, face, xi, yi, edge=False)
1294+
self.assertEqual(np.count_nonzero(np.isnan(mask)), 179)
1295+
1296+
def test_mesh2vol(self):
1297+
no1, el1 = meshgrid5(np.arange(1, 4), np.arange(1, 3), np.arange(1, 3))
1298+
mask, _ = mesh2vol(
1299+
no1,
1300+
el1,
1301+
np.arange(1, 3.1, 0.05),
1302+
np.arange(1, 2.1, 0.05),
1303+
np.arange(1, 2, 0.05),
1304+
)
1305+
counts = np.histogram(mask, bins=np.arange(0, el1.shape[0] + 2) - 0.5)[0]
1306+
1307+
self.assertEqual(np.count_nonzero(np.isnan(mask)), 2657)
1308+
self.assertTrue(np.all(counts > 100))
1309+
1310+
def test_m2v(self):
1311+
no1, el1 = meshgrid5(np.arange(1, 3), np.arange(1, 3), np.arange(1, 3))
1312+
mask, _ = m2v(
1313+
no1,
1314+
el1,
1315+
np.arange(1, 3.1, 0.05),
1316+
np.arange(1, 2.1, 0.05),
1317+
np.arange(1, 2, 0.05),
1318+
)
1319+
counts = np.histogram(mask, bins=np.arange(0, el1.shape[0] + 2) - 0.5)[0]
1320+
1321+
self.assertEqual(np.count_nonzero(np.isnan(mask)), 9928)
1322+
self.assertTrue(np.all(counts > 50))
1323+
12811324

12821325
@unittest.skipIf(
12831326
(int(mpl_ver[0]), int(mpl_ver[1])) < (3, 6), "Requires Matplotlib 3.6 or higher"

0 commit comments

Comments
 (0)