Skip to content

Commit

Permalink
add mesh.non_manifold_faces()
Browse files Browse the repository at this point in the history
  • Loading branch information
marcomusy committed Feb 19, 2023
1 parent 1e8ddfd commit 0bc914d
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 11 deletions.
4 changes: 2 additions & 2 deletions docs/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
- add `utils.is_ragged()` to check if an array is homogeneuos (not ragged)
- fix `shapes.Lines()` class
- add `shapes.ThickTube()` as per #800
- add `settings.backend_autoclose` (default is True) to automatically close `Plotter` after show
in jupyter notebooks, #802
- add `settings.backend_autoclose` (default is True) to automatically close `Plotter` after show() in jupyter notebooks, #802
- fix k3d backend - remove pinning to 2.7.4 #808
- in `Text2D` and `Text3D` shortcut to symbols are now formatted as `:gamma` and no more as `\gamma`
- fix `mesh.stretch()` thanks to @mikaeltulldahl in #807
- fixes to `cmap()`
- added `mesh.is_manifold()` method #813
- added `utils.open3d2vedo()` and `utils.vedo2open3d` converters as per #663
- added `mesh.non_manifold_faces()` to detect and (try to) remove non-manifold faces of a triangular mesh #813 #663 and https://github.com/SlicerIGT/SlicerBoneReconstructionPlanner/issues/35


-------------------------
Expand Down
63 changes: 61 additions & 2 deletions vedo/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
from vedo.colors import color_map
from vedo.colors import get_color
from vedo.pointcloud import Points
from vedo.utils import buildPolyData
from vedo.utils import is_sequence, mag, mag2
from vedo.utils import buildPolyData, is_sequence, mag, mag2
from vedo.utils import numpy2vtk, vtk2numpy

__docformat__ = "google"
Expand Down Expand Up @@ -743,6 +742,66 @@ def is_manifold(self):
ne = fe.GetOutput().GetNumberOfCells()
return not bool(ne)

def non_manifold_faces(self, remove_all=False, remove_boundary=False, collapse_edges=False):
"""
Detect and (try to) remove non-manifold faces of a triangular mesh.
Leaving all to `False` will add a new cell array with name "NonManifoldCell".
Note that "faces" and "cells" are synomyms.
"""
if int(remove_all)+int(remove_boundary)+int(collapse_edges) > 1:
raise ValueError("Select only one option")
# mark original point and cell ids
self.add_ids()
nme = self.boundaries(
boundary_edges=False,
non_manifold_edges=True,
)
nme_pids = nme.pointdata["PointID"]

bnd_cids = []
if remove_boundary:
# find cells sitting on boundaries
bnd_cids = self.boundaries(return_cell_ids=True)

all_nf = []
toremove = []
faces = self.faces()
points = self.points()
new_points = np.array(points)
for e in vedo.utils.progressbar(nme.lines(), delay=5, title="parsing faces"):
ie0, ie1 = e
ip0, ip1 = nme_pids[ie0], nme_pids[ie1]
for i, f in enumerate(faces):
if ip0 in f and ip1 in f:
all_nf.append(i)
if remove_all:
toremove.append(i)
elif remove_boundary:
if i in bnd_cids:
toremove.append(i)
elif collapse_edges:
pass
m = (points[ip0] + points[ip1])/2
new_points[ip0] = m
new_points[ip1] = m

# print("NF points", nme_pids)
# print("NF cells ", nfcells)
# print("NF toremove ", toremove)

if collapse_edges:
self.points(new_points)
self.clean()
elif toremove:
self.delete_cells(toremove)
else:
mark = np.zeros(self.ncells, dtype=np.uint8)
mark[all_nf] = 1
self.celldata["NonManifoldCell"] = mark

return self

def shrink(self, fraction=0.85):
"""Shrink the triangle polydata in the representation of the input mesh.
Expand Down
19 changes: 13 additions & 6 deletions vedo/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def __init__(
italic=False,
title="",
eta=True,
delay=0,
width=25,
char="\U00002501",
char_back="\U00002500",
Expand Down Expand Up @@ -120,23 +121,28 @@ def __init__(
self.percent = 0.0
self.percent_int = 0
self.eta = eta
self.delay = delay
self.clock0 = time.time()
self._remt = 1e10
self._update(0)

self._counts = 0
self._oldbar = ""
self._lentxt = 0
self._range = np.arange(start, stop, step)
self._len = len(self._range)

def print(self, txt="", counts=None, c=None):
def print(self, txt="", c=None):
"""Print the progress bar and optional message."""
if not c:
c = self.color
if counts:
self._update(counts)
else:
self._update(self._counts + self.step)

self._update(self._counts + self.step)

if self.delay:
if time.time() - self.clock0 < self.delay:
return

if self.pbar != self._oldbar:
self._oldbar = self.pbar
eraser = [" "] * self._lentxt + ["\b"] * self._lentxt
Expand Down Expand Up @@ -223,6 +229,7 @@ def progressbar(
title="",
eta=True,
width=25,
delay=0,
):
"""
Function to print a progress bar with optional text message.
Expand All @@ -248,7 +255,7 @@ def progressbar(
pb = ProgressBar(
0, total,
c=c, bold=bold, italic=italic,
title=title, eta=eta, width=width,
title=title, eta=eta, delay=delay, width=width,
)
for item in iterable:
pb.print()
Expand Down
2 changes: 1 addition & 1 deletion vedo/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
_version = '2023.4.3.dev15'
_version = '2023.4.3.dev16'

0 comments on commit 0bc914d

Please sign in to comment.