vtk extruded polygon stl flipped normal

I am using vtk python to extrude a polygon, and then write out the extrusion to an stl file. The stl file gets written out with all the face normals pointing ‘outwards’, except for the base, for which the normal seems to point ‘inwards’. I suspect this is due to the normal defined by the anti-clockwise ordering of the points describing the base polygon. Can anyone tell me what I should be doing differently?

Here is my code that extrudes a unit square defined in the ‘z=0’ plane, by 1 unit, to create a cube:

# Extrude a square to create a cube using vtk python, and save it as an stl

import vtk

def main():

    # Define the extrusion base and height
    extrusion =	{
        "base": [(0,0,0), (1,0,0), (1,1,0), (0,1,0)],
        "height": 1
    }

    # Points describing the polygon to be extruded
    points = vtk.vtkPoints()
    for p in extrusion['base']:
        points.InsertNextPoint(p[0], p[1], p[2])
    
    # Create the vtk polygon
    polygon = vtk.vtkPolygon()
    polygon.GetPointIds().SetNumberOfIds(len(extrusion['base']))

    for i in range(0, len(extrusion['base'])):
        polygon.GetPointIds().SetId(i, i)

    polygons = vtk.vtkCellArray()
    polygons.InsertNextCell(polygon)

    # Create PolyData for the polygon
    polyData = vtk.vtkPolyData()
    polyData.SetPoints(points)
    polyData.SetPolys(polygons)

    # Apply the extrusion filter to the polygon
    extrude = vtk.vtkLinearExtrusionFilter()
    extrude.SetInputData(polyData)
    extrude.SetExtrusionTypeToNormalExtrusion()
    extrude.SetVector(0, 0, extrusion['height'])
    extrude.Update()
    
    # Write the extrusion to stl
    stlWriter = vtk.vtkSTLWriter()
    stlWriter.SetFileName('./cube.stl')
    stlWriter.SetInputConnection(extrude.GetOutputPort())
    stlWriter.Write()

if __name__ == '__main__':
    main()

The stl file produced is as follows. The normals of all the facets point ‘outwards’. However, the normals of the 2 facets that describe the base of the cube (third and fourth from the bottom) point in the (0,0,1) direction, i.e, ‘inwards’.

solid ascii
 facet normal 0 -1 0
  outer loop
   vertex 0 0 0
   vertex 1 0 0
   vertex 0 0 1
  endloop
 endfacet
 facet normal 0 -1 0
  outer loop
   vertex 0 0 1
   vertex 1 0 0
   vertex 1 0 1
  endloop
 endfacet
 facet normal 1 0 0
  outer loop
   vertex 1 0 0
   vertex 1 1 0
   vertex 1 0 1
  endloop
 endfacet
 facet normal 1 0 -0
  outer loop
   vertex 1 0 1
   vertex 1 1 0
   vertex 1 1 1
  endloop
 endfacet
 facet normal 0 1 0
  outer loop
   vertex 1 1 0
   vertex 0 1 0
   vertex 1 1 1
  endloop
 endfacet
 facet normal 0 1 0
  outer loop
   vertex 1 1 1
   vertex 0 1 0
   vertex 0 1 1
  endloop
 endfacet
 facet normal -1 0 0
  outer loop
   vertex 0 1 0
   vertex 0 0 0
   vertex 0 1 1
  endloop
 endfacet
 facet normal -1 0 0
  outer loop
   vertex 0 1 1
   vertex 0 0 0
   vertex 0 0 1
  endloop
 endfacet
 facet normal 0 0 1
  outer loop
   vertex 0 0 0
   vertex 1 0 0
   vertex 0 1 0
  endloop
 endfacet
 facet normal -0 0 1
  outer loop
   vertex 1 1 0
   vertex 0 1 0
   vertex 1 0 0
  endloop
 endfacet
 facet normal 0 0 1
  outer loop
   vertex 0 0 1
   vertex 1 0 1
   vertex 0 1 1
  endloop
 endfacet
 facet normal -0 0 1
  outer loop
   vertex 1 1 1
   vertex 0 1 1
   vertex 1 0 1
  endloop
 endfacet
endsolid

I would be grateful for any assistance in pointing out how I can correct the error.

Thank you.

Maybe try using the vtkCubeSource which will precompute the proper normals for you?

import vtk

src = vtk.vtkCubeSource()
src.SetCenter((0.5, 0.5, 0.5))
src.SetXLength(1.0)
src.SetYLength(1.0)
src.SetZLength(1.0)
src.Update()

# Write the extrusion to stl
stlWriter = vtk.vtkSTLWriter()
stlWriter.SetFileName('./cube.stl')
stlWriter.SetInputConnection(src.GetOutputPort())
stlWriter.Write()

Another workaround is to append vtkPolyDataNormals filter. This can enforce consistent polygon ordering as an option.

    # Apply the extrusion filter to the polygon
    extrude = vtk.vtkLinearExtrusionFilter()
    extrude.SetInputData(polyData)
    extrude.SetExtrusionTypeToNormalExtrusion()
    extrude.SetVector(0, 0, extrusion['height'])

    # Generate normals
    normals = vtk.vtkPolyDataNormals()
    normals.SetInputConnection(extrude.GetOutputPort())
    normals.ComputeCellNormalsOn()
    normals.FlipNormalsOn()
    normals.ConsistencyOn()
    
    # Write the extrusion to stl
    stlWriter = vtk.vtkSTLWriter()
    stlWriter.SetFileName('./cube.stl')
    stlWriter.SetInputConnection(normals.GetOutputPort())
    stlWriter.Write()

Thanks, guys. I had eventually solved the problem following an approach similar to @Kenichiro-Yoshimi’s since I want to be able to extrude an arbitrary polygon, not always a square.

Interestingly, however, I found that simply appending a vtkPolyDataNormals filter to the vtkLinearExtrusionFilter output did not seem to have any effect (for example, normals.FlipNormalsOn() didn’t flip the normals in the output .stl file), even after running normals.Update(). I wonder if this is a small bug in vtk (I’m using python vtk version 8.1.2).

I worked around the problem by appending a vtkTriangleFilter to the extrusion, and then appending the vtkPolyDataNormals filter to that, which then seemed to have the desired effect.

extrude = vtk.vtkLinearExtrusionFilter()
extrude.SetInputData(polyData)
extrude.SetExtrusionTypeToNormalExtrusion()
extrude.SetVector(0, 0, extrusion['height'])
extrude.Update()

triangle_filter = vtk.vtkTriangleFilter()
triangle_filter.SetInputData(extrude.GetOutput())
triangle_filter.Update()

normals = vtk.vtkPolyDataNormals()
normals.SetInputConnection(triangle_filter.GetOutputPort())
normals.FlipNormalsOn()
normals.Update()

stlWriter = vtk.vtkSTLWriter()
stlWriter.SetFileName('./cube.stl')
stlWriter.SetInputConnection(normals.GetOutputPort())
stlWriter.Write()
2 Likes