Skip to content

Commit 30a8f7c

Browse files
committed
[feat] add line.py for polyline, fix missing import, fix circular dependency
1 parent fd06a01 commit 30a8f7c

File tree

9 files changed

+781
-165
lines changed

9 files changed

+781
-165
lines changed

iso2mesh/__init__.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
outersurf,
104104
surfvolume,
105105
insurface,
106+
remeshsurf,
106107
)
107108

108109
from .modify import (
@@ -128,7 +129,15 @@
128129
surfboolean,
129130
meshresample,
130131
domeshsimplify,
131-
remeshsurf,
132+
)
133+
134+
from .line import (
135+
linextriangle,
136+
getplanefrom3pt,
137+
polylinelen,
138+
closestnode,
139+
polylineinterp,
140+
polylinesimplify,
132141
)
133142

134143
from .raster import (
@@ -138,6 +147,15 @@
138147
barycentricgrid,
139148
)
140149

150+
from .register import (
151+
affinemap,
152+
meshinterp,
153+
meshremap,
154+
proj2mesh,
155+
dist2surf,
156+
regpt2surf,
157+
)
158+
141159
__version__ = "0.3.6"
142160
__all__ = [
143161
"advancefront",
@@ -253,5 +271,17 @@
253271
"vol2restrictedtri",
254272
"vol2surf",
255273
"volface",
274+
"linextriangle",
275+
"getplanefrom3pt",
276+
"polylinelen",
277+
"closestnode",
278+
"polylineinterp",
279+
"polylinesimplify",
280+
"affinemap",
281+
"meshinterp",
282+
"meshremap",
283+
"proj2mesh",
284+
"dist2surf",
285+
"regpt2surf",
256286
]
257287
__license__ = """GNU General Public License v3 and later"""

iso2mesh/core.py

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"outersurf",
2323
"surfvolume",
2424
"insurface",
25+
"remeshsurf",
2526
]
2627

2728
##====================================================================================
@@ -31,7 +32,7 @@
3132
import numpy as np
3233
import os
3334
import re
34-
import platform
35+
import sys
3536
import subprocess
3637
from iso2mesh.trait import (
3738
surfinterior,
@@ -40,6 +41,8 @@
4041
elemvolume,
4142
meshreorient,
4243
finddisconnsurf,
44+
maxsurf,
45+
volface,
4346
)
4447
from iso2mesh.utils import *
4548
from iso2mesh.io import saveoff, readoff, saveinr, readtetgen, savesurfpoly, readmedit
@@ -49,6 +52,7 @@
4952
meshresample,
5053
removeisolatedsurf,
5154
removeisolatednode,
55+
qmeshcut,
5256
)
5357

5458
##====================================================================================
@@ -572,7 +576,9 @@ def surf2volz(node, face, xi, yi, zi):
572576

573577
for i in iz:
574578
plane = np.array([[0, 100, zi[i]], [100, 0, zi[i]], [0, 0, zi[i]]])
575-
bcutpos, bcutvalue, bcutedges = qmeshcut(face[:, :3], node, node[:, 0], plane)
579+
bcutpos, bcutvalue, bcutedges, _, _ = qmeshcut(
580+
face[:, :3], node, node[:, 0], plane
581+
)
576582

577583
if bcutpos.size == 0:
578584
continue
@@ -622,6 +628,7 @@ def surf2vol(node, face, xi, yi, zi, **kwargs):
622628
img: a volumetric binary image at the position of ndgrid(xi, yi, zi)
623629
v2smap (optional): a 4x4 matrix denoting the Affine transformation to map voxel coordinates back to the mesh space.
624630
"""
631+
from scipy.ndimage import binary_fill_holes
625632

626633
opt = kwargs
627634
label = opt.get("label", 0)
@@ -651,7 +658,7 @@ def surf2vol(node, face, xi, yi, zi, **kwargs):
651658
im |= np.moveaxis(surf2volz(node[:, [1, 2, 0]], fc[:, :3], yi, zi, xi), 0, 1)
652659

653660
if opt.get("fill", 0) or label:
654-
im = imfill(im, "holes")
661+
im = binary_fill_holes(im)
655662
if label:
656663
im = im.astype(elabel.dtype) * lbl
657664

@@ -851,7 +858,7 @@ def cgalv2m(vol, opt, maxvol):
851858
return node, elem, face
852859

853860

854-
def cgals2m(v, f, opt, maxvol, *args):
861+
def cgals2m(v, f, opt, maxvol, **kwargs):
855862
"""
856863
Convert a triangular surface to a tetrahedral mesh using CGAL mesher.
857864
@@ -888,7 +895,6 @@ def cgals2m(v, f, opt, maxvol, *args):
888895
ssize = 6
889896
approx = 0.5
890897
reratio = 3
891-
flags = args_to_dict(*args)
892898

