Reputation: 715
I'm looking for a way to offset a TIN (triangulated irregular network) mesh (e.g. of a landscape) by a consistent distance via a Python script. In the past I've called Blender's Solidify modifier via the API, which works well, but I'd like to find an alternative that doesn't require Blender as a dependency.
Currently, I'm iterating over each triangle, making a copy of each vertex and moving these along their parent triangle's surface normal by a set distance. Where the same vertex appears in multiple triangles I calculate the offset for each normal and use the mean. Then I connect the vertices together again to make an offset mesh.
This seems to work ok for small offsets, but sharp features in the mesh can create self-intersections.
Is there a specific algorithm or library which offers a better way?
So far, apart from Blender, I've found:
But I haven't found much for Python e.g. in Open3D or Trimesh.
Upvotes: 5
Views: 3713
Reputation: 66
One of the most robust methods to make uniform offset is rasterization of mesh, e.g. making voxels filled with signed distances to the surface, and then building iso-surface with desired offset. But this method have limitation: to have voxels filled with signed distances input mesh have to be closed. Anyway you can make unsigned voxels which lead to shell offset (for non-closed surfaces).
General advantage of this method is that it can work with self-intersecting mesh.
There is C++ implementation using openvdb in opensource MeshLib library that has python modules. So you can use following code:
from meshlib import mrmeshpy
# load closed mesh
closedMesh = mrmeshpy.loadMesh("closedMesh.stl")
# load non-closed mesh
nonClosedMesh = mrmeshpy.loadMesh("nonClosedMesh.stl")
# setup offset parameters
params = mrmeshpy.OffsetParameters()
params.voxelSize = 0.04
params.type = mrmeshpy.OffsetParametersType.Offset # requires closed mesh
# create positive offset mesh
posOffset = mrmeshpy.offsetMesh(closedMesh, 0.2, params)
# create negative offset mesh
negOffset = mrmeshpy.offsetMesh(closedMesh, -0.1, params)
# change offset mode to `Shell`
params.type = mrmeshpy.OffsetParametersType.Shell # does not require closed mesh
# create shell mesh
shell = mrmeshpy.offsetMesh(enonClosedMesh, 0.1, params)
# save results
mrmeshpy.saveMesh(posOffset, "posOffset.stl")
mrmeshpy.saveMesh(negOffset, "negOffset.stl")
mrmeshpy.saveMesh(shell, "shell.stl")
Upvotes: 1
Reputation: 159
I think the easy fix for your offsetting is to move the vertices along the mean normal, instead of moving it by the mean of along each normal. (ok the difference can be tricky to see in that sentence ;) )
What I mean is that when you get sharp geometries the mean of neighbooring normal is no more of length 1, so in the end you don't offset your vertex by the desired distance. I think you should compute the sum of neighbooring normals and then normalize those vectors to get the normal to each vertex. If you have more than 3 triangles connected to each vertex (which is the general case) you also need to make a weighted sum of neighbooring normals by the triangles' connection angle ...
You can also pymadcad There is a function that does exactly what I said:
from madcad import *
surface = Mesh([points, ...], [triangles, ...])
my_thick_mesh = thicken(surface, distance)
There is 3 function regarding this problem:
inflateoffsets gives the desired offset vectors
inflate offsets a mesh surface
thicken create an envelope with
the original surface
the offseted surface
the sides
Upvotes: 0