A possible bug for vtkPicker

I am using vtkCellPicker in my project. In some situation, the vtkCellPicker do not work. For example:


import vtkmodules.all as vtk

polyData = vtk.vtkPolyData()
polyData.SetPoints(vtk.vtkPoints())
polyData.SetVerts(vtk.vtkCellArray())

polyData.GetPoints().InsertNextPoint(1, 1, 1)
polyData.GetVerts().InsertNextCell(1)
polyData.GetVerts().InsertCellPoint(0)

polyData.GetPoints().Modified()
polyData.GetVerts().Modified()
polyData.Modified()

mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(polyData)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(255, 255, 0)
actor.GetProperty().SetPointSize(10)

#################### add user matrix
# x = [1, 0, 0]
# y = [0, 0, 1]
# z = [0, 1, 0]
# 
# element = [
#     x[0], y[0], z[0], 0,
#     x[1], y[1], z[1], 0,
#     x[2], y[2], z[2], 0,
#     0, 0, 0, 1
# ]
# 
# matrix = vtk.vtkMatrix4x4()
# matrix.DeepCopy(element)
# actor.SetUserMatrix(matrix)
#####################

def mousemove(iren, event):
    pos = iren.GetEventPosition()
    picker = vtk.vtkCellPicker()
    picker.SetTolerance(0.01)
    picker.Pick(pos[0], pos[1], 0, render)
    actor = picker.GetProp3D()
    if actor == None:
        print('no actor')
    else:
        print('pick actor')

render = vtk.vtkRenderer()
render.AddActor(actor)
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(render)
renWin.Render()
iren = vtk.vtkRenderWindowInteractor()
iren.AddObserver('MouseMoveEvent', mousemove)
iren.SetRenderWindow(renWin)
iren.Initialize()
iren.Start()

In the above code, we can pick the actor. However, if I uncommet the code for use matrix:


#################### add user matrix
# x = [1, 0, 0]
# y = [0, 0, 1]
# z = [0, 1, 0]
# 
# element = [
#     x[0], y[0], z[0], 0,
#     x[1], y[1], z[1], 0,
#     x[2], y[2], z[2], 0,
#     0, 0, 0, 1
# ]
# 
# matrix = vtk.vtkMatrix4x4()
# matrix.DeepCopy(element)
# actor.SetUserMatrix(matrix)
#####################

The actor would not be picked.

After looking for the C++ source code for vtkCellPicker, I find the reason is:

In the vtkPicker obtain the scale in line 630:

        this->Transform->SetMatrix(lastMatrix);
        this->Transform->Push();
        this->Transform->Inverse();
        this->Transform->GetScale(scale); // need to scale the tolerance

However, if the user matrix is:

x = [1, 0, 0]
y = [0, 0, 1]
z = [0, 1, 0]

the obtained scale is [-1, -1, -1], which make the actor would never be picked in this->IntersectWithLine() since tol<0.

The GetScale method is tested as:


import vtkmodules.all as vtk
transform = vtk.vtkTransform()

x = [1, 0, 0]
y = [0, 0, 1]
z = [0, 1, 0]

element = [
    x[0], y[0], z[0], 0,
    x[1], y[1], z[1], 0,
    x[2], y[2], z[2], 0,
    0, 0, 0, 1
]

matrix = vtk.vtkMatrix4x4()
matrix.DeepCopy(element)
transform.SetMatrix(matrix)
s = transform.GetScale()
print(s)

Is this a bug? Any suggestion is appreciated~~~

The determinant of the user matrix is -1, which means you turn the actor inside out. I would expect this to completely break the rendering (due to normals pointing in wrong direction, etc.). It is a wonder that rendering still works.

If you want to apply such transform to your actor then you can use vtkTransformPolyDataFilter on the input data.

@lassoan, Thank you for your suggestion. I have solve my problem by vtkTransformPolyDataFilter.

I don’t understand about this part. But, the vtkPicker should consider the following matrix since SetUserMatrix is allowed in vtkProp3D.

x = [1, 0, 0]
y = [0, 0, 1]
z = [0, 1, 0]

In addition, for such transform, I find the vtkCellPicker will still work for vtkLine. Because, in vtkLine::IntersectWithLine, the tol is used as:

vttkLine::DistanceToLine() <= tol*tol

Thus, it can pick line even tol<0.

However, vtkVertex can not be picked, because the tol is used as:

fabs(X[i]-projXYZ[i]) > tol  //(vtkVertex.cxx line 163, vtkVertex::IntersectWithLine)

Since tol<0, the point will never be picked.

If the tol is used as: Normal( X, projXYZ ) > tol*tol, the vertex may also be picked.