893899
if not isinstance(opt, dict):
894900
ssize = opt
@@ -899,7 +905,7 @@ def cgals2m(v, f, opt, maxvol, *args):
899905
approx = opt.get("distbound", approx)
900906
reratio = opt.get("reratio", reratio)
901907

902-
if flags.get("DoRepair", 0) == 1:
908+
if kwargs.get("dorepair", 0) == 1:
903909
v, f = meshcheckrepair(v, f)
904910

905911
saveoff(v, f, mwpath("pre_cgalpoly.off"))
@@ -1149,3 +1155,68 @@ def insurface(node, face, points):
11491155
tf = tf.astype(int)
11501156

11511157
return tf
1158+
1159+
1160+
def remeshsurf(node, face, opt):
1161+
"""
1162+
remeshsurf(node, face, opt)
1163+
1164+
Remesh a triangular surface, output is guaranteed to be free of self-intersecting elements.
1165+
This function can both downsample or upsample a mesh.
1166+
1167+
Parameters:
1168+
node: list of nodes on the input surface mesh, 3 columns for x, y, z
1169+
face: list of triangular elements on the surface, [n1, n2, n3, region_id]
1170+
opt: function parameters
1171+
opt.gridsize: resolution for the voxelization of the mesh
1172+
opt.closesize: if there are openings, set the closing diameter
1173+
opt.elemsize: the size of the element of the output surface
1174+
If opt is a scalar, it defines the elemsize and gridsize = opt / 4
1175+
1176+
Returns:
1177+
newno: list of nodes on the resulting surface mesh, 3 columns for x, y, z
1178+
newfc: list of triangular elements on the surface, [n1, n2, n3, region_id]
1179+
"""
1180+
from scipy.ndimage import binary_fill_holes
1181+
1182+
# Step 1: convert the old surface to a volumetric image
1183+
p0 = np.min(node, axis=0)
1184+
p1 = np.max(node, axis=0)
1185+
1186+
if isinstance(opt, dict):
1187+
dx = opt.get("gridsize", None)
1188+
else:
1189+
dx = opt / 4
1190+
1191+
x_range = np.arange(p0[0] - dx, p1[0] + dx, dx)
1192+
y_range = np.arange(p0[1] - dx, p1[1] + dx)
1193+
z_range = np.arange(p0[2] - dx, p1[2] + dx)
1194+
1195+
img = surf2vol(node, face, x_range, y_range, z_range)
1196+
1197+
# Compute surface edges
1198+
eg = surfedge(face)
1199+
1200+
closesize = 0
1201+
if eg.size > 0 and isinstance(opt, dict):
1202+
closesize = opt.get("closesize", 0)
1203+
1204+
# Step 2: fill holes in the volumetric binary image
1205+
img = binary_fill_holes(img)
1206+
1207+
# Step 3: convert the filled volume to a new surface
1208+
if isinstance(opt, dict):
1209+
if "elemsize" in opt:
1210+
opt["radbound"] = opt["elemsize"] / dx
1211+
newno, newfc, _, _ = v2s(img, 0.5, opt, "cgalsurf")
1212+
else:
1213+
opt = {"radbound": opt / dx}
1214+
newno, newfc, _, _ = v2s(img, 0.5, opt, "cgalsurf")
1215+
1216+
# Adjust new nodes to match original coordinates
1217+
newno[:, 0:3] *= dx
1218+
newno[:, 0] += p0[0]
1219+
newno[:, 1] += p0[1]
1220+
newno[:, 2] += p0[2]
1221+
1222+
return newno, newfc

iso2mesh/geometry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import sys
2929
from itertools import permutations, combinations
3030
from iso2mesh.core import surf2mesh, vol2restrictedtri
31-
from iso2mesh.trait import meshreorient, volface, surfedge
31+
from iso2mesh.trait import meshreorient, volface, surfedge, nodesurfnorm
3232
from iso2mesh.utils import *
3333
from iso2mesh.modify import removeisolatednode, meshcheckrepair
3434

iso2mesh/io.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,15 @@
2525
import struct
2626
from datetime import datetime
2727
import re
28-
from iso2mesh.trait import meshreorient, surfedge, extractloops
28+
from iso2mesh.trait import (
29+
meshreorient,
30+
surfedge,
31+
extractloops,
32+
volface,
33+
surfplane,
34+
bbxflatsegment,
35+
internalpoint,
36+
)
2937

3038
##====================================================================================
3139
## implementations

0 commit comments

Comments
 (0)