mikedh / trimesh

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

Draping 2D features on 3D mesh #2288

Open swiss-knight opened 2 months ago

swiss-knight commented 2 months ago

Hello,

this is not an issue sensu stricto but it seems that the github Discussion panel is not available in this repo.

I'm simply wondering if it's possible to compute the projection of a 2D feature onto a 3D mesh using trimesh.

The use case which comes to my mind is when one is working with 2D geographical data and want to get those 2D data (points, lines or polygons) "onto" a TIN (given e.g. as ply, stl or obj file...), representing a terrain.

This is only a subset of the question, because in that case we know the projection is done "vertically": (x,y) coordinates of the 2D features do not change during the operation and the normals of the terrain triangles all have a positive z component (i.e. there is no overhang)

Thank you for the nice work done on this package! Warm Regards.

Kiord commented 2 months ago

This is a common practice for texture mapping for instance. Can you elaborate on the goal of draping those 2D features ? Is it solely for visualization ?

swiss-knight commented 2 months ago

Hi @Kiord,

No it's not for visualization; I would like the output to be a real 3D geometry.

The main goal is to retrieve the 3rd dimension of some given 2D shapes to actually make those shapes 3 dimensional.

You can think about a set of line features representing a road network which is given in 2 dimensions (e.g. as Shapely LINESTRING objects having (x,y) coordinates), which you want to convert to real 3D lines having (x,y,z) coordinates using an existing terrain model given as a TIN mesh (e.g. as a PLY file), to retrieve the Z-values and make those 2D geometries real 3D geometries (e.g. also a Shapely LINESTRINGs).

An other way of representing the problem at hand would to apply a parallel projection (in the current case, a vertical one) of the 2D shapes "onto" the mesh. This operation is sometimes known as "draping" 2D shapes onto a terrain/mesh.

Kiord commented 2 months ago

Thanks for the details, to my knowledge there is no such thing in Trimesh.

You could obtain the Z coordinate of all the features (or at least their control points) by raycasting them along the Z axis. Something like that : (not tested)

# Make sure to set the ray origins on one side of the mesh
origin_z = mesh.vertices[:, 2].min() - 1 
features_2d_with_low_z  = np.zeros((len(features_2d), 3))
features_2d_with_low_z[:, 2] = origin_z

up_vectors = np.zeros_like(features_2d_with_low_z)
up_vectors[:, 2] = 1

# ray/mesh intersections computation
intersections, ray_indices, face_indices = scan_mesh.ray.intersects_location(features_2d_with_low_z, up_vectors, multiple_hits)

intersections_z = -np.ones(len(features_2d))
intersections_z[ray_indices] = intersections[:, 2]

But if the triangle mesh resolution is higher than the spacing of the 2D features, the now-3D shapes won't exactly lie on 3D surface. An exact solution would require to compute the 2D intersections of the 2D shapes on the XY triangles of the surface mesh. With straight lines only, it should not be so hard.

PS : The 3D ray casting is a bit overkill considering it only solves a 2D point query on 2D triangles...