Skip to content

Commit

Permalink
split mesh.decimate()
Browse files Browse the repository at this point in the history
  • Loading branch information
marcomusy committed Dec 19, 2023
1 parent 092a97f commit eff9653
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 25 deletions.
2 changes: 2 additions & 0 deletions docs/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- add `smooth_data()` to smooth/diffuse data attributes
- add `shapes.Tubes()`
- add `utils.Minimizer()` class
- add `CellCenters(Points)` class


## Breaking changes
Expand All @@ -25,6 +26,7 @@
- improvements to `pointcloud.pca_ellipse()` and bug fixes
- change `clone2d(scale=...)` to `clone2d(size=...)`
- remove `shapes.StreamLines()` becoming `object.compute_streamlines()`
- split `mesh.decimate()` into `mesh.decimate()`, `mesh.decimate_pro()` and `mesh.decimate_binned()` as per #992


### Renaming
Expand Down
7 changes: 5 additions & 2 deletions vedo/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,11 @@ def cell_centers(self):
Examples:
- [delaunay2d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/delaunay2d.py)
See also: `CellCenters()`.
"""
vcen = vtk.new("CellCenters")
vcen.CopyArraysOff()
vcen.SetInputData(self.dataset)
vcen.Update()
return utils.vtk2numpy(vcen.GetOutput().GetPoints().GetData())
Expand Down Expand Up @@ -941,7 +944,7 @@ def resample_data_from(self, source, tol=None, categorical=False):
m1.pointdata["xvalues"] = np.power(pts[:,0], 3)
m1.celldata["yvalues"] = np.power(ces[:,1], 3)
m2 = Mesh(dataurl+'bunny.obj')
m2.resample_arrays_from(m1)
m2.resample_data_from(m1)
# print(m2.pointdata["xvalues"])
show(m1, m2 , N=2, axes=1)
```
Expand Down Expand Up @@ -1053,7 +1056,7 @@ def interpolate_data_from(
interpolator.SetSourceData(points)
interpolator.SetKernel(kern)
interpolator.SetLocator(locator)
interpolator.PassFieldArraysOff()
interpolator.PassFieldArraysOn()
interpolator.SetNullPointsStrategy(null_strategy)
interpolator.SetNullValue(null_value)
interpolator.SetValidPointsMaskArrayName("ValidPointMask")
Expand Down
175 changes: 154 additions & 21 deletions vedo/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,24 +959,35 @@ def subdivide(self, n=1, method=0, mel=None):
)
return self

def decimate(self, fraction=0.5, n=None, method="quadric", boundaries=False):

def decimate(self, fraction=0.5, n=None, preserve_volume=True, regularize=True):
"""
Downsample the number of vertices in a mesh to `fraction`.
This filter preserves the `pointdata` of the input dataset.
Arguments:
fraction : (float)
the desired target of reduction.
n : (int)
the desired number of final points (`fraction` is recalculated based on it).
method : (str)
can be either 'quadric' or 'pro'. In the first case triagulation
will look like more regular, irrespective of the mesh original curvature.
In the second case triangles are more irregular but mesh is more precise on more
curved regions.
boundaries : (bool)
in "pro" mode decide whether to leave boundaries untouched or not
the desired number of final points
(`fraction` is recalculated based on it).
preserve_volume : (bool)
Decide whether to activate volume preservation which greatly
reduces errors in triangle normal direction.
regularize : (bool)
regularize the point finding algorithm so as to have better quality
mesh elements at the cost of a slightly lower precision on the
geometry potentially (mostly at sharp edges).
Can be useful for decimating meshes that have been triangulated on noisy data.
.. note:: Setting `fraction=0.1` leaves 10% of the original number of vertices
Note:
Setting `fraction=0.1` leaves 10% of the original number of vertices.
Internally the VTK class
[vtkQuadricDecimation](https://vtk.org/doc/nightly/html/classvtkQuadricDecimation.html)
is used for this operation.
See also: `decimate_binned()` and `decimate_pro()`.
"""
poly = self.dataset
if n: # N = desired number of points
Expand All @@ -985,29 +996,151 @@ def decimate(self, fraction=0.5, n=None, method="quadric", boundaries=False):
if fraction >= 1:
return self

if "quad" in method:
decimate = vtk.new("QuadricDecimation")
# decimate.SetVolumePreservation(True)
decimate = vtk.new("QuadricDecimation")
decimate.SetVolumePreservation(preserve_volume)
decimate.SetRegularize(regularize)
decimate.MapPointDataOn()

else:
decimate = vtk.new("DecimatePro")
decimate.PreserveTopologyOn()
if boundaries:
decimate.BoundaryVertexDeletionOff()
else:
decimate.BoundaryVertexDeletionOn()
decimate.SetInputData(poly)
decimate.SetTargetReduction(1 - fraction)
decimate.SetInputData(poly)
decimate.Update()

self._update(decimate.GetOutput())
self.metadata["decimate_actual_fraction"] = 1 - decimate.GetActualReduction()

self.pipeline = OperationNode(
"decimate",
parents=[self],
comment=f"#pts {self.dataset.GetNumberOfPoints()}",
)
return self

def decimate_pro(
self,
fraction=0.5,
n=None,
preserve_topology=True,
preserve_boundaries=True,
splitting=False,
splitting_angle=75,
feature_angle=0,
inflection_point_ratio=10,
):
"""
Downsample the number of vertices in a mesh to `fraction`.
This filter preserves the `pointdata` of the input dataset.
Arguments:
fraction : (float)
The desired target of reduction.
Setting `fraction=0.1` leaves 10% of the original number of vertices.
n : (int)
the desired number of final points (`fraction` is recalculated based on it).
preserve_topology : (bool)
If on, mesh splitting and hole elimination will not occur.
This may limit the maximum reduction that may be achieved.
preserve_boundaries : (bool)
Turn on/off the deletion of vertices on the boundary of a mesh.
Control whether mesh boundaries are preserved during decimation.
feature_angle : (float)
Specify the angle that defines a feature.
This angle is used to define what an edge is
(i.e., if the surface normal between two adjacent triangles
is >= FeatureAngle, an edge exists).
splitting : (bool)
Turn on/off the splitting of the mesh at corners,
along edges, at non-manifold points, or anywhere else a split is required.
Turning splitting off will better preserve the original topology of the mesh,
but you may not obtain the requested reduction.
splitting_angle : (float)
Specify the angle that defines a sharp edge.
This angle is used to control the splitting of the mesh.
A split line exists when the surface normals between two edge connected triangles
are >= `splitting_angle`.
inflection_point_ratio : (float)
An inflection point occurs when the ratio of reduction error between two iterations
is greater than or equal to the `inflection_point_ratio` value.
Note:
Setting `fraction=0.1` leaves 10% of the original number of vertices
See also:
`decimate()` and `decimate_binned()`.
"""
poly = self.dataset
if n: # N = desired number of points
npt = poly.GetNumberOfPoints()
fraction = n / npt
if fraction >= 1:
return self

decimate = vtk.new("DecimatePro")
decimate.SetPreserveTopology(preserve_topology)
decimate.SetBoundaryVertexDeletion(preserve_boundaries)
if feature_angle:
decimate.SetFeatureAngle(feature_angle)
decimate.SetSplitting(splitting)
decimate.SetSplitAngle(splitting_angle)
decimate.SetInflectionPointRatio(inflection_point_ratio)

decimate.SetTargetReduction(1 - fraction)
decimate.SetInputData(poly)
decimate.Update()
self._update(decimate.GetOutput())

self.pipeline = OperationNode(
"decimate_pro",
parents=[self],
comment=f"#pts {self.dataset.GetNumberOfPoints()}",
)
return self

def decimate_binned(self, divisions=(), use_clustering=False):
"""
Downsample the number of vertices in a mesh.
This filter preserves the `celldata` of the input dataset,
if `use_clustering=True` also the `pointdata` will be preserved in the result.
Arguments:
divisions : (list)
number of divisions along x, y and z axes.
auto_adjust : (bool)
if True, the number of divisions is automatically adjusted to
create more uniform cells.
use_clustering : (bool)
use [vtkQuadricClustering](https://vtk.org/doc/nightly/html/classvtkQuadricClustering.html)
instead of
[vtkBinnedDecimation](https://vtk.org/doc/nightly/html/classvtkBinnedDecimation.html).
See also: `decimate()` and `decimate_pro()`.
"""
if use_clustering:
decimate = vtk.new("QuadricClustering")
decimate.CopyCellDataOn()
else:
decimate = vtk.new("BinnedDecimation")
decimate.ProducePointDataOn()
decimate.ProduceCellDataOn()

decimate.SetInputData(self.dataset)

if len(divisions) == 0:
decimate.SetAutoAdjustNumberOfDivisions(1)
else:
decimate.SetAutoAdjustNumberOfDivisions(0)
decimate.SetNumberOfDivisions(divisions)
decimate.Update()

self._update(decimate.GetOutput())
self.metadata["decimate_binned_divisions"] = decimate.GetNumberOfDivisions()
self.pipeline = OperationNode(
"decimate_binned",
parents=[self],
comment=f"#pts {self.dataset.GetNumberOfPoints()}",
)
return self

def delete_cells(self, ids):
"""
Expand Down
23 changes: 22 additions & 1 deletion vedo/pointcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
__all__ = [
"Points",
"Point",
"CellCenters",
"merge",
"delaunay2d", # deprecated, use .generate_delaunay2d()
"fit_line",
Expand Down Expand Up @@ -3579,6 +3580,26 @@ def visible_points(self, area=(), tol=None, invert=False):
svp.SelectInvisibleOn()
svp.Update()

m = Points(svp.GetOutput())#.point_size(5)
m = Points(svp.GetOutput())
m.name = "VisiblePoints"
return m

####################################################
class CellCenters(Points):
def __init__(self, pcloud):
"""
Generate `Points` at the center of the cells of any type of object.
Check out also `cell_centers()`.
"""
vcen = vtk.new("CellCenters")
vcen.CopyArraysOn()
vcen.VertexCellsOn()
# vcen.ConvertGhostCellsToGhostPointsOn()
try:
vcen.SetInputData(pcloud.dataset)
except AttributeError:
vcen.SetInputData(pcloud)
vcen.Update()
super().__init__(vcen.GetOutput())
self.name = "CellCenters"
2 changes: 1 addition & 1 deletion vedo/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
_version = '2023.5.0+dev16'
_version = '2023.5.0+dev17'
2 changes: 2 additions & 0 deletions vedo/vtkclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@
"vtk3DLinearGridCrinkleExtractor",
"vtkAppendFilter",
"vtkAppendPolyData",
"vtkBinnedDecimation",
"vtkCellCenters",
"vtkCellDataToPointData",
"vtkCenterOfMass",
Expand All @@ -272,6 +273,7 @@
"vtkPointDataToCellData",
"vtkPolyDataNormals",
"vtkProbeFilter",
"vtkQuadricClustering",
"vtkQuadricDecimation",
"vtkResampleWithDataSet",
"vtkReverseSense",
Expand Down

0 comments on commit eff9653

Please sign in to comment.