Python library for loading and using triangular meshes.

Overview

trimesh


Github Actions PyPI version codecov

Trimesh is a pure Python (2.7-3.4+) library for loading and using triangular meshes with an emphasis on watertight surfaces. The goal of the library is to provide a full featured and well tested Trimesh object which allows for easy manipulation and analysis, in the style of the Polygon object in the Shapely library.

The API is mostly stable, but this should not be relied on and is not guaranteed: install a specific version if you plan on deploying something using trimesh.

Pull requests are appreciated and responded to promptly! If you'd like to contribute, here is an up to date list of potential enhancements although things not on that list are also welcome. Here are some tips for writing mesh code in Python.

Basic Installation

Keeping trimesh easy to install is a core goal, thus the only hard dependency is numpy. Installing other packages adds functionality but is not required. For the easiest install with just numpy, pip can generally install trimesh cleanly on Windows, Linux, and OSX:

pip install trimesh

The minimal install can load many supported formats (STL, PLY, GLTF/GLB) into numpy arrays. More functionality is available when soft dependencies are installed. This includes things like convex hulls (scipy), graph operations (networkx), faster ray queries (pyembree), vector path handling (shapely and rtree), XML formats like 3DXML/XAML/3MF (lxml), preview windows (pyglet), faster cache checks (xxhash), etc. To install trimesh with the soft dependencies that generally install cleanly on Linux, OSX, and Windows using pip:

pip install trimesh[easy]

Further information is available in the advanced installation documentation.

Quick Start

Here is an example of loading a mesh from file and colorizing its faces. Here is a nicely formatted ipython notebook version of this example. Also check out the cross section example or possibly the integration of a function over a mesh example.

import numpy as np
import trimesh

# attach to logger so trimesh messages will be printed to console
trimesh.util.attach_to_log()

# mesh objects can be created from existing faces and vertex data
mesh = trimesh.Trimesh(vertices=[[0, 0, 0], [0, 0, 1], [0, 1, 0]],
                       faces=[[0, 1, 2]])

# by default, Trimesh will do a light processing, which will
# remove any NaN values and merge vertices that share position
# if you want to not do this on load, you can pass `process=False`
mesh = trimesh.Trimesh(vertices=[[0, 0, 0], [0, 0, 1], [0, 1, 0]],
                       faces=[[0, 1, 2]],
                       process=False)

# some formats represent multiple meshes with multiple instances
# the loader tries to return the datatype which makes the most sense
# which will for scene-like files will return a `trimesh.Scene` object.
# if you *always* want a straight `trimesh.Trimesh` you can ask the
# loader to "force" the result into a mesh through concatenation
mesh = trimesh.load('models/CesiumMilkTruck.glb', force='mesh')

# mesh objects can be loaded from a file name or from a buffer
# you can pass any of the kwargs for the `Trimesh` constructor
# to `trimesh.load`, including `process=False` if you would like
# to preserve the original loaded data without merging vertices
# STL files will be a soup of disconnected triangles without
# merging vertices however and will not register as watertight
mesh = trimesh.load('../models/featuretype.STL')

# is the current mesh watertight?
mesh.is_watertight

# what's the euler number for the mesh?
mesh.euler_number

# the convex hull is another Trimesh object that is available as a property
# lets compare the volume of our mesh with the volume of its convex hull
print(mesh.volume / mesh.convex_hull.volume)

# since the mesh is watertight, it means there is a
# volumetric center of mass which we can set as the origin for our mesh
mesh.vertices -= mesh.center_mass

# what's the moment of inertia for the mesh?
mesh.moment_inertia

# if there are multiple bodies in the mesh we can split the mesh by
# connected components of face adjacency
# since this example mesh is a single watertight body we get a list of one mesh
mesh.split()

# facets are groups of coplanar adjacent faces
# set each facet to a random color
# colors are 8 bit RGBA by default (n, 4) np.uint8
for facet in mesh.facets:
    mesh.visual.face_colors[facet] = trimesh.visual.random_color()

# preview mesh in an opengl window if you installed pyglet and scipy with pip
mesh.show()

# transform method can be passed a (4, 4) matrix and will cleanly apply the transform
mesh.apply_transform(trimesh.transformations.random_rotation_matrix())

# axis aligned bounding box is available
mesh.bounding_box.extents

# a minimum volume oriented bounding box also available
# primitives are subclasses of Trimesh objects which automatically generate
# faces and vertices from data stored in the 'primitive' attribute
mesh.bounding_box_oriented.primitive.extents
mesh.bounding_box_oriented.primitive.transform

# show the mesh appended with its oriented bounding box
# the bounding box is a trimesh.primitives.Box object, which subclasses
# Trimesh and lazily evaluates to fill in vertices and faces when requested
# (press w in viewer to see triangles)
(mesh + mesh.bounding_box_oriented).show()

# bounding spheres and bounding cylinders of meshes are also
# available, and will be the minimum volume version of each
# except in certain degenerate cases, where they will be no worse
# than a least squares fit version of the primitive.
print(mesh.bounding_box_oriented.volume,
      mesh.bounding_cylinder.volume,
      mesh.bounding_sphere.volume)

