mikedh / trimesh

Python library for loading and using triangular meshes.
https://trimesh.org
MIT License
3k stars 580 forks source link

Creating triangles from 3D shapely polygon? #1769

Open larsgrobe opened 1 year ago

larsgrobe commented 1 year ago

Dear trimesh-community,

first of all, thank you for providing and supporting this amazing software package!

I am trying to create a trimesh representation of a 2.5D geometry defined by a 2D-path, a base height, and an z-extrusion. Initially I had hoped that there was some trimesh-function allowing to extrude an open 2D-path to a triangulated, vertical rectange, but could not find any such feature.

My workaround was to generate a 3D shapely polygon by transforming the path to the base height, then transforming its copy by the extrusion lenght and inverting its coordinates, and finally closing it. This results in a 3D shapely polygon like this:

POLYGON Z ((-6.198309651941504 5.723988925475256 15, -5.787440372932203 4.571607560399093 15, -5.787440372932203 4.571607560399093 16.9, -6.198309651941504 5.723988925475256 16.9, -6.198309651941504 5.723988925475256 15))

I was hoping that I could now convert it into a mesh with the help of trimesh.creation.triangulate_polygon(). Unfortunately, this does not work (I have shortened the path names):

File testPath2.py:149 in refinePolygons print(trimesh.creation.triangulate_polygon(p)) File python3.10/site-packages/trimesh/creation.py:454 in triangulate_polygon faces = _tri_earcut(vertices, rings).reshape( ValueError: The second dimension of vertices is not 2!

The function trimesh.path.exchange.misc.polygon_to_path() apparently accepts the polygon, it returns for the same input:

{'entities': [<trimesh.path.entities.Line object at 0x16a6a8400>], 'vertices': array([[ 0.15185252, 4.65746915, 14.5 ], [ 0.91988539, 4.88676764, 14.5 ], [ 0.91988539, 4.88676764, 16.5 ], [ 0.15185252, 4.65746915, 16.5 ], [ 0.15185252, 4.65746915, 14.5 ]])}

I tried to construct a path from this result, leading to

TypeError: iteration over a 0-d array

What is accepted is to pass only the vertices:

trimesh.path.Path2D(vertices=par['vertices'])

resulting in:

<trimesh.Path2D(vertices.shape=(4, 3), len(entities)=0)>

I read this like a path comprising unconnected vertices. At this point I am stuck. Before I try to create the triangles from my vertices "manually" - is there any functionality implemented in trimesh for what I want to achieve?

Best, Lars.

mikedh commented 1 year ago

Hey, have you looked at trimesh.creation.extrude_polygon or trimesh.primitive.Extrusion?:

In [1]: import trimesh

In [2]: from shapely.geometry import Point

In [3]: polygon = Point([0,0]).buffer(1.0)

In [4]: polygon
Out[4]: <POLYGON ((1 0, 0.995 -0.098, 0.981 -0.195, 0.957 -0.29, 0.924 -0.383, 0.882...>

In [5]: extrude = trimesh.primitives.Extrusion(polygon=polygon, height=10)

In [6]: extrude
Out[6]: <trimesh.primitives.Extrusion>

In [7]: extrude.volume
Out[7]: 31.365484905459397

In [8]: extrude.bounds
Out[8]: 
array([[-1., -1.,  0.],
       [ 1.,  1., 10.]])

In [9]: m = trimesh.creation.extrude_polygon(polygon, height=10)

In [10]: m
Out[10]: <trimesh.Trimesh(vertices.shape=(128, 3), faces.shape=(252, 3))>
larsgrobe commented 1 year ago

Hi Mike,

thank you for the example! I have done this to create mesh volumes. The difference is that now I want to create a thin surface, not a closed volume.

Best, Lars.

mikedh commented 1 year ago

Oh gotcha, seems like we could pretty easily add an cap=True option to trimesh.creation.extrude_triangulation to just optionally skip the caps? Would filtering by face normal work in the mean time?

In [1]: import trimesh

In [2]: from shapely.geometry import Point

In [3]: polygon = Point([0,0]).buffer(1.0)

In [4]: extrude = trimesh.creation.extrude_polygon(polygon, height=10)

In [5]: extrude
Out[5]: <trimesh.Trimesh(vertices.shape=(128, 3), faces.shape=(252, 3))>

In [7]: import numpy as np

In [8]: epsilon = 1e-8

In [10]: dot = np.dot(extrude.face_normals, [0,0,1])

In [11]: face_ok = np.logical_and(dot > (epsilon - 1), dot < (1 - epsilon))

In [12]: face_ok.all()
Out[12]: False

In [13]: extrude.update_faces(face_ok)

extrude

larsgrobe commented 1 year ago

Hi Mike,

filtering would be an option, good point - I am just not sure if I can extrude open paths (like a simple line). My first attempts were not very successful with it, but this may be due to me just starting with trimesh.

Best, Lars.