Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove non-manifold edges before Boolean mesh operation #35

Closed
lassoan opened this issue Mar 10, 2021 · 30 comments
Closed

Remove non-manifold edges before Boolean mesh operation #35

lassoan opened this issue Mar 10, 2021 · 30 comments
Assignees

Comments

@lassoan
Copy link
Collaborator

lassoan commented Mar 10, 2021

vtkbool fails (returns empty mesh or meshes with errors) for some Boolean mesh operations. We work together with vtkbool developer to identify and fix the issues.

It seems that vtkbool expects input meshes to not have any non-manifold edges (which is completely reasonable). We need to test if we detect non-manifold edges using vtkCleanPolyData and vtkFeatureEdges and then maybe stitch the holes using vtkFillHolesFilter solves the problem - see zippy84/vtkbool#40 (comment).

@lassoan
Copy link
Collaborator Author

lassoan commented Mar 10, 2021

@mauigna06 please try this as soon as possible. It is really unique that maintainer of vtkbool is personally willing to investigate issues with our data sets, so we need to do everything we can to keep him interested and respond to him quickly. Can you try this today, or latest tomorrow?

@mauigna06
Copy link
Collaborator

@lassoan I'm out of town today. Tomorrow I can start working on this. I hope that's okey.
I looked for examples of vtkCleanPolyData and there aren't so I plan to use it with all the optional flags on.
I haven't researched how vtkFeatureEdges and vtkFillHolesFilter are used. But after that, if I have doubts I'll ask you if you don't mind

@mauigna06
Copy link
Collaborator

OK, thans for the clarification. I'll check if vtkFeatureEdges can correctly detect this edge and if we can fix this by removing the edge and fill holes.

So you want me to use vtkFeatureEdges to detect non manifold edges in the mesh "guide-dec.vtk" that we know has non manifold edges because of paraview? Should I check if the output of that filter has a different than zero point count to be sure we detect the non manifold edges? How do I remove that non manifold edges? Should I use the output of vtkFeatureEdges as input for vtkRemovePolyData? After that I should use vtkFillHolesFilter?

What benefit would make using vtkCleanPolyData for this mesh?

@lassoan
Copy link
Collaborator Author

lassoan commented Mar 10, 2021

We already knew that decimated meshes may contain non-manifold edges, we just did not know how much vtkbool is sensitive to presence of those. Now we know that it is sensitive (which is not surprising) and we need to take care of it.

Removing duplicate points, removing non-manifold edges, and filling small holes are the most commonly needed mesh cleaning operations and probably users have asked about it on the VTK mailing list/forum many times, so I would start with some web searching.

I've also asked on the VTK forum, hopefully we'll hear back (but I don't have high hopes): https://discourse.vtk.org/t/remove-faces-in-contact-with-non-manifold-edges/4116/2

Removing an edge is not trivial, because you need to remove all cells that has that edge. So, you need to find the edges, then find the cells, and then use vtkExtractSelection or vtkThreshold to filter out those cells from the mesh. Since removing cells opens holes in the mesh, we probably need to apply hole filling to eliminate these holes (hopefully the hole filling does not introduce non-manifold edges).

Duplicate points (multiple points at the same position) may confuse mesh processing algorithms. vtkCleanPolyData removes these duplicate points. This may be needed because for example vtkPolyDataNormals duplicates points at sharp edges to allow sharp changes in normal directions at edges.

@mauigna06
Copy link
Collaborator

Andras, thank you for such a complete answer.

I don't understand what is my homework for tomorrow exactly

@lassoan
Copy link
Collaborator Author

lassoan commented Mar 11, 2021

Task for tomorrow: test if you can remove non-manifold edges with combination of VTK filters so that the resulting mesh can be processed correctly using Combine models module.

@mauigna06
Copy link
Collaborator

mauigna06 commented Mar 11, 2021

@lassoan, this is my code to try do that:

fibula = getNode('fibula')
guide_dec = getNode('guide-dec')
featureEdges = vtk.vtkFeatureEdges()
featureEdges.SetInputData(guide_dec.GetPolyData())
featureEdges.BoundaryEdgesOff()
featureEdges.FeatureEdgesOff()
featureEdges.ManifoldEdgesOff()
featureEdges.NonManifoldEdgesOn()
featureEdges.Update()
nonManifoldEdgesPolyData = featureEdges.GetOutput()

nonManifoldEdges = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLModelNode','non-manifold edges of guide-dec')
nonManifoldEdges.CreateDefaultDisplayNodes()
nonManifoldEdges.SetAndObservePolyData(featureEdges.GetOutput())

ids = vtk.vtkIdTypeArray()
ids.SetNumberOfComponents(1)

for i in range(nonManifoldEdgesPolyData.GetNumberOfPoints()):
  pointOfNonManifoldEdges = nonManifoldEdges.GetPolyData().GetPoints().GetPoint(i)
  pointIdInOriginal = guide_dec.GetPolyData().FindPoint(pointOfNonManifoldEdges)
  ids.InsertNextValue(pointIdInOriginal)

selectionNode = vtk.vtkSelectionNode()
selectionNode.SetFieldType(vtk.vtkSelectionNode.POINT);
selectionNode.SetContentType(vtk.vtkSelectionNode.INDICES);
selectionNode.SetSelectionList(ids);
selectionNode.GetProperties().Set(vtk.vtkSelectionNode.CONTAINING_CELLS(), 1)
selectionNode.GetProperties().Set(vtk.vtkSelectionNode.INVERSE(), 1)

selection =vtk.vtkSelection()
selection.AddNode(selectionNode);

extractSelection = vtk.vtkExtractSelection()
extractSelection.SetInputConnection(0, guide_dec.GetPolyDataConnection())
extractSelection.SetInputData(1, selection)
extractSelection.Update()

geometryFilter = vtk.vtkGeometryFilter()
geometryFilter.SetInputData(extractSelection.GetOutput())
geometryFilter.Update()

selectedTriangles = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLModelNode','selected triangles')
selectedTriangles.CreateDefaultDisplayNodes()
selectedTriangles.SetAndObservePolyData(geometryFilter.GetOutput())

filler = vtk.vtkFillHolesFilter()
filler.SetInputData(geometryFilter.GetOutput())
filler.SetHoleSize(10)
filler.Update()

filledHoles = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLModelNode','filled holes')
filledHoles.CreateDefaultDisplayNodes()
filledHoles.SetAndObservePolyData(filler.GetOutput())

cleanFilter = vtk.vtkCleanPolyData()
cleanFilter.SetInputData(filler.GetOutput())
cleanFilter.ConvertLinesToPointsOn()
cleanFilter.ConvertPolysToLinesOn()
cleanFilter.ConvertStripsToPolysOn()
cleanFilter.Update()

cleaned = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLModelNode','cleaned')
cleaned.CreateDefaultDisplayNodes()
cleaned.SetAndObservePolyData(filler.GetOutput())

featureEdges2 = vtk.vtkFeatureEdges()
featureEdges2.SetInputData(cleaned.GetPolyData())
featureEdges2.BoundaryEdgesOff()
featureEdges2.FeatureEdgesOff()
featureEdges2.ManifoldEdgesOff()
featureEdges2.NonManifoldEdgesOn()
featureEdges2.Update()
featureEdges2.GetOutput().GetNumberOfPoints()

I was able to delete the cells that contained the points of the non-manifold edges.
Triangles from filled holes have inverted normals (I don't know if that's a problem).
If filler parameter is bigger than 15, it fills the slit of the surgicalGuide so you have to be careful with that. But it doesn't fill all the holes made by the selection.

Making boolean difference (using the CombineModels module) to 'cleaned' with 'fibula' fails

There were answers to this post: https://discourse.vtk.org/t/remove-faces-in-contact-with-non-manifold-edges/4116/2

@lassoan
Copy link
Collaborator Author

lassoan commented Mar 11, 2021

This is very nice, thanks for diving into this.

Triangles from filled holes have inverted normals (I don't know if that's a problem).

You can fix that by running vtkPolyDataNormals (with SplittingOff, to prevent point duplication).

Can you attach a few screenshots that show the non-manifold edges (zoomed out to show where they are, zoomed in to show its size and shape)?

Have you tried combining meshes on the result of removal of non-manifold edges, without hole filling?

The code cuts out bigger holes than needed. Now the script removes all the cells that contains any of the points that are used in a feature edge. But a point may be used in many edges and only a very few of them are removed. Could you update the script with an additional filtering step?

  • Use vtkSelectionNode (without INVERSE) to get the list of cells that potentially need to be removed
  • Iterate through these cells and check if they contain a non-manifold edge (list of cell points contain the two non-manifold edge points right next to each other). If they actually contain a non-manifold edge then add the cell ID (the "pedigree ID" - cell ID in the input mesh) to the list of cells that are confirmed that are needed to be removed.
  • Remove cells that are confirmed for removal (using selection or threshold filter)

I'll follow up on the discourse post once we see how well your script can solve this.

@lassoan
Copy link
Collaborator Author

lassoan commented Mar 11, 2021

I've been looking at the models and I'm wondering if we bring the problems upon to ourselves by suboptimal processing. Specifically, the non-manifold edges seem to be created by decimation. If we can find other ways to speed up previews (e.g., not performing Boolean mesh operations for preview, or lower the segmentation resolution or decimate the bone mesh and not the combined meshes) and we use full-resolution, non-decimated meshes for generating the final guide then maybe we will not run into meshing problems.

@mauigna06
Copy link
Collaborator

mauigna06 commented Mar 11, 2021

I've been looking at the models and I'm wondering if we bring the problems upon to ourselves by suboptimal processing.

Yes. I think we can leave this for now

Specifically, the non-manifold edges seem to be created by decimation.

Do you know if these non-manifold edges will affect the dynamic modeler plane cuts when we replace the fibulaMesh by the decimatedFibulaMesh?

not performing Boolean mesh operations for preview

I think this is okey since the surgicalGuide is created only after all the surgicalPlanning and after all the miterBoxes positioning so the user can check if the position of the miterBoxes is okey before clicking "Make boolean operations to surgical guide base with screwHolesCylinders and miterBoxes"

lower the segmentation resolution

It is an option

decimate the bone mesh and not the combined meshes

Would these meshes need to be processed (delete non-manifold edges, vtkCleanPolyData, etc)?

we use full-resolution, non-decimated meshes for generating the final guide then maybe we will not run into meshing problems.

I agree

@mauigna06
Copy link
Collaborator

Andras just let me know if you want me to keep working on this

@lassoan
Copy link
Collaborator Author

lassoan commented Mar 14, 2021

Yes, please test if latest Combine models works well for non-decimated meshes. If you find any errors then upload the meshes somewhere and post the link here. I'll take care of the rest.

@mauigna06
Copy link
Collaborator

mauigna06 commented Mar 14, 2021

Thank you Andras.
I downloaded the latest version of the Sandbox Extension and I'm using Slicer 4.13.0-2012-03-09 preview release.
vktbool has improved a lot but still there are a few errors in the "result" of difference of "FibulaSurgicalGuidePrototype_3" with "fibula".
Here is a link to the meshes: https://gofile.io/d/cT8Pw8

P.S. We can make mandible surgical guides now with our module, checkout the last pull request

@mauigna06
Copy link
Collaborator

Andras tell me if you could open the meshes because I created them with a preview release of Slicer and I found stable releases couldn't open one of those files and Slicer crashed

@mauigna06
Copy link
Collaborator

Just in case someone needs it, here is a script that removes non-manifold edges added by decimation.

guide_dec = getNode('guide-dec')

idFilter = vtk.vtkIdFilter()
idFilter.SetInputData(guide_dec.GetMesh());
idFilter.SetPointIds(True)
idFilter.SetCellIds(False)
idFilter.SetPointIdsArrayName("PointIds")
idFilter.SetCellIdsArrayName("CellIds")
idFilter.Update()

nonManifoldEdgesFilter = vtk.vtkFeatureEdges()
nonManifoldEdgesFilter.SetInputData(idFilter.GetOutput())
nonManifoldEdgesFilter.BoundaryEdgesOff()
nonManifoldEdgesFilter.FeatureEdgesOff()
nonManifoldEdgesFilter.ManifoldEdgesOff()
nonManifoldEdgesFilter.NonManifoldEdgesOn()
nonManifoldEdgesFilter.Update()

nonManifoldPointids = nonManifoldEdgesFilter.GetOutput().GetPointData().GetArray("PointIds")
nonManifoldPointids.GetNumberOfValues()

edgesFilter = vtk.vtkFeatureEdges()
edgesFilter.SetInputData(idFilter.GetOutput())
edgesFilter.BoundaryEdgesOff()
edgesFilter.FeatureEdgesOff()
edgesFilter.ManifoldEdgesOn()
edgesFilter.NonManifoldEdgesOn()
edgesFilter.Update()

allPointids = edgesFilter.GetOutput().GetPointData().GetArray("PointIds")

ids = vtk.vtkIdTypeArray()
ids.SetNumberOfComponents(1)
for i in range(nonManifoldPointids.GetNumberOfValues()):
    nonManifoldPointIDFound = True
    for j in range(allPointids.GetNumberOfValues()):
        if int(nonManifoldPointids.GetTuple1(i)) == int(allPointids.GetTuple1(i)):
            nonManifoldPointIDFound = False
            break
    ids.InsertNextValue(int(nonManifoldPointids.GetTuple1(i)))

selectionNode = vtk.vtkSelectionNode()
selectionNode.SetFieldType(vtk.vtkSelectionNode.POINT)
selectionNode.SetContentType(vtk.vtkSelectionNode.INDICES)
selectionNode.SetSelectionList(ids);
selectionNode.GetProperties().Set(vtk.vtkSelectionNode.CONTAINING_CELLS(), 1)
selectionNode.GetProperties().Set(vtk.vtkSelectionNode.INVERSE(), 1)

selection = vtk.vtkSelection()
selection.AddNode(selectionNode);

extractSelection = vtk.vtkExtractSelection()
extractSelection.SetInputConnection(0, guide_dec.GetPolyDataConnection())
extractSelection.SetInputData(1, selection)
extractSelection.Update()

geometryFilter = vtk.vtkGeometryFilter()
geometryFilter.SetInputData(extractSelection.GetOutput())
geometryFilter.Update()

# test if the removal of non manifold edges did work
featureEdges2 = vtk.vtkFeatureEdges()
featureEdges2.SetInputData(geometryFilter.GetOutput())
featureEdges2.BoundaryEdgesOff()
featureEdges2.FeatureEdgesOff()
featureEdges2.ManifoldEdgesOff()
featureEdges2.NonManifoldEdgesOn()
featureEdges2.Update()
featureEdges2.GetOutput().GetNumberOfPoints()

# save the model without non-manifold edges
guide_dec.SetAndObserveMesh(geometryFilter.GetOutput())

@marcomusy
Copy link

Hi @mauigna06 thanks for sharing this code, I've been struggling with this problem lately in marcomusy/vedo#663 and could not find a satisfactory solution.

I'm not sure if there's any typo in the code you posted, shouldn't it be instead
if int(nonManifoldPointids.GetTuple1(i)) == int(allPointids.GetTuple1(j)):
And how is nonManifoldPointIDFound used in the code? (it seems it's not used!)
I tried it on a sample mesh (you can find it in the vedo issue) and i get this:

import vtk
import vedo

poly = vedo.Mesh('small_dino.obj').polydata()

idFilter = vtk.vtkIdFilter()
idFilter.SetInputData(poly);
idFilter.SetPointIds(True)
idFilter.SetCellIds(False)
idFilter.SetPointIdsArrayName("PointIds")
idFilter.SetCellIdsArrayName("CellIds")
idFilter.Update()

nonManifoldEdgesFilter = vtk.vtkFeatureEdges()
nonManifoldEdgesFilter.SetInputData(idFilter.GetOutput())
nonManifoldEdgesFilter.BoundaryEdgesOff()
nonManifoldEdgesFilter.FeatureEdgesOff()
nonManifoldEdgesFilter.ManifoldEdgesOff()
nonManifoldEdgesFilter.NonManifoldEdgesOn()
nonManifoldEdgesFilter.Update()

#vedo.show(nonManifoldEdgesFilter.GetOutput()).close()

nonManifoldPointids = nonManifoldEdgesFilter.GetOutput().GetPointData().GetArray("PointIds")

edgesFilter = vtk.vtkFeatureEdges()
edgesFilter.SetInputData(idFilter.GetOutput())
edgesFilter.BoundaryEdgesOff()
edgesFilter.FeatureEdgesOff()
edgesFilter.ManifoldEdgesOn()
edgesFilter.NonManifoldEdgesOn()
edgesFilter.Update()

allPointids = edgesFilter.GetOutput().GetPointData().GetArray("PointIds")

ids = vtk.vtkIdTypeArray()
ids.SetNumberOfComponents(1)
for i in range(nonManifoldPointids.GetNumberOfValues()):
    nonManifoldPointIDFound = True
    for j in range(allPointids.GetNumberOfValues()):
        if int(nonManifoldPointids.GetTuple1(i)) == int(allPointids.GetTuple1(j)):
            nonManifoldPointIDFound = False
            break
    ids.InsertNextValue(int(nonManifoldPointids.GetTuple1(i)))

selectionNode = vtk.vtkSelectionNode()
selectionNode.SetFieldType(vtk.vtkSelectionNode.POINT)
selectionNode.SetContentType(vtk.vtkSelectionNode.INDICES)
selectionNode.SetSelectionList(ids);
selectionNode.GetProperties().Set(vtk.vtkSelectionNode.CONTAINING_CELLS(), 1)
selectionNode.GetProperties().Set(vtk.vtkSelectionNode.INVERSE(), 1)

selection = vtk.vtkSelection()
selection.AddNode(selectionNode);

extractSelection = vtk.vtkExtractSelection()
extractSelection.SetInputData(0, poly)
extractSelection.SetInputData(1, selection)
extractSelection.Update()

geometryFilter = vtk.vtkGeometryFilter()
geometryFilter.SetInputData(extractSelection.GetOutput())
geometryFilter.Update()

vedo.show(geometryFilter.GetOutput()).close()

Screenshot from 2022-07-25 12-24-56

@mauigna06
Copy link
Collaborator

Hi @marcomusy. Thank you for testing. Please try this updated version (it should be more understandable) and tell me if it works:

guide_dec = getNode("FibulaSurgicalGuidePrototype_1_1")

idFilter = vtk.vtkIdFilter()
idFilter.SetInputData(guide_dec.GetMesh())
idFilter.SetPointIds(True)
idFilter.SetCellIds(False)
idFilter.SetPointIdsArrayName("PointIds")
idFilter.SetCellIdsArrayName("CellIds")
idFilter.Update()

nonManifoldEdgesFilter = vtk.vtkFeatureEdges()
nonManifoldEdgesFilter.SetInputData(idFilter.GetOutput())
nonManifoldEdgesFilter.BoundaryEdgesOff()
nonManifoldEdgesFilter.FeatureEdgesOff()
nonManifoldEdgesFilter.ManifoldEdgesOff()
nonManifoldEdgesFilter.NonManifoldEdgesOn()
nonManifoldEdgesFilter.Update()

nonManifoldPointids = (
    nonManifoldEdgesFilter.GetOutput().GetPointData().GetArray("PointIds")
)
nonManifoldPointids.GetNumberOfValues()

manifoldEdgesFilter = vtk.vtkFeatureEdges()
manifoldEdgesFilter.SetInputData(idFilter.GetOutput())
manifoldEdgesFilter.BoundaryEdgesOff()
manifoldEdgesFilter.FeatureEdgesOff()
manifoldEdgesFilter.ManifoldEdgesOn()
manifoldEdgesFilter.NonManifoldEdgesOff()
manifoldEdgesFilter.Update()

manifoldPointids = manifoldEdgesFilter.GetOutput().GetPointData().GetArray("PointIds")

ids = vtk.vtkIdTypeArray()
ids.SetNumberOfComponents(1)
for i in range(nonManifoldPointids.GetNumberOfValues()):
    sharedPointIDFound = False
    for j in range(manifoldPointids.GetNumberOfValues()):
        if int(nonManifoldPointids.GetTuple1(i)) == int(manifoldPointids.GetTuple1(j)):
            nonManifoldPointIDFound = True
            break
    if not sharedPointIDFound:
          ids.InsertNextValue(int(nonManifoldPointids.GetTuple1(i)))

selectionNode = vtk.vtkSelectionNode()
selectionNode.SetFieldType(vtk.vtkSelectionNode.POINT)
selectionNode.SetContentType(vtk.vtkSelectionNode.INDICES)
selectionNode.SetSelectionList(ids)
selectionNode.GetProperties().Set(vtk.vtkSelectionNode.CONTAINING_CELLS(), 1)
selectionNode.GetProperties().Set(vtk.vtkSelectionNode.INVERSE(), 1)

selection = vtk.vtkSelection()
selection.AddNode(selectionNode)

extractSelection = vtk.vtkExtractSelection()
extractSelection.SetInputConnection(0, guide_dec.GetPolyDataConnection())
extractSelection.SetInputData(1, selection)
extractSelection.Update()

geometryFilter = vtk.vtkGeometryFilter()
geometryFilter.SetInputData(extractSelection.GetOutput())
geometryFilter.Update()

# test if the removal of non manifold edges did work
featureEdges2 = vtk.vtkFeatureEdges()
featureEdges2.SetInputData(geometryFilter.GetOutput())
featureEdges2.BoundaryEdgesOff()
featureEdges2.FeatureEdgesOff()
featureEdges2.ManifoldEdgesOff()
featureEdges2.NonManifoldEdgesOn()
featureEdges2.Update()
featureEdges2.GetOutput().GetNumberOfPoints()

# save the model without non-manifold edges
guide_dec.SetAndObserveMesh(geometryFilter.GetOutput())

@marcomusy
Copy link

Hi Mauro, I basically get the same output..

Maybe the problem is in vtkSelectionNode.CONTAINING_CELLS(), if 2 non-manifold points from 2 independent cells are so close that the are part of another same cell, then that cell is dropped too, causing a hole. That's because CONTAINING_CELLS flags cells for ANY not ALL points.

@mauigna06
Copy link
Collaborator

mauigna06 commented Jul 25, 2022

Yeah. I checked on the testing data of the issue of vedo and I couldn't get it yet... I'll keep trying on my free time

@ttsesm
Copy link

ttsesm commented Aug 26, 2022

@mauigna06 any update on the mentioned problem from @marcomusy?

@lassoan
Copy link
Collaborator Author

lassoan commented Aug 26, 2022

@mauigna06 has taken some time off. You can try to ping him again in a week or two.

@ttsesm
Copy link

ttsesm commented Sep 28, 2022

@mauigna06 any update?

@mauigna06
Copy link
Collaborator

Hi @ttsesm. I couldn't solve it yet and it's not a feature that is essencial to any of my projects so maybe I'll try again on weekends but it's not probable

@ttsesm
Copy link

ttsesm commented Oct 6, 2022

Hi @ttsesm. I couldn't solve it yet and it's not a feature that is essencial to any of my projects so maybe I'll try again on weekends but it's not probable

Thanks for the reply Mauro. I do not understand though, is this a vtk issue? Also did you check what Marco is suggesting.

@AndreAhmed
Copy link

Would someone help with c++ code for that purpose ?

@AndreAhmed
Copy link

AndreAhmed commented Dec 9, 2022

I tried to convert the above python code to c++
but still have very bad output

	vtkNew<vtkIdFilter> idFilter;
	idFilter->SetInputData(extrude_vornoi->GetOutput());
	idFilter->SetPointIds(true);
	idFilter->SetCellIds(false);
	idFilter->SetPointIdsArrayName("PointIds");
	idFilter->SetCellIdsArrayName("CellIds");
	idFilter->Update();


	vtkNew<vtkFeatureEdges> nonManifoldEdgesFilter;
	nonManifoldEdgesFilter->SetInputData(idFilter->GetOutput());
	nonManifoldEdgesFilter->BoundaryEdgesOff();
	nonManifoldEdgesFilter->FeatureEdgesOff();
	nonManifoldEdgesFilter->ManifoldEdgesOff();
	nonManifoldEdgesFilter->NonManifoldEdgesOn();
	nonManifoldEdgesFilter->Update();

	vtkDataArray* nonManifoldPointids = nonManifoldEdgesFilter->GetOutput()->GetPointData()->GetArray("PointIds");
	
	vtkNew<vtkFeatureEdges> manifoldEdgesFilter;
	manifoldEdgesFilter->SetInputData(idFilter->GetOutput());
	manifoldEdgesFilter->BoundaryEdgesOff();
	manifoldEdgesFilter->FeatureEdgesOff();
	manifoldEdgesFilter->ManifoldEdgesOff();
	manifoldEdgesFilter->NonManifoldEdgesOn();
	manifoldEdgesFilter->Update();

	vtkDataArray* manifoldPointids = manifoldEdgesFilter->GetOutput()->GetPointData()->GetArray("PointIds");

	vtkNew<vtkIdTypeArray> ids;
	ids->SetNumberOfComponents(1);
	for (int i = 0; i < nonManifoldPointids->GetNumberOfValues(); i++)
	{
		bool sharedPointIDFound = false;
		bool nonManifoldPointIDFound = false;
		for (int j = 0; j < manifoldPointids->GetNumberOfValues(); j++)
		{
			if ((int)nonManifoldPointids->GetTuple1(i) == (int)manifoldPointids->GetTuple1(j))
			{
				nonManifoldPointIDFound = true;
				break;
			}
			if (!sharedPointIDFound)
			{
				ids->InsertNextValue((int)nonManifoldPointids->GetTuple1(i));
			}
		}
	}

	vtkNew<vtkSelectionNode> selectionNode;
	selectionNode->SetFieldType(vtkSelectionNode::POINT);
	selectionNode->SetContentType(vtkSelectionNode::INDICES);
	selectionNode->SetSelectionList(ids);
	selectionNode->GetProperties()->Set(vtkSelectionNode::CONTAINING_CELLS(), 1);
	selectionNode->GetProperties()->Set(vtkSelectionNode::INVERSE(), 1);

	vtkNew<vtkSelection> selection;
	selection->AddNode(selectionNode);

	vtkNew<vtkExtractSelection> extractSelection;
	extractSelection->SetInputConnection(0, extrude_vornoi->GetOutputPort());
	extractSelection->SetInputData(1, selection);
	extractSelection->Update();

	vtkNew<vtkGeometryFilter> geometryFilter;
	geometryFilter->SetInputData(extractSelection->GetOutput());
	geometryFilter->Update();

	vtkNew<vtkOBJWriter> plyWriter;

	plyWriter->SetFileName("curve2.obj");
	plyWriter->SetInputData(geometryFilter->GetOutput());
	plyWriter->Write();```

@mauigna06
Copy link
Collaborator

Hi @AndreAhmed.

I never tried again to solve this problem. You are right the code you quoted does not work

Best wishes

@marcomusy
Copy link

I developed this function for vedo, in case you're interested - should be easy to unpack the logic to translate it to pure vtk
marcomusy/vedo#813 (comment)
it seems to work fine in relatively simple situations.

@mauigna06
Copy link
Collaborator

Hi @marcomusy
Thanks a lot for the update!

@marcomusy
Copy link

marcomusy commented Feb 25, 2023

..and this is better version which also does repairing (not 100% perfect, but not so bad!):
https://github.com/marcomusy/vedo/blob/204bf357a43d7c476f55e24d37f8aa2300e3fdf2/vedo/mesh.py#L749

from vedo import *

msh = Mesh('piece_1.obj')
# msh = Mesh('small_dino.obj')
msh.backcolor("purple5").linewidth(1)

print("msh is_manifold?", msh.is_manifold())
msh.non_manifold_faces(tol=0.2, remove=True)
print("msh is_manifold?", msh.is_manifold())

# msh.cmap("Reds", "NonManifoldCell", on="cells")
bb = msh.boundaries(non_manifold_edges=True, boundary_edges=False)
show(msh, bb, axes=1)

Screenshot 2023-02-25 at 03 25 28

piece_1.obj.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants