vtkClipPolyData incorrectly work

I am using vtkClipPolyData to cut a mesh, but it give a incorrect result.

The code is:

import vtkmodules.all as vtk
import numpy as np

def CreateActor(ply: vtk.vtkPolyData, color=[1, 0, 0]):

    mapper = vtk.vtkPolyDataMapper()
    mapper.SetInputData(ply)
    mapper.ScalarVisibilityOff()

    actor = vtk.vtkActor()
    actor.SetMapper(mapper)

    actor.GetProperty().SetColor(color[0], color[1], color[2])
    actor.GetProperty().SetOpacity(0.3)
    return actor

def CutMesh(ply: vtk.vtkPolyData):
    plyClone = vtk.vtkPolyData()
    plyClone.DeepCopy(ply)

    centers = np.array([
        [25.54791, 131.9023, 570.03764],
        [46.95663, 83.99182, 511.02341],
        [58.50379, 42.54272, 577.24325],
        [37.09508, 90.45326, 636.25748],
    ])

    ############################################################################
    ########## after remove the minus sign, this function output a wrong cut result
    normals = -np.array([  # NOTE THE MINUS SIGN
        [0.34503, -0.93555, 0.07544],
        [-0.07840, 0.05137, 0.99560],
        [-0.34503, 0.93555, -0.07544],
        [0.07840, -0.05137, -0.99560],
    ])
    ############################################################################

    vpoints = vtk.vtkPoints()
    for i in range(4):
        vpoints.InsertNextPoint(centers[i])
    vectors = vtk.vtkFloatArray()
    vectors.SetNumberOfComponents(3)
    for n in normals:
        vectors.InsertNextTuple3(n[0], n[1], n[2])

    planes = vtk.vtkPlanes()
    planes.SetPoints(vpoints)
    planes.SetNormals(vectors)

    clipper = vtk.vtkClipPolyData()
    clipper.SetInputData(plyClone)
    clipper.SetClipFunction(planes)
    clipper.GenerateClippedOutputOff()
    clipper.GenerateClipScalarsOff()
    clipper.SetValue(0)
    clipper.Update()

    newply = clipper.GetOutput()
    return CreateActor(newply, [0, 1, 0])

reader = vtk.vtkOBJReader()
reader.SetFileName('before.obj')
reader.Update()

oriMesh = CreateActor(reader.GetOutput(), [1, 0, 0])
newMesh = CutMesh(reader.GetOutput())

renderer = vtk.vtkRenderer()
renderer.AddActor(oriMesh)
renderer.AddActor(newMesh)
renderer.SetBackground(1, 1, 1)


renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(renderer)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
iren.Initialize()
iren.Start()

The data is: https://github.com/zhang-qiang-github/data/blob/main/before.obj

The result is correct:

image
Fig. 1

The whole mesh is the original mesh. The yellow mesh is the cutted result.

However, if I remove the minu sign for normals, for example:


    ############################################################################
    ########## after remove the minus sign, this function output a wrong cutted result
    normals = np.array([  # NOTE THE MINUS SIGN
        [0.34503, -0.93555, 0.07544],
        [-0.07840, 0.05137, 0.99560],
        [-0.34503, 0.93555, -0.07544],
        [0.07840, -0.05137, -0.99560],
    ])
    ############################################################################

In my opinion, the cutted part should be the red part in Fig. 1. However, the result is:

image

The environment is:

win 10
python 3.9.12
vtk 9.2.6

Any suggestion is appreciated~~~

Just to double check, the following comments from vtkImplicitFunction.h say the following, hopefully this is the convention you are using.

Implicit functions are real valued functions defined in 3D
space, w = F(x,y,z). Two primitive operations are required: the ability to
evaluate the function, and the function gradient at a given point. The
implicit function divides space into three regions: on the surface
(F(x,y,z)=w), outside of the surface (F(x,y,z)>c), and inside the
surface (F(x,y,z)<c). (When c is zero, positive values are outside,
negative values are inside, and zero is on the surface. Note also
that the function gradient points from inside to outside.)

Yes, that is the convention I am using. However, the result is wrong.

It seems you’ve found a problem, thank you. I used “clipper.InsideOutOn()” to get it to work. I also created a quick piece of code to glyph the plane normals with oriented cones (centered on the plane points). With InsideOutOn() I get a better image (with the plane normals shown):

I’m not exactly sure what the problem is at this point (probably vtkPlanes, but not sure without detective work), we’ll have to trace through the code etc. at some point. Can you create an issue so we don’t forget??

Here’s the (ugly) glyphing code:

def ShowPlanes():
showPlanes = vtk.vtkPolyData()

centers = np.array([
    [25.54791, 131.9023, 570.03764],
    [46.95663, 83.99182, 511.02341],
    [58.50379, 42.54272, 577.24325],
    [37.09508, 90.45326, 636.25748],
])

############################################################################
########## after remove the minus sign, this function output a wrong cut result
normals = -np.array([  # NOTE THE MINUS SIGN
    [0.34503, -0.93555, 0.07544],
    [-0.07840, 0.05137, 0.99560],
    [-0.34503, 0.93555, -0.07544],
    [0.07840, -0.05137, -0.99560],
])
############################################################################

vpoints = vtk.vtkPoints()
for i in range(4):
    vpoints.InsertNextPoint(centers[i])
vectors = vtk.vtkFloatArray()
vectors.SetNumberOfComponents(3)
for n in normals:
    vectors.InsertNextTuple3(n[0], n[1], n[2])

showPlanes.SetPoints(vpoints)
showPlanes.GetPointData().SetVectors(vectors);

cone = vtk.vtkConeSource()

glypher = vtk.vtkGlyph3D()
glypher.SetInputData(showPlanes)
glypher.SetSourceConnection(cone.GetOutputPort())
glypher.SetScaleFactor(10)
glypher.Update()

glyphs = glypher.GetOutput()
return CreateActor(glyphs, [0, 0, 1])

I have created an issue: https://gitlab.kitware.com/vtk/vtk/-/issues/19223