Features

  • Import meshes from binary/ASCII STL, Wavefront OBJ, ASCII OFF, binary/ASCII PLY, GLTF/GLB 2.0, 3MF, XAML, 3DXML, etc.
  • Import and export 2D or 3D vector paths from/to DXF or SVG files
  • Import geometry files using the GMSH SDK if installed (BREP, STEP, IGES, INP, BDF, etc)
  • Export meshes as binary STL, binary PLY, ASCII OFF, OBJ, GLTF/GLB 2.0, COLLADA, etc.
  • Export meshes using the GMSH SDK if installed (Abaqus INP, Nastran BDF, etc)
  • Preview meshes using pyglet or in- line in jupyter notebooks using three.js
  • Automatic hashing of numpy arrays for change tracking using MD5, zlib CRC, or xxhash
  • Internal caching of computed values validated from hashes
  • Calculate face adjacencies, face angles, vertex defects, etc.
  • Calculate cross sections, i.e. the slicing operation used in 3D printing
  • Slice meshes with one or multiple arbitrary planes and return the resulting surface
  • Split mesh based on face connectivity using networkx, graph-tool, or scipy.sparse
  • Calculate mass properties, including volume, center of mass, moment of inertia, principal components of inertia vectors and components
  • Repair simple problems with triangle winding, normals, and quad/tri holes
  • Convex hulls of meshes
  • Compute rotation/translation/tessellation invariant identifier and find duplicate meshes
  • Determine if a mesh is watertight, convex, etc.
  • Uniformly sample the surface of a mesh
  • Ray-mesh queries including location, triangle index, etc.
  • Boolean operations on meshes (intersection, union, difference) using OpenSCAD or Blender as a back end. Note that mesh booleans in general are usually slow and unreliable
  • Voxelize watertight meshes
  • Volume mesh generation (TETgen) using Gmsh SDK
  • Smooth watertight meshes using laplacian smoothing algorithms (Classic, Taubin, Humphrey)
  • Subdivide faces of a mesh
  • Minimum volume oriented bounding boxes for meshes
  • Minimum volume bounding spheres
  • Symbolic integration of functions over triangles
  • Calculate nearest point on mesh surface and signed distance
  • Determine if a point lies inside or outside of a well constructed mesh using signed distance
  • Primitive objects (Box, Cylinder, Sphere, Extrusion) which are subclassed Trimesh objects and have all the same features (inertia, viewers, etc)
  • Simple scene graph and transform tree which can be rendered (pyglet window, three.js in a jupyter notebook, pyrender) or exported.
  • Many utility functions, like transforming points, unitizing vectors, aligning vectors, tracking numpy arrays for changes, grouping rows, etc.

Viewer

Trimesh includes an optional pyglet based viewer for debugging and inspecting. In the mesh view window, opened with mesh.show(), the following commands can be used:

  • mouse click + drag rotates the view
  • ctl + mouse click + drag pans the view
  • mouse wheel zooms
  • z returns to the base view
  • w toggles wireframe mode
  • c toggles backface culling
  • g toggles an XY grid with Z set to lowest point
  • a toggles an XYZ-RGB axis marker between: off, at world frame, or at every frame and world, and at every frame
  • f toggles between fullscreen and windowed mode
  • m maximizes the window
  • q closes the window

If called from inside a jupyter notebook, mesh.show() displays an in-line preview using three.js to display the mesh or scene. For more complete rendering (PBR, better lighting, shaders, better off-screen support, etc) pyrender is designed to interoperate with trimesh objects.

Projects Using Trimesh

You can check out the Github network for things using trimesh. A select few:

  • Nvidia's kaolin for deep learning on 3D geometry.
  • Cura, a popular slicer for 3D printing.
  • Berkeley's DexNet4 and related ambidextrous.ai work with robotic grasp planning and manipulation.
  • Kerfed's Kerfed's Engine for analyzing assembly geometry for manufacturing.
  • MyMiniFactory's P2Slice for preparing models for 3D printing.
  • pyrender A library to render scenes from Python using nice looking PBR materials.
  • urdfpy Load URDF robot descriptions in Python.
  • moderngl-window A helper to create GL contexts and load meshes.
  • vedo Visualize meshes interactively (see example gallery).
  • fsleyes View MRI images and brain data.

Which Mesh Format Should I Use?

Quick recommendation: GLB or PLY. Every time you replace OBJ with GLB an angel gets its wings.

If you want things like by-index faces, instancing, colors, textures, etc, GLB is a terrific choice. GLTF/GLB is an extremely well specified modern format that is easy and fast to parse: it has a JSON header describing data in a binary blob. It has a simple hierarchical scene graph, a great looking modern physically based material system, support in dozens-to-hundreds of libraries, and a John Carmack endorsment. Note that GLTF is a large specification, and trimesh only supports a subset of features: loading basic geometry is supported, NOT supported are fancier things like animations, skeletons, etc.

In the wild, STL is perhaps the most common format. STL files are extremely simple: it is basically just a list of triangles. They are robust and are a good choice for basic geometry. Binary PLY files are a good step up, as they support indexed faces and colors.

Wavefront OBJ is also pretty common: unfortunately OBJ doesn't have a widely accepted specification so every importer and exporter implements things slightly differently, making it tough to support. It also allows unfortunate things like arbitrary sized polygons, has a face representation which is easy to mess up, references other files for materials and textures, arbitrarily interleaves data, and is slow to parse. Give GLB or PLY a try as an alternative!

How can I cite this library?

A question that comes up pretty frequently is how to cite the library. A quick BibTex recommendation:

@software{trimesh,
	author = {{Dawson-Haggerty et al.}},
	title = {trimesh},
	url = {https://trimsh.org/},
	version = {3.2.0},
	date = {2019-12-8},
}

Containers

If you want to deploy something in a container that uses trimesh, automated debian:buster-slim based builds with trimesh and dependencies are available on Docker Hub with image tags for latest, git short hash for the commit in master (i.e. trimesh/trimesh:0c1298d), and version (i.e. trimesh/trimesh:3.5.27):

docker pull trimesh/trimesh

Here's an example of how to render meshes using LLVMpipe and XVFB inside a container.

Comments
  • Enhancement Ideas

    Enhancement Ideas

    I was pinged recently looking for some projects to help out with, here are some ideas. PR's are always appreciated!

    Updated 7/27/2020

    • (docs) Figure out how to get the examples embedded in the actual docs webpage. Right now when you build docs, example ipynb files are rendered to HTML which is just linked to from the examples page. It would be really nice if each example was embedded in sphinx somehow, and kept the sidebar on the left so one could navigate around between examples and back to the docs.
    • (feature) Mesh simplification, either through creating a meshlabserver interface or through implementing a vectorized numpy version of quadratic edge collapse: https://github.com/mikedh/trimesh/issues/41
    • (feature) MSH support, as requested in https://github.com/mikedh/trimesh/issues/206. Probably via meshio
    • (feature) Color and line width in Path objects: https://github.com/mikedh/trimesh/issues/356
    • (feature) More 3MF tests. Trimesh has a 3MF importer, it would be great to put together a test corpus of various small- ish models (specifically interesting would be multibody assemblies with duplication) from various exporters.
    • (feature) Do whatever it takes to get pyinstaller to work with trimesh, as per #412
    • (infrastructure) Put all of Shapenet, Thingiverse 10k, GLTF samples, and all available meshes into an AWS S3 bucket, and continuously benchmark trimesh, pymesh, and assimp for percentage of files loaded and time. #510
    • (community) See if upstream numpy has interest in including TrackedArray or ndarray.crc #283
    • (speedup) Add broad-phase culling for mesh.slice_plane to speed up on large meshes: #630
    • (feature) Exact rather than approximate medial axis calculation as per #924
    opened by mikedh 27
  • scene.save_image() doesn't work

    scene.save_image() doesn't work

    Hi! I just tried the example in https://github.com/mikedh/trimesh/blob/master/examples/offscreen_render.py. However, I cannot get the correct saved image. The saved image is just plain black. BTW, the mesh viewer can display the mesh correctly, but the rotation doesn't work (the camera view is never rotated).

    Could you help resolve the save_image() issue? Thanks!

    opened by weikaichenusc 24
  • rendering using camera matrices

    rendering using camera matrices

    Hello Mike, Thank you for the wonderful library! Really appreciate the effort. This is more of an inquiry of how to render the scene only using camera_pose_wrt_world, camera intrinsics, and mesh vertices. I have tried using the dot product of 3D vertices with camera matrix but I'm getting very different points than I get by rendering an image using scene.save_image() in Trimesh. As I need it to be in real time, I am only allowed to use numpy to tackle this problem.

    Thank you!

    opened by DHarshil32 23
  • Possible bug in Pillow rasterization

    Possible bug in Pillow rasterization

    Rasterization using PIL doesn't create a clean image when sectioning a mesh

    geometry = trimesh.load_mesh(path)
    slice = geometry.section(place_origin=[0, 0, 0.15], plane_normal = [0,0,1])
    planar, _ = slice.to_planar(normal=[0, 0, 1])
    im_origin = planar.bounds[0]
    im = planar.rasterize(resolution, im_origin)
    

    the file used is here : https://cdn.thingiverse.com/assets/7d/fc/6e/33/fe/3DBenchy.stl

    I believe the issue is inside Pillow. I copied the rastization implementation and I just used scikit-image to draw ,instead of Pillow, and it fixed the issue.

    
    from skimage import draw
    
    
    def rasterize(
        path: trimesh.path.Path2D, pitch: npt.NDArray, origin: npt.NDArray, resolution=None
    ) -> npt.NDArray:
    
        pitch = np.asanyarray(pitch, dtype=np.float64)
        origin = np.asanyarray(origin, dtype=np.float64)
        # if resolution is None make it larger than path
        if resolution is None:
            span = np.vstack((path.bounds, origin)).ptp(axis=0)
            resolution = np.ceil(span / pitch) + 2
        
        im = np.zeros(resolution.astype(int)).astype(bool)
        # get resolution as a (2,) int tuple
        discrete = [((i - origin) / pitch).round().astype(np.int64) for i in path.discrete]
        roots = path.root
        enclosure = path.enclosure_directed
    
        for root in roots:
            outer = draw.polygon(discrete[root][:,0],discrete[root][:,1], shape = im.shape)
            im[outer] = 1
            for child in enclosure[root]:
                hole = draw.polygon(discrete[child][:,0],discrete[child][:,1], shape = im.shape)
                im[hole] = 0 
        
        return im
    

    I can make a pull request if interested

    opened by rockandsalt 17
  • Binvox voxel IO/representations

    Binvox voxel IO/representations

    Hi Mike, Apologies for making this rather large. I understand you might not be looking to extend functionality in this direction, but I was writing this stuff for my own work anyway and so figured I'd offer it up. As an overview:

    • The original motivation was just to add binvox support.
    • binvox uses run-length-encodings, and many operations can be performed on the RLE rather than the dense version - see trimesh.rle and the trimesh.voxel.VoxelBase implementation VoxelRle.
    • for some reason the binvox specification is for axis ordering 'xzy'. I've implemented this in a separate VoxelTranspose class, which represents a lazily transposed VoxelBase.
    • The Binary RLE (brle-prefixed) stuff isn't commonly used AFAIK, but it's an obvious direction to take things if you ask me and I've found it useful in my own work.

    I'm very happy to restructure it/remove parts/add additional tests to your liking - I just thought I'd seek some high-level feedback before I go over everything with a fine tooth-comb.

    Thanks again for the package!

    opened by jackd 17
  • Big Endian failures

    Big Endian failures

    Another issue that came up in Fedora. We build packages on the first available builder and sometimes that's Big Endian (ppc64 on Fedora 28 or s390x anywhere). Apparently, the tests fail a lot on those.

    Mos likely load_stl_binary is broken and load_stl_ascii is called for binary STLs on Big Endian.

    + /usr/bin/python3 -m pytest -v -k 'not test_export and not test_remote'
    ============================= test session starts ==============================
    platform linux -- Python 3.6.6, pytest-3.4.2, py-1.5.4, pluggy-0.6.0 -- /usr/bin/python3
    cachedir: .pytest_cache
    rootdir: /builddir/build/BUILD/trimesh-2.35.24, inifile:
    collecting ... collected 193 items
    tests/test_boolean.py::BooleanTest::test_boolean PASSED                  [  0%]
    tests/test_boolean.py::BooleanTest::test_multiple PASSED                 [  1%]
    tests/test_bounds.py::BoundsTest::test_2D FAILED                         [  1%]
    tests/test_bounds.py::BoundsTest::test_cylinder FAILED                   [  2%]
    tests/test_bounds.py::BoundsTest::test_obb_mesh FAILED                   [  2%]
    tests/test_bounds.py::BoundsTest::test_obb_order FAILED                  [  3%]
    tests/test_bounds.py::BoundsTest::test_obb_points FAILED                 [  3%]
    tests/test_cache.py::CacheTest::test_contiguous PASSED                   [  4%]
    tests/test_cache.py::CacheTest::test_hash FAILED                         [  4%]
    tests/test_cache.py::CacheTest::test_track PASSED                        [  5%]
    tests/test_collision.py::CollisionTest::test_collision PASSED            [  6%]
    tests/test_collision.py::CollisionTest::test_distance PASSED             [  6%]
    tests/test_collision.py::CollisionTest::test_scene PASSED                [  7%]
    tests/test_convex.py::ConvexTest::test_convex FAILED                     [  7%]
    tests/test_convex.py::ConvexTest::test_primitives PASSED                 [  8%]
    tests/test_convex.py::ConvexTest::test_projections FAILED                [  8%]
    tests/test_creation.py::CreationTest::test_annulus PASSED                [  9%]
    tests/test_creation.py::CreationTest::test_path_sweep PASSED             [  9%]
    tests/test_creation.py::CreationTest::test_soup PASSED                   [ 10%]
    tests/test_creation.py::CreationTest::test_triangulate PASSED            [ 10%]
    tests/test_creation.py::CreationTest::test_triangulate_plumbing PASSED   [ 11%]
    tests/test_creation.py::CreationTest::test_uv PASSED                     [ 12%]
    tests/test_curvature.py::CurvatureTest::test_gaussian_curvature FAILED   [ 12%]
    tests/test_curvature.py::CurvatureTest::test_mean_curvature PASSED       [ 13%]
    tests/test_curvature.py::CurvatureTest::test_vertex_defect PASSED        [ 13%]
    tests/test_decomposition.py::DecompositionTest::test_convex_decomposition PASSED [ 14%]
    tests/test_dxf.py::DXFTest::test_bulge PASSED                            [ 14%]
    tests/test_dxf.py::DXFTest::test_dxf PASSED                              [ 15%]
    tests/test_dxf.py::DXFTest::test_spline PASSED                           [ 15%]
    tests/test_dxf.py::DXFTest::test_versions PASSED                         [ 16%]
    tests/test_facets.py::FacetTest::test_facet FAILED                       [ 16%]
    tests/test_gltf.py::GLTFTest::test_duck FAILED                           [ 17%]
    tests/test_gltf.py::GLTFTest::test_units FAILED                          [ 18%]
    tests/test_graph.py::GraphTest::test_components FAILED                   [ 18%]
    tests/test_graph.py::GraphTest::test_engine_time FAILED                  [ 19%]
    tests/test_graph.py::GraphTest::test_engines PASSED                      [ 19%]
    tests/test_graph.py::GraphTest::test_smoothed PASSED                     [ 20%]
    tests/test_graph.py::GraphTest::test_soup FAILED                         [ 20%]
    tests/test_graph.py::GraphTest::test_traversals PASSED                   [ 21%]
    tests/test_graph.py::GraphTest::test_vertex_adjacency_graph FAILED       [ 21%]
    tests/test_graph.py::GraphTest::test_watertight PASSED                   [ 22%]
    tests/test_grouping.py::GroupTests::test_blocks PASSED                   [ 22%]
    tests/test_grouping.py::GroupTests::test_boolean_rows PASSED             [ 23%]
    tests/test_grouping.py::GroupTests::test_cluster PASSED                  [ 24%]
    tests/test_grouping.py::GroupTests::test_group_rows PASSED               [ 24%]
    tests/test_grouping.py::GroupTests::test_group_vector PASSED             [ 25%]
    tests/test_grouping.py::GroupTests::test_runs PASSED                     [ 25%]
    tests/test_grouping.py::GroupTests::test_unique_float PASSED             [ 26%]
    tests/test_grouping.py::GroupTests::test_unique_rows PASSED              [ 26%]
    tests/test_html.py::ViewTest::test_JSHTML FAILED                         [ 27%]
    tests/test_html.py::ViewTest::test_inNB PASSED                           [ 27%]
    tests/test_identifier.py::IdentifierTest::test_identifier FAILED         [ 28%]
    tests/test_identifier.py::IdentifierTest::test_scene_id PASSED           [ 28%]
    tests/test_inertia.py::InertiaTest::test_inertia FAILED                  [ 29%]
    tests/test_inertia.py::InertiaTest::test_primitives PASSED               [ 30%]
    tests/test_integrate.py::IntegrateTest::test_integrate FAILED            [ 30%]
    tests/test_loaded.py::LoaderTest::test_3MF PASSED                        [ 31%]
    tests/test_loaded.py::LoaderTest::test_obj_compressed PASSED             [ 31%]
    tests/test_loaded.py::LoaderTest::test_obj_groups PASSED                 [ 32%]
    tests/test_loaded.py::LoaderTest::test_obj_multiobj PASSED               [ 32%]
    tests/test_loaded.py::LoaderTest::test_obj_quad PASSED                   [ 33%]
    tests/test_loaded.py::LoaderTest::test_obj_simple_order PASSED           [ 33%]
    tests/test_loaded.py::LoaderTest::test_obj_split_attributes PASSED       [ 34%]
    tests/test_loaded.py::LoaderTest::test_stl PASSED                        [ 34%]
    tests/test_mesh.py::MeshTests::test_meshes FAILED                        [ 35%]
    tests/test_mesh.py::MeshTests::test_vertex_neighbors PASSED              [ 36%]
    tests/test_normals.py::NormalsTest::test_face_normals PASSED             [ 36%]
    tests/test_normals.py::NormalsTest::test_vertex_normal PASSED            [ 37%]
    tests/test_nsphere.py::NSphereTest::test_isnsphere PASSED                [ 37%]
    tests/test_nsphere.py::NSphereTest::test_minball FAILED                  [ 38%]
    tests/test_packing.py::PackingTest::test_obb PASSED                      [ 38%]
    tests/test_packing.py::PackingTest::test_paths PASSED                    [ 39%]
    tests/test_paths.py::VectorTests::test_discrete PASSED                   [ 39%]
    tests/test_paths.py::VectorTests::test_edges FAILED                      [ 40%]
    tests/test_paths.py::VectorTests::test_poly PASSED                       [ 40%]
    tests/test_paths.py::VectorTests::test_random_polygon PASSED             [ 41%]
    tests/test_paths.py::VectorTests::test_sample PASSED                     [ 42%]
    tests/test_paths.py::ArcTests::test_center PASSED                        [ 42%]
    tests/test_paths.py::ArcTests::test_center_random PASSED                 [ 43%]
    tests/test_paths.py::ArcTests::test_multiroot PASSED                     [ 43%]
    tests/test_paths.py::SplitTest::test_split PASSED                        [ 44%]
    tests/test_paths.py::ExportTest::test_svg PASSED                         [ 44%]
    tests/test_paths.py::SectionTest::test_section PASSED                    [ 45%]
    tests/test_paths.py::CreationTests::test_circle PASSED                   [ 45%]
    tests/test_paths.py::CreationTests::test_rect PASSED                     [ 46%]
    tests/test_permutate.py::PermutateTest::test_base PASSED                 [ 46%]
    tests/test_permutate.py::PermutateTest::test_permutate FAILED            [ 47%]
    tests/test_permutate.py::PermutateTest::test_tesselation FAILED          [ 48%]
    tests/test_points.py::PointsTest::test_kmeans PASSED                     [ 48%]
    tests/test_points.py::PointsTest::test_plane PASSED                      [ 49%]
    tests/test_points.py::PointsTest::test_pointcloud PASSED                 [ 49%]
    tests/test_points.py::PointsTest::test_tsp PASSED                        [ 50%]
    tests/test_points.py::PointsTest::test_vertex_only PASSED                [ 50%]
    tests/test_poses.py::PosesTest::test_multiple FAILED                     [ 51%]
    tests/test_poses.py::PosesTest::test_nonsampling_poses PASSED            [ 51%]
    tests/test_poses.py::PosesTest::test_round PASSED                        [ 52%]
    tests/test_primitives.py::PrimitiveTest::test_box PASSED                 [ 53%]
    tests/test_primitives.py::PrimitiveTest::test_extrusion PASSED           [ 53%]
    tests/test_primitives.py::PrimitiveTest::test_primitives PASSED          [ 54%]
    tests/test_primitives.py::PrimitiveTest::test_sample PASSED              [ 54%]
    tests/test_proximity.py::NearestTest::test_acute_edge_case PASSED        [ 55%]
    tests/test_proximity.py::NearestTest::test_coplanar_signed_distance FAILED [ 55%]
    tests/test_proximity.py::NearestTest::test_edge_case FAILED              [ 56%]
    tests/test_proximity.py::NearestTest::test_helper FAILED                 [ 56%]
    tests/test_proximity.py::NearestTest::test_naive PASSED                  [ 57%]
    tests/test_proximity.py::NearestTest::test_nearest_naive PASSED          [ 57%]
    tests/test_raster.py::RasterTest::test_rasterize PASSED                  [ 58%]
    tests/test_ray.py::RayTests::test_box FAILED                             [ 59%]
    tests/test_ray.py::RayTests::test_contain_single FAILED                  [ 59%]
    tests/test_ray.py::RayTests::test_contains FAILED                        [ 60%]
    tests/test_ray.py::RayTests::test_multiple_hits PASSED                   [ 60%]
    tests/test_ray.py::RayTests::test_on_edge FAILED                         [ 61%]
    tests/test_ray.py::RayTests::test_on_vertex PASSED                       [ 61%]
    tests/test_ray.py::RayTests::test_rays FAILED                            [ 62%]
    tests/test_ray.py::RayTests::test_rps FAILED                             [ 62%]
    tests/test_registration.py::RegistrationTest::test_icp_mesh PASSED       [ 63%]
    tests/test_registration.py::RegistrationTest::test_icp_points PASSED     [ 63%]
    tests/test_registration.py::RegistrationTest::test_mesh PASSED           [ 64%]
    tests/test_registration.py::RegistrationTest::test_procrustes PASSED     [ 65%]
    tests/test_remesh.py::SubDivideTest::test_subdivide FAILED               [ 65%]
    tests/test_render.py::RenderTest::test_args FAILED                       [ 66%]
    tests/test_repair.py::RepairTests::test_fill_holes FAILED                [ 66%]
    tests/test_repair.py::RepairTests::test_fix_normals FAILED               [ 67%]
    tests/test_repair.py::RepairTests::test_multi FAILED                     [ 67%]
    tests/test_repair.py::RepairTests::test_winding FAILED                   [ 68%]
    tests/test_sample.py::SampleTest::test_sample FAILED                     [ 68%]
    tests/test_scene.py::SceneTests::test_3DXML PASSED                       [ 69%]
    tests/test_scene.py::SceneTests::test_doubling PASSED                    [ 69%]
    tests/test_scene.py::SceneTests::test_dupe PASSED                        [ 70%]
    tests/test_scene.py::SceneTests::test_empty PASSED                       [ 71%]
    tests/test_scene.py::SceneTests::test_scaling PASSED                     [ 71%]
    tests/test_scene.py::SceneTests::test_scene PASSED                       [ 72%]
    tests/test_scene.py::SceneTests::test_zipped PASSED                      [ 72%]
    tests/test_scene.py::GraphTests::test_forest PASSED                      [ 73%]
    tests/test_section.py::SectionTest::test_section FAILED                  [ 73%]
    tests/test_section.py::PlaneLine::test_planes PASSED                     [ 74%]
    tests/test_section.py::SliceTest::test_slice PASSED                      [ 74%]
    tests/test_segments.py::SegmentsTest::test_param PASSED                  [ 75%]
    tests/test_simplify.py::SimplifyTest::test_simplify PASSED               [ 75%]
    tests/test_simplify.py::SimplifyTest::test_spline PASSED                 [ 76%]
    tests/test_splines.py::SplineTests::test_bezier_example PASSED           [ 77%]
    tests/test_thickness.py::ThicknessTest::test_known PASSED                [ 77%]
    tests/test_thickness.py::ThicknessTest::test_ray_reach PASSED            [ 78%]
    tests/test_thickness.py::ThicknessTest::test_ray_thickness PASSED        [ 78%]
    tests/test_thickness.py::ThicknessTest::test_sphere_reach PASSED         [ 79%]
    tests/test_thickness.py::ThicknessTest::test_sphere_thickness PASSED     [ 79%]
    tests/test_transformations.py::TransformTest::test_around PASSED         [ 80%]
    tests/test_transformations.py::TransformTest::test_doctest PASSED        [ 80%]
    tests/test_transformations.py::TransformTest::test_downstream PASSED     [ 81%]
    tests/test_transformations.py::TransformTest::test_rotation PASSED       [ 81%]
    tests/test_transformations.py::TransformTest::test_tiny PASSED           [ 82%]
    tests/test_triangles.py::TrianglesTest::test_barycentric FAILED          [ 83%]
    tests/test_triangles.py::TrianglesTest::test_closest PASSED              [ 83%]
    tests/test_triangles.py::TrianglesTest::test_degenerate PASSED           [ 84%]
    tests/test_units.py::UnitsTest::test_conversion FAILED                   [ 84%]
    tests/test_units.py::UnitsTest::test_path PASSED                         [ 85%]
    tests/test_units.py::UnitsTest::test_units FAILED                        [ 85%]
    tests/test_utility.py::VectorTests::test_align PASSED                    [ 86%]
    tests/test_utility.py::VectorTests::test_unitize_multi PASSED            [ 86%]
    tests/test_utility.py::UtilTests::test_bounds_tree PASSED                [ 87%]
    tests/test_utility.py::UtilTests::test_concat PASSED                     [ 87%]
    tests/test_utility.py::UtilTests::test_pairwise PASSED                   [ 88%]
    tests/test_utility.py::UtilTests::test_strips PASSED                     [ 89%]
    tests/test_utility.py::IOTest::test_dae PASSED                           [ 89%]
    tests/test_utility.py::ContainsTest::test_inside PASSED                  [ 90%]
    tests/test_utility.py::MassTests::test_mass FAILED                       [ 90%]
    tests/test_utility.py::IOWrapTests::test_file_hash PASSED                [ 91%]
    tests/test_utility.py::IOWrapTests::test_io_wrap PASSED                  [ 91%]
    tests/test_utility.py::CompressTests::test_compress PASSED               [ 92%]
    tests/test_validate.py::ValidTest::test_validate FAILED                  [ 92%]
    tests/test_vector.py::SphericalTests::test_spherical PASSED              [ 93%]
    tests/test_vector.py::HemisphereTests::test_hemisphere PASSED            [ 93%]
    tests/test_vhacd.py::VHACDTest::test_vhacd PASSED                        [ 94%]
    tests/test_visual.py::VisualTest::test_concatenate PASSED                [ 95%]
    tests/test_visual.py::VisualTest::test_conversion PASSED                 [ 95%]
    tests/test_visual.py::VisualTest::test_data_model FAILED                 [ 96%]
    tests/test_visual.py::VisualTest::test_interpolate PASSED                [ 96%]
    tests/test_visual.py::VisualTest::test_smooth FAILED                     [ 97%]
    tests/test_visual.py::VisualTest::test_vertex FAILED                     [ 97%]
    tests/test_visual.py::VisualTest::test_visual FAILED                     [ 98%]
    tests/test_voxel.py::VoxelTest::test_local PASSED                        [ 98%]
    tests/test_voxel.py::VoxelTest::test_marching PASSED                     [ 99%]
    tests/test_voxel.py::VoxelTest::test_voxel FAILED                        [100%]
    ============================= 10 tests deselected ==============================
    ============ 53 failed, 130 passed, 10 deselected in 124.47 seconds ============
    

    failures.log

    opened by hroncok 17
  • Split mesh using a splitting plane

    Split mesh using a splitting plane

    I want to split a mesh using a plane, similar to cross_section but returning both halves of the mesh as closed meshes instead of the polyline boundary of the intersection surface. I wrote the following code to achieve this:

    def split_mesh(mesh, plane_origin, plane_normal):
        """
        Splits mesh by intersection with a plane, 
        returns closed mesh for positive and negative side of plane 
        """
        plane_normal = unit_vector(plane_normal)
        
        polyline, triangles_edge = trimesh.intersections.mesh_plane(bbox_mesh, 
                                                               plane_origin= plane_origin, 
                                                               plane_normal= plane_normal, 
                                                               return_faces=True)
        
        vertices_negative = []
        vertices_positive = []
        for vertex_id, vertex in enumerate(bbox_mesh.vertices):
            # TODO: add threshold
            signed_distance = np.dot(plane_normal, np.subtract(vertex, plane_origin))
            if signed_distance < 0:
                vertices_negative.append(vertex_id)
            else:
                vertices_positive.append(vertex_id)
        
        triangles_negative = []
        triangles_positive = []
        # For each triangle check how many points on either side of the plane
        for triangle in bbox_mesh.faces:
            triangle_negative_check = [vertex_id in vertices_negative for vertex_id in triangle]
            # If all points on the negative side this triangle is not affected
            if all(triangle_negative_check):
                triangles_negative.append(np.array(bbox_mesh.vertices[triangle]))
            elif any(triangle_negative_check):
                # Get section line for this triangle on edge
                section = polyline[np.all(np.isin(bbox_mesh.faces[triangles_edge], np.array(triangle)), axis=1)][0]
                assert section.shape == (2,3)
                # If two points on negative side
                if sum(triangle_negative_check) == 2:
                    vertex_1, vertex_2 = bbox_mesh.vertices[triangle[triangle_negative_check]]
                    triangles_negative.extend(unordered_to_triangles([section[0], section[1], vertex_1, vertex_2]))
                    vertex_3 = bbox_mesh.vertices[triangle[np.logical_not(triangle_negative_check)]]
                    triangles_positive.append(np.vstack([section[0], section[1], vertex_3]))
                elif sum(triangle_negative_check) == 1:
                    vertex_3 = bbox_mesh.vertices[triangle[triangle_negative_check]]
                    triangles_negative.append(np.vstack([section[0], section[1], vertex_3]))
                    vertex_1, vertex_2 = bbox_mesh.vertices[triangle[np.logical_not(triangle_negative_check)]]
                    triangles_positive.extend(unordered_to_triangles([section[0], section[1], vertex_1, vertex_2]))
            # If all points on the positive side this triangle is not affected
            else:
                triangles_positive.append(np.array(bbox_mesh.vertices[triangle]))
                
        # Get triangulation of the split surface
        vertices_splitsurface = []
        for a, b in polyline:
            for vertex in [a,b]:
                if tuple(vertex) not in vertices_splitsurface:
                    vertices_splitsurface.append(tuple(vertex))
    
        triangles_splitsurface = unordered_to_triangles(vertices_splitsurface)
        
        # Add triangles of split surface to both the positive and negative side
        triangles_negative.extend(triangles_splitsurface)
        triangles_positive.extend(triangles_splitsurface)
        
        # Create Trimesh from triangles for positive and negative
        mesh_negative = trimesh.Trimesh(*to_face_indices(triangles_negative))
        mesh_positive = trimesh.Trimesh(*to_face_indices(triangles_positive))
        
        return mesh_positive, mesh_negative
    

    Would it be useful to improve and create a pull request, or is there a much more simple way to achieve this with trimesh?

    Looking forward to your reply!

    opened by themmes 17
  • mesh loading not order preserving?

    mesh loading not order preserving?

    I realized recently that when a mesh is loaded, the vertex order is not preserved? That is, mesh.vertices[0] is not the first vertex in the original mesh file.

    the format I'm using is OFF.

    I feel this is fundementally problematic, and can cause serious confusion to users.

    opened by riaqn 17
  • save mesh as an image

    save mesh as an image

    Hi,

    Thank you for the great library.

    Is there a way to save the current mesh as an image, but to return a numpy array (or other type of arrays) instead. I know that data = scene.save_image(resolution=(1080,1080)) return an image, but in bytes :(

    Thank you in advance!

    opened by pvnieo 16
  • [WIP] Texture Support

    [WIP] Texture Support

    Opening a PR to track progress; this is slowly getting there. Fixes #218

    • [x] TextureVisual object
    • [ ] Finalize TextureVisuals data structure (multiple materials? manage UV for multiple materials?)
    • [x] Material Objects: PBR, Basic
    • [ ] Finalize Material data structure
    • [X] Assets resolver for file system, ZIP, and remote
    • [x] OBJ load texture
    • [x] UV/Texture to color
    • [x] GLTF load texture
    • [x] GLTF exports don't crash on texture
    • [x] PLY load texture
    • [x] Figure out why PLY textures are mapping wrong
    • [x] Pyglet viewer show texture

    Post-PR Scope

    • GLTF export texture
    • PLY export texture
    • OBJ export texture
    • shaders for PBR in pyglet viewer trimesh.viewer.windowed

    Other major changes: trimesh.io and trimesh.path.io renamed to trimesh.exchange and trimesh.path.exchange, as io is a builtin library that we were previously stomping on and relying on import precedence. exchange isn't so great but is better than anything else I could think of, and is used elsewhere to mean this (Hoops Exchange is basically a bunch of importers/exporters).

    opened by mikedh 15
  • pyembree ray casting acceleration

    pyembree ray casting acceleration

    Hi @mikedh and thanks for the nice work with trimesh. I am quite new with it but it really gets in hand.

    My question now is related with the ray casting approach based on the pyembree acceleration. I need to ray cast quite a few rays in a 3D scene, approx. 18060108 rays and maybe even more. Imagine that I have a scene with 16676 faces and from each face center I need to cast 1083 rays (i.e. 16676x1083 = 18060108).

    As you understand I want to avoid for loops and I cast all the rays at once. Also I am using the pyembree based solution, as it states to accelerate the casting procedure in comparison to normal version, as follows:

    import trimesh
    import numpy as np
    
    mesh = trimesh.Trimesh(vertices=vertices, faces=faces, face_normals=normals, process=False, use_embree=True) # create mesh from given data
    
    intersection_points, index_ray, index_tri = mesh.ray.intersects_location(origins, drays, multiple_hits=False) # where origins are my triangle centers and drays my rays direction vectors
    
    

    but this seems to perform quite slow as well, even though I am using pyembree acceleration. Is this the best I can get or I am doing something wrong?

    opened by ttsesm 14
  • Update gmsh.py

    Update gmsh.py

    According to the GMSH documentation, these are the available algorithms: 3D mesh algorithm (1: Delaunay, 3: Initial mesh only, 4: Frontal, 7: MMG3D, 9: R-tree, 10: HXT)

    This tiny patch makes sure that trimesh is up-to-date with the latest GMSH.

    opened by kotsaloscv 0
  • Geometry that doesn't have mtl information is being combined with another geometry in the scene

    Geometry that doesn't have mtl information is being combined with another geometry in the scene

    Hi, thanks for this amazing project!

    I have a following issue:

    After adding three objects as OBJ in the scene by using add_geometry method, I manually changed the name of material of each object, except one that doesn't have a material(no mtl file).

    for index,id_name in enumerate(mesh.geometry):
                    geo_mesh = mesh.geometry[id_name]
                    if hasattr(geo_mesh.visual, "material"):
                            geo_mesh.visual.material.name = id_name
                    else:
                            pass
    

    Here is the output of the mesh.geometry.keys(): OrderedDict([('2362ec480b3e9baa4fd5721982c508ad_support_table', <trimesh.Trimesh(vertices.shape=(512, 3), faces.shape=(284, 3))>), ('cup_9_watertight', <trimesh.Trimesh(vertices.shape=(44592, 3), faces.shape=(89180, 3))>), ('pitcher_1', <trimesh.Trimesh(vertices.shape=(4973, 3), faces.shape=(9344, 3))>)])

    The one that doesn't have a matrial, its visual is <trimesh.visual.color.ColorVisuals object at 0x7f2c8c04ef10>

    Then I export the mesh as a OBJ file and reload it using: trimesh.load(path)

    However, when I checked the mesh.geometry again, I found there are only two geometries in the scene. The output of the mesh.geometry.keys(): OrderedDict([('pitcher_1', <trimesh.Trimesh(vertices.shape=(4674, 3), faces.shape=(9344, 3))>), ('2362ec480b3e9baa4fd5721982c508ad_support_table', <trimesh.Trimesh(vertices.shape=(44736, 3), faces.shape=(89464, 3))>)])

    It seems like the geometry that doesn't have mtl information is being combined with another geometry(support_table) in the scene. This is unexpected. Could you please give me some hints where the problem could be? I suppose it's possible to add the mesh to the scene that doesn't have mtl information right?

    Thanks in advance! Best Regards, Cai

    opened by caixiaoniweimar 0
  • Analyze the individual sections of every sides.

    Analyze the individual sections of every sides.

    Hi,

    I am new to trimesh and just a beginner with it. I have a problem where I have an stl object and i would like to analyze individual section of the parts and slice / crop them in 3D. For ex: 3D object of a car door panel and i would like to slice only the part where there is nuts / bolts in them. I have couple of questions related to that please.

    1. Is there any direct / easy method to analyze every sides and their individual sections of the panel after applying the mesh ?
    2. How to crop / slice the portion on each section of the sides, where there is a screw & nuts

    Regards, Rudy

    opened by NiRu-hub 0
  • PR access denied

    PR access denied

    Hi, I would like to open a small PR regarding this issue. When I try to push my brnach thoug, I get the following error message:

    ERROR: Permission to mikedh/trimesh.git denied to anna-charlotte.
    fatal: Could not read from remote repository.
    Please make sure you have the correct access rights
    and the repository exists.
    

    How can I open a PR?

    opened by anna-charlotte 1
  • Apply_scale doesn't work (as expected) for trimesh.primitives

    Apply_scale doesn't work (as expected) for trimesh.primitives

    Following setup:

    import trimesh
    s = trimesh.primitives.Sphere()
    s.extents
    # array([2., 2., 2.])    -- Fine
    s.apply_scale(0.5)
    s.extents
    # array([2., 2., 2.])    -- Not fine
    s.primitive.radius
    # 1.0 -- Also not fine
    

    I think this is due to the fact that the scaling information is encoded in the transform matrix:

    s.transform
    # TrackedArray([[0.5, 0.  , 0.  , 0.  ],
    #              [0.  , 0.5, 0.  , 0.  ],
    #              [0.  , 0.  , 0.5, 0.  ],
    #              [0.  , 0.  , 0.  , 1.  ]])
    

    But I think it would be much more helpful if the parameters were updated accordingly by e.g. overriding apply_scale().

    I deal with this problem (having primitives and Trimeshes in the same scene) currently by a big if-else-branch.

    Any thoughts? Thanks!

    opened by clemense 0
Releases(3.17.1)
Owner
Michael Dawson-Haggerty
Michael Dawson-Haggerty
Picasso: A CUDA-based Library for Deep Learning over 3D Meshes

The Picasso Library is intended for complex real-world applications with large-scale surfaces, while it also performs impressively on the small-scale applications over synthetic shape manifolds. We have upgraded the point cloud modules of SPH3D-GCN from homogeneous to heterogeneous representations, and included the upgraded modules into this latest work as well. We are happy to announce that the work is accepted to IEEE CVPR2021.

null 97 Dec 1, 2022
Blender Add-On for slicing meshes with planes

MeshSlicer Blender Add-On for slicing meshes with multiple overlapping planes at once. This is a simple Blender addon to slice a silmple mesh with mul

null 52 Dec 12, 2022
DeepMetaHandles: Learning Deformation Meta-Handles of 3D Meshes with Biharmonic Coordinates

DeepMetaHandles (CVPR2021 Oral) [paper] [animations] DeepMetaHandles is a shape deformation technique. It learns a set of meta-handles for each given

Liu Minghua 73 Dec 15, 2022
Code for HodgeNet: Learning Spectral Geometry on Triangle Meshes, in SIGGRAPH 2021.

HodgeNet | Webpage | Paper | Video HodgeNet: Learning Spectral Geometry on Triangle Meshes Dmitriy Smirnov, Justin Solomon SIGGRAPH 2021 Set-up To ins

Dima Smirnov 61 Nov 27, 2022
Learning Generative Models of Textured 3D Meshes from Real-World Images, ICCV 2021

Learning Generative Models of Textured 3D Meshes from Real-World Images This is the reference implementation of "Learning Generative Models of Texture

Dario Pavllo 115 Jan 7, 2023
The all new way to turn your boring vector meshes into the new fad in town; Voxels!

Voxelator The all new way to turn your boring vector meshes into the new fad in town; Voxels! Notes: I have not tested this on a rotated mesh. With fu

null 6 Feb 3, 2022
CoSMA: Convolutional Semi-Regular Mesh Autoencoder. From Paper "Mesh Convolutional Autoencoder for Semi-Regular Meshes of Different Sizes"

Mesh Convolutional Autoencoder for Semi-Regular Meshes of Different Sizes Implementation of CoSMA: Convolutional Semi-Regular Mesh Autoencoder arXiv p

Fraunhofer SCAI 10 Oct 11, 2022
Approximate Nearest Neighbors in C++/Python optimized for memory usage and loading/saving to disk

Annoy Annoy (Approximate Nearest Neighbors Oh Yeah) is a C++ library with Python bindings to search for points in space that are close to a given quer

Spotify 10.6k Jan 4, 2023
Simple tools for logging and visualizing, loading and training

TNT TNT is a library providing powerful dataloading, logging and visualization utilities for Python. It is closely integrated with PyTorch and is desi

null 1.5k Jan 2, 2023
Calling Julia from Python - an experiment on data loading

Calling Julia from Python - an experiment on data loading See the slides. TLDR After reading Patrick's blog post, we decided to try to replace C++ wit

Abel Siqueira 8 Jun 7, 2022
Python code for loading the Aschaffenburg Pose Dataset.

Aschaffenburg Pose Dataset (APD) This repository contains Python code for loading and filtering the Aschaffenburg Pose Dataset. The dataset itself and

null 1 Nov 26, 2021
Pytorch implementation of MLP-Mixer with loading pre-trained models.

MLP-Mixer-Pytorch PyTorch implementation of MLP-Mixer: An all-MLP Architecture for Vision with the function of loading official ImageNet pre-trained p

Qiushi Yang 2 Sep 29, 2022
dyld_shared_cache processing / Single-Image loading for BinaryNinja

Dyld Shared Cache Parser Author: cynder (kat) Dyld Shared Cache Support for BinaryNinja Without any of the fuss of requiring manually loading several

cynder 76 Dec 28, 2022
A python library for face detection and features extraction based on mediapipe library

FaceAnalyzer A python library for face detection and features extraction based on mediapipe library Introduction FaceAnalyzer is a library based on me

Saifeddine ALOUI 14 Dec 30, 2022
This source code is implemented using keras library based on "Automatic ocular artifacts removal in EEG using deep learning"

CSP_Deep_EEG This source code is implemented using keras library based on "Automatic ocular artifacts removal in EEG using deep learning" {https://www

Seyed Mahdi Roostaiyan 2 Nov 8, 2022
Python wrappers to the C++ library SymEngine, a fast C++ symbolic manipulation library.

SymEngine Python Wrappers Python wrappers to the C++ library SymEngine, a fast C++ symbolic manipulation library. Installation Pip See License section

null 136 Dec 28, 2022
Fast image augmentation library and easy to use wrapper around other libraries. Documentation: https://albumentations.ai/docs/ Paper about library: https://www.mdpi.com/2078-2489/11/2/125

Albumentations Albumentations is a Python library for image augmentation. Image augmentation is used in deep learning and computer vision tasks to inc

null 11.4k Jan 9, 2023
Technical Indicators implemented in Python only using Numpy-Pandas as Magic - Very Very Fast! Very tiny! Stock Market Financial Technical Analysis Python library . Quant Trading automation or cryptocoin exchange

MyTT Technical Indicators implemented in Python only using Numpy-Pandas as Magic - Very Very Fast! to Stock Market Financial Technical Analysis Python

dev 34 Dec 27, 2022