Code Monkey home page Code Monkey logo

trimesh's Introduction

trimesh


Github Actions codecov Docker Image Version (latest by date) PyPI version

Trimesh is a pure Python 3.7+ 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's a quick development and contributing guide.

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 (embreex), 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.

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 Manifold3D or Blender 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
  • Approximate minimum volume oriented bounding boxes for meshes
  • Approximate minimum volume bounding spheres
  • 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://trimesh.org/},
	version = {3.2.0},
	date = {2019-12-8},
}

Containers

If you want to deploy something in a container that uses trimesh automated debian:slim-bullseye based builds with trimesh and most dependencies are available on Docker Hub with image tags for latest, git short hash for the commit in main (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.

trimesh's People

Contributors

bbarroqueiro avatar clemense avatar dabeschte avatar dbukenberger avatar epicwink avatar eric-vin avatar erkhembayaar avatar freakthemighty avatar glennkerbiriou avatar hyeonseonam avatar ibozhilov avatar iory avatar jackd avatar jonasnebl avatar keisuke-ishihara avatar krande avatar lejafar avatar luisbc92 avatar marviel avatar mikedh avatar mjd3 avatar mmatl avatar pauldmccarthy avatar pchorak avatar psigen avatar tobaloidee avatar whateverforever avatar willmycroft avatar wkentaro avatar yuanwenqing avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

trimesh's Issues

NameError: name 'load_path' is not defined

Update 2016-08-16:

Hmm... I tried Python 2.7 but the problem remains.

Hi Michael,

First of all - thank you for the excellent package!

I think that it is because I am using Python 3.5 (Anaconda Python 3.5 64 bit in 64 bit Windows 10), I have this error (potentially due to the relative import mechanism).

When I do the following code snippet:

import numpy as np
import trimesh

if __name__ == '__main__':
    mesh = trimesh.load_mesh(test.stl')
    section = mesh.section(plane_origin=[0, 0, 0],
                           plane_normal=[0, 0, 1])

I see the following error info:

runfile('C:/ProjectFolder/MaterialFEA/geometry/Export/test.py', wdir='C:/ProjectFolder/MaterialFEA/geometry/Export')
Traceback (most recent call last):

  File "<ipython-input-14-a216ee98491b>", line 1, in <module>
    runfile('C:/ProjectFolder/MaterialFEA/geometry/Export/test.py', wdir='C:/ProjectFolder/MaterialFEA/geometry/Export')

  File "C:\Anaconda3\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 714, in runfile
    execfile(filename, namespace)

  File "C:\Anaconda3\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 89, in execfile
    exec(compile(f.read(), filename, 'exec'), namespace)

  File "C:/ProjectFolder/MaterialFEA/geometry/Export/test.py", line 15, in <module>
    plane_normal=[0, 0, 1])

  File "C:\Anaconda3\lib\site-packages\trimesh\base.py", line 945, in section
    path = load_path(lines)

NameError: name 'load_path' is not defined

Do you think it is the python 2 vs 3 compatibility issue due to the relative imports? Any ideas on whether there is a quick fix available?

Thank you!

Shawn

PS: The stl file is just a simple cube for testing and I don't think that is the issue.

Path3D via mesh.section should reference incomplete sections

I'm using the mesh.section function to detect intersections. The problem is that a lot of our data is incorrect geometry (missing faces), and the library discards those segments completely. Having that data is essential when dealing with bad files.

error caught in embree.

Hi,

Very simple piece of code leads to the following error warning, although it does not break the code execution. I am not 100% sure it is trimesh related, however, any clarification would be helpful.

The issue is following.
I load a mesh from an 'obj' file and execute ray.inersects_any function by using a single ray origin and unit vector. Since there is no single intersection the function returns 'False'. In the next line I load again the same 'obj' file (assigning a different variable name) and execute again a ray.inersects_any function. The following three lines of error warnings appears before the output of the function (which is again 'False'):

ERROR CAUGHT IN EMBREE
ERROR: Invalid operation
ERROR MESSAGE: b'already initialized'

The code is:

import trimesh

ray_origin = [[5, -0.001, 2.5]]
vector = [[0, -1, 0]]

mesh = trimesh.load_mesh('./test_mesh.obj')
intersect = mesh.ray.intersects_any(ray_origin, vector)
print(intersect)

mesh1 = trimesh.load_mesh('./test_mesh.obj')
intersect = mesh1.ray.intersects_any(ray_origin, vector)
print(intersect)

While the 'test_mesh.obj' is:

v 0 0 5
v 0 0 0
v 10 0 5
v 10 0 0
v 10 10 5
v 10 10 0
v 0 10 5
v 0 10 0
f 1 2 3
f 3 2 4
f 3 4 5
f 5 4 6
f 5 6 7
f 7 6 8
f 7 8 1
f 1 8 2

Best regards

Update a mesh while it's being displayed

Hi,

Is it possible to update a mesh, specifically its vertex colours, while it's being shown with mesh.show()?
I found a mesh.visual.update_vertices(mask=...) method but I don't think it does what I'm looking for.

Specifically, if I wanted to add a slider to the viewer that changes vertex colour and position values of the mesh, and the mesh should update in real-time in the viewer. Do you think that's feasible with trimesh and pyglets? Would I probably best start by copying viewer.py?

Any advice welcome - I'm looking for a simple tool that would allow me to achieve this. I've also been thinking about using Blender's Python interface for this.

Decimate a mesh

Hi Mike,

(I assume this would be a simple question :)

Would there be any simple way to decimate a mesh? Say, simplify the geometry from 10,000 faces to 1,000 faces?

Thanks!

Shawn

Missing inertial component

file: tirangle.py
function: mass_properties
problem: Missing value inertia[1,0]

inertia = np.zeros((3,3))
inertia[0,0] = integrated[5] + integrated[6] - (volume * (center_mass[[1,2]]**2).sum())
inertia[1,1] = integrated[4] + integrated[6] - (volume * (center_mass[[0,2]]**2).sum())
inertia[2,2] = integrated[4] + integrated[5] - (volume * (center_mass[[0,1]]**2).sum())
inertia[0,1] = (integrated[7] - (volume * np.product(center_mass[[0,1]])))
inertia[1,2] = (integrated[8] - (volume * np.product(center_mass[[1,2]])))
inertia[0,2] = (integrated[9] - (volume * np.product(center_mass[[0,2]])))
inertia[2,0] = inertia[0,2]
inertia[2,1] = inertia[1,2] 

-----------------

Add the following line which is missing

-----------------

inertia[1,0] = inertia[0,1]

Bug in merge_points

Hi Michael,

yesterday I came across your code because I have to merge a lot of points of a surface triangulation. After some testing I came across a bug in the "merge_vertices()" function of your code.
Here is a minimal example:

import numpy as np
import trimesh
nds2 = np.array([[ 1. , 1.60994057, 2.07798185],
[ 1. , 0.60994057, 2.07798184],
[ 1. , 0.60994057, 2.07798185]])
ele2 = np.array([[0,1,2]])
mesh = trimesh.Trimesh(vertices=nds2, faces=ele2,process=True)
mesh.merge_vertices()
print mesh.vertices
print np.linalg.norm(mesh.vertices[1]-mesh.vertices[2])

Best,
Konrad

Shapely polygon from trimesh.path.path.Path2D

I have sliced a 3d mesh with yz planes into several 2d sections (i.e. Path2D objects). Now, I'd like to create Shapely polygons from these 2d sections. For doing so, I need a list of points as XY coords. However, if I use Path2D.vertices, I get a weird polygon because the vertices are un-ordered. Is there any way to get an ordered list of the vertices composing a Path2D object?

Obviously I cannot use the python .sort() method.

Request: Trimesh.edges_sparse

In complete analogy to Trimesh.faces_sparse from #19, I have a use case for a cached sparse matrix encoding the mapping vertex -> edges:

edges = self.face_adjacency_edges
row = edges.reshape(-1)
col = np.tile(np.arange(len(edges)).reshape(-1, 1), (1, 2)).reshape(-1)
self.edges_sparse = sp.coo_matrix(
    (np.ones(len(col), dtype=np.bool), (row, col)),
    shape=(vertex_count, len(edges)), dtype=np.bool)

Then

G = np.zeros((vertex_count, 3))
for edge, f in zip(self.face_adjacency_edges, F):
    G[edge] += f

is equivalent to:

G = self.edges_sparse.dot(F)

Weird outline edges orientation

Hi! Thanks for the great library!

Let's consider the following code:

    paths = mesh.outline().paths
    for p in paths:
        entities = mesh.outline().entities[p]
        ee = []
        for e in entities:
            ee.extend(e.points)
        print(ee)

Which is just extracting the list of vertex indexes from each outline. In my case the following result is obtained:

[0, 19, 0, 69, 168, 69, 192, 168, 192, 124, 138, 124, 138, 8, 8, 98, 98, 76, 197, 76, 197, 112, 21, 112, 59, 21, 59, 177, 9, 177, 108, 9, 22, 108, 22, 157, 126, 157, 194, 126, 6, 194, 6, 73, 213, 73, 139, 213, 139, 89, 89, 180, 39, 180, 18, 39, 93, 18, 93, 55, 55, 20, 101, 20, 129, 101, 129, 64, 64, 58, 42, 58, 42, 170, 170, 154, 209, 154, 128, 209, 128, 153, 46, 153, 46, 37, 71, 37, 71, 164, 164, 115, 115, 48, 121, 48, 121, 120, 30, 120, 150, 30, 150, 137, 137, 176, 77, 176, 188, 77, 17, 188, 17, 191, 191, 195, 195, 106, 205, 106, 158, 205, 158, 181, 65, 181, 65, 19]
[12, 107, 12, 140, 140, 97, 97, 27, 160, 27, 160, 60, 60, 100, 100, 11, 63, 11, 63, 52, 29, 52, 29, 184, 165, 184, 3, 165, 3, 56, 167, 56, 204, 167, 204, 207, 207, 13, 136, 13, 136, 196, 62, 196, 62, 202, 202, 151, 50, 151, 50, 155, 211, 155, 211, 2, 4, 2, 4, 78, 61, 78, 61, 198, 198, 161, 70, 161, 70, 199, 47, 199, 32, 47, 32, 24, 44, 24, 44, 51, 148, 51, 148, 15, 86, 15, 86, 111, 111, 118, 45, 118, 45, 28, 28, 144, 49, 144, 49, 85, 43, 85, 43, 5, 34, 5, 34, 212, 212, 105, 178, 105, 178, 125, 125, 94, 94, 147, 16, 147, 53, 16, 53, 75, 25, 75, 25, 103, 103, 182, 182, 166, 145, 166, 169, 145, 102, 169, 102, 14, 206, 14, 206, 67, 67, 119, 1, 119, 1, 107]
[146, 92, 189, 92, 189, 23, 132, 23, 90, 132, 90, 82, 82, 81, 81, 80, 79, 80, 83, 79, 91, 83, 91, 133, 133, 26, 186, 26, 186, 190, 33, 190, 33, 152, 117, 152, 117, 193, 40, 193, 57, 40, 185, 57, 185, 95, 95, 174, 141, 174, 74, 141, 74, 179, 135, 179, 172, 135, 172, 163, 163, 162, 130, 162, 130, 66, 66, 84, 84, 201, 68, 201, 116, 68, 116, 131, 131, 54, 54, 31, 200, 31, 200, 88, 159, 88, 159, 203, 41, 203, 41, 114, 114, 143, 142, 143, 113, 142, 38, 113, 38, 183, 183, 99, 171, 99, 208, 171, 208, 110, 173, 110, 87, 173, 87, 96, 72, 96, 210, 72, 210, 109, 10, 109, 10, 156, 134, 156, 134, 127, 123, 127, 123, 122, 104, 122, 104, 35, 175, 35, 175, 149, 187, 149, 187, 36, 7, 36, 7, 146]

That is, 3 outlines. In the former two cases, I was expecting that the last point of each edge was the first point of the next one ([19, 0, 0, 69, 69, 168, 168, 192, 192, ...]).

Anyway, assuming that such weird organization is right, the last outline has something strange at the end... i.e. concatenating the points list with itself you would ge something like [..., 36, 7, 146, 146, 92, 189, ...]

Am I doing something wrong? Is it the expected behavior?
I know that paths is only returning the list of entities from the mesh.outline(), and therefore which its orientation. However, looking at the code of closed_paths() function, it seems you are caring about the orientation

Thanks!
Pepe

Rotation stops working after resetting the view with 'z'

Hi!

When I display a mesh with mesh.show(), rotating it with the left mouse works, until I press 'z' to reset the view - then, it only rotates around the z-axis (roll-angle), and not around the other two axes anymore. It's 100% reproducible.

This is on Win 10 in a virtualenv with python-3.6.1 (64-bit), numpy+mkl, scipy and Shapely from here, and pip install trimesh[easy].

mesh.copy() segfaults, can't deep copy rtree.index.Index??

The following code segfaults for me after 15-20 iterations:

import trimesh
from copy import deepcopy

mesh = trimesh.primitives.Sphere()
mesh.triangles_tree()

print type(mesh._cache.cache['_triangles_tree'])

for i in range(0,30):
    print i
    deepcopy(mesh)

print "done"

(This was distilled for a much larger failing example that was originally calling mesh.copy(), which is implemented with deepcopy()).

Because mesh.copy() breaks, so do all of the functions that depend on it for their implementation.

I suspect the problem is the rtree.index.Index object stored in the mesh cache in the slot '_triangles_tree' -- it doesn't look like it supports deepcopy.

Consider: clearing the cache before copying (seems to work in a simple example), OR, to implement copy / deepcopy in the util.Cache object to do the right thing.

pip install missing some dependencies

The pip install trimesh worked fine, but when I attempted to import the module (import trimesh) it failed since shapely was not found.

Info: windows 10, WinPython 3.5.1.2, Python 3.5, 64bit

trimesh documentation

Hi,

My apologies if this is not the right place for this question. Could you please tell me how to generate the documentation for the trimesh library? I have not yet found the readme that explains how it is done! Many thanks.

Adrian Butscher

Screen shots are blank under MacOS

It seems the MacOS implementation of the window doesn't render with visible=false. This affects both the following ways of generating an image:

mesh.scene().save_image('image.png')
and
mesh.show(save_image='image.png')

As a work around, could you please add the option to set the visible flag to True even when using save_image?

Otherwise the workaround is to duplicate much of SceneViewer() to force visible to True

Bug in Trimesh.vertex_normals?

Hello there! I have recently started using your package and find it very appealing. Thank you for sharing your work!

It seems that geometry.mean_vertex_normals loses information about the normals of some incident faces, i.e. when the list of indices faces[:,i] contains repetitions. Instead, to create a field G on vertices by accumulating a field F on incident faces, I use:

G = np.zeros([len(self.vertices)] + list(F.shape[1:]))
for i, face in enumerate(self.faces):
    G[face] += F[i]

The mapping vertex -> incident faces could be cached as a sparse matrix, but for tasks similar to this, the necessary lookups probably wouldn't improve performance.

SceneViewer has _nswindow = None

I've successfully loaded a little mesh and checked it for watertightness, etc., and I'm trying to 'show' it in a pyglet window using some code I cribbed from trimesh's examples. I know my pyglet / pygame / opengl installation is (at least somewhat) correct because I can run their samples standalone, e.g., THIS ONE. The trimesh code is shown below.

import numpy as np
import trimesh

mesh = trimesh.load_mesh('../models/j39binary.fbx')
mesh.is_watertight # ~~> True
mesh.euler_number # ~~> 2
np.divide(mesh.volume, mesh.convex_hull.volume) # ~~> works as expected
mesh.vertices -= mesh.center_mass # ditto
mesh.moment_inertia # ~~> results look reasonable
mesh.split() # ~~> ok
for facet in mesh.facets:
    mesh.visual.face_colors[facet] = trimesh.visual.random_color()

mesh.show() # ~~> throws inside here
# omitting the rest

The stack trace is below. The symptom is that _nswindow in the SceneViewer is None. I've stepped through a ton of pyglet code. The problem seems to be that a correctly created _nswindow is thrown away on line 209 of gl/__init__py with a call of app.windows.remove. Later on, SceneViewer tries to use _nswindow, but it's gone. This appears to be due to some multithreading issues or some tricks for creating windows in the face of circular imports, it's hard to say from the code I stepped through.

I just don't know enough about pyglet and its Cocoa implementation to figure out a workaround, so I thought I'd bring this to you. Simple things like trying to create a new window right inside trimesh's viewer.py and assigning it to _nswindow caused more problems and the exercise is becoming a classic rabbit hole. I'm sure it's something simple, but I'm not sure it's my problem because I copy-pasted the calling code from trimesh samples. Also, I am not the only one with the problem: please see THIS STACKOVERFLOW QUESTION. I'd be grateful for any advice.

Traceback (most recent call last):
  File "/Applications/PyCharm CE.app/Contents/helpers/pydev/pydevd.py", line 1591, in <module>
    globals = debugger.run(setup['file'], None, None, is_module)
  File "/Applications/PyCharm CE.app/Contents/helpers/pydev/pydevd.py", line 1018, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Users/bbeckman/Documents/trimesh/examples/debugio.py", line 43, in <module>
    mesh.show()
  File "/Users/bbeckman/Documents/trimesh/trimesh/base.py", line 1373, in show
    scene.show(**kwargs)
  File "/Users/bbeckman/Documents/trimesh/trimesh/scene/scene.py", line 388, in show
    viewer()
  File "/Users/bbeckman/Documents/trimesh/trimesh/scene/scene.py", line 385, in viewer
    SceneViewer(self, **kwargs)
  File "/Users/bbeckman/Documents/trimesh/trimesh/scene/viewer.py", line 31, in __init__
    self.reset_view(flags=flags)
  File "/Users/bbeckman/Documents/trimesh/trimesh/scene/viewer.py", line 129, in reset_view
    if (self.width is not None and
  File "/Users/bbeckman/anaconda/envs/rsimcon-pyassimp-minimal/lib/python2.7/site-packages/pyglet/window/__init__.py", line 850, in <lambda>
    width = property(lambda self: self.get_size()[0],
  File "/Users/bbeckman/anaconda/envs/rsimcon-pyassimp-minimal/lib/python2.7/site-packages/pyglet/window/cocoa/__init__.py", line 398, in get_size
    window_frame = self._nswindow.frame()
AttributeError: 'NoneType' object has no attribute 'frame'

Voxelisation is susceptible to rounding error

Hi,

I have an edge case where voxelising a mesh is incorrectly calculating the number of voxels required due to rounding error. The issue is on line 68 in voxel.py, specifically:

raw_shape = np.ptp(bounds/pitch, axis=0).astype(int)

One of the axes is coming out at 14.99999999999999822364, which is converted to 14 as an integer. This later results in out of range indexing in run_to_raw() (voxel.py line 43).

The following change is one viable solution:
raw_shape = (np.ptp(bounds/pitch, axis=0) + (0.5, 0.5, 0.5)).astype(int)

Thanks.

Mesh not loaded correctly

Hi, I have the following error when loading a mesh.

I am trying to load this off file by

import trimesh
mesh = trimesh.load_mesh('chair.off', 'off', process=False)

The header in the off file is

OFF
37580 74488 0

But then the loaded mesh has only 74487 faces. Do you have any idea what's going on here or do I miss anything?
Thank you very much.

PLY reading does not work?

Hi

Older (much older) version of trimesh worked fine for me.
After updating (now 2.2.11), it does not manage to read the PLY file, I get

File "/usr/lib/python2.7/site-packages/trimesh/io/ply.py", line 131, in elements_to_kwargs
faces = elements['face']['data']['vertex_indices']['f1']
ValueError: no field of name vertex_indices

Any ideas on what is the issue in the updated version?

Exception: ascii ply not supported

Hi, can you support models in ascii ply format?

Here is what I got when trying to load a ply file.

'''
51 if 'ascii' in encoding:
---> 52 raise ValueError('ASCII PLY not supported!')
53
54 endian = ['<', '>']['big' in encoding]
'''

trimesh/ section.py

Hi Michael,
my name is Johann from Germany and I did found trimesh very interesting.
The main purpose is cutting a mesh with a plane.
Therefore I did tested section.py and it works well well also if I did cutted featuretype.STL also in the y or x direction changing the code
y_extents = mesh.bounds[:,1]

# slice every .125 model units (eg, inches)
y_levels  = np.arange(*y_extents, step=0.25)
print (y_levels)
# create an array to hold the section objects
sections  = [None] * len(y_levels)

for i, y in enumerate(y_levels):
    print (i, y)
    # this will return a Path3D object, each of which will 
    # have curves in 3D space
    sections[i] = mesh.section(plane_origin = [0,y,0],                                                
                               plane_normal = [0,1,0])

Everything is working fine until I did used a different mesh-file skinabdomen.STL ( body surface generated from dicom CT files)
The script is working for this file for the Z cut as described below:

mesh = trimesh.load_mesh('/home/medinex/trimesh-master/models/skinabdomen.STL')
#mesh = trimesh.load_mesh('/home/medinex/trimesh-master/models/featuretype.STL')
mesh.show()
print (mesh.bounding_box_oriented.primitive.extents)
# we're going to slice the mesh into evenly spaced chunks along z
# this takes the (2,3) bounding box and slices it into [minz, maxz]
z_extents = mesh.bounds[:,2]

# slice every .125 model units (eg, inches)
z_levels  = np.arange(*z_extents, step=10)
print (z_levels)
# create an array to hold the section objects
sections  = [None] * len(z_levels)

for i, z in enumerate(z_levels):
    print (i,z)
    # this will return a Path3D object, each of which will 
    # have curves in 3D space
    sections[i] = mesh.section(plane_origin = [0,0,z],
                               plane_normal = [-0.001,0,1])

In case of Y direction cut
mesh = trimesh.load_mesh('/home/medinex/trimesh-master/models/skinabdomen.STL')
#mesh = trimesh.load_mesh('/home/medinex/trimesh-master/models/featuretype.STL')
mesh.show()
print (mesh.bounding_box_oriented.primitive.extents)
# we're going to slice the mesh into evenly spaced chunks along z
# this takes the (2,3) bounding box and slices it into [minz, maxz]
y_extents = mesh.bounds[:,1]

# slice every .125 model units (eg, inches)
y_levels  = np.arange(*y_extents, step=30)
print (y_levels)
# create an array to hold the section objects
sections = [None]*len(y_levels)
print (len(y_levels))
for j, y in enumerate(y_levels):
    print (j,y)
    # this will return a Path3D object, each of which will 
    # have curves in 3D space
    sections[j] = mesh.section(plane_origin = [0,y,0],
                               plane_normal = [0,1,0])

The error is displayed:

Traceback (most recent call last):
File "/home/medinex/trimesh-master/trimesh/section-skinabdomeny.py", line 43, in
plane_normal = [0,1,0])
File "/usr/local/lib/python2.7/dist-packages/trimesh/base.py", line 998, in section
raise ValueError('Specified plane doesn't intersect mesh!')
ValueError: Specified plane doesn't intersect mesh!

I looked to the base.py, trying to detect the error and I have seen that in your original code and your original line below it is : from .io.load import load_path
Should it maybe be : from .path.io.load import load_path because load path is only there.

your code :
from .io.load import load_path
lines = intersections.mesh_plane(mesh = self,
plane_normal = plane_normal,
plane_origin = plane_origin)
if len(lines) == 0:
raise ValueError('Specified plane doesn't intersect mesh!')
path = load_path(lines)
return path
trying to detect the error.

Please let me know if maybe you see a reason !!
The y direction cut is working without problem for the featuretype.STL file.

Do you have maybe a more detailed description how to use intersections.py from trimesh ?
Thank you in advance
Best regards
Johann

Differences in Pyassimp FBX and STL results

I'm exercising trimesh on some FBX models. I backed off to Pyassimp after having bad luck with Cyassimp (unexplained IO failures in the C code), looking for ways to make quicker progress (I may one day get back to Cyassimp, but I need to move quickly now). I am having good luck with Pyassimp, but I noticed some differences in the shape of results returned to trimesh from Pyassimp when Pyassimp imports FBX models and when Pyassimp imports STL models. I worked around the differences at the trimesh level, but my workarounds aren't sufficiently well thought-out (by a very long margin) to submit as a pull request. In fact, this entire issue may be better addressed as an assimp issue, and I will be digging in there as time allows. In the meantime, I thought I'd bring it to your attention. Perhaps the following code snippet from near line 120 in load.py is self-explanatory. This is what I did to get trimesh to work.

    # make sure we keep passed kwargs to loader
    # but also make sure loader keys override passed keys
    loader_keys = mesh_loaders[file_type](file_obj, file_type)
    # [bbeckman: introduce the variable above to track in the debugger.
    # Apparently, the fbx loader produces a list of dictionaries, whereas the
    # STL loader, used in all the examples in trimesh, produces a dictionary.
    # That trips up kwargs.update. We'll just work around it for our cases.]
    if isinstance(loader_keys, list):
        kwargs.update(loader_keys[0])
    else:
        kwargs.update(loader_keys)

    # [bbeckman: The fbx loader returns a vertex_colors key whose value is
    # the empty list, []. That apparently causes some downstream methods to
    # silently fail. The failure occurs in the ColorVisuals call at the end
    # of visual.create_visual, which is called on line 105 of base.py. We'll
    # work around it for our cases.]
    if 'vertex_colors' in kwargs and \
            not kwargs["vertex_colors"]: # idiom for empty
        kwargs.pop("vertex_colors")

convexify example

with the convexify example, i get an error message stating that "np float object does not support show method."

I'll post the code and the exact error message later. I am using a different stl file than box.stl

convex_combined = np.sum(meshes_convex)
convex_combined.show()

Bounding box corner question

Hi Mike,

I came across your Trimesh library for Python after trialling several other tools for generating signed distance functions from STL files. I’ve found yours to be by far the best for what I need, but I have a simple question that I couldn’t quite figure out from looking at the source:

Essentially what I want to do is to fill the bounding box with a 3D rectangular grid of points, and compute the SDF from these. I am able to generate points and calculate the SDF with no problems, but I don’t know how to get the coordinates of the corners of the bounding box. If I use:

box = mesh.bounding_box_oriented.primitive.extents

I get the half-lengths of each side of the bounding box, but if I use these to plot the 8 corners they form a box the same size and shape as the bounding box, but seemingly randomly oriented in space. How can I correctly get the coordinates of the corners so I can construct my meshgrid within the bounding box?
I see that in bounds.py there is a function corners that seems to do what I want, but I don't know where to get the bounds argument from.

Thank you for your time,
Joel

possible implementation of overlapping and touching test?

Hi, I have a model consists of a list of 3D mesh shapes. I'm trying to find all the pairs of objects that are overlapping or touching.
The way I'm using trimesh now is to choose one mesh, traverse all the other meshes, use rtree find potential intersecting triangles, then do point contain tests to check if they are overlapping. There are three problems:

  1. very slow
  2. sometime the mesh has defect - not water tight, then the point contain test may fail
  3. the objects touching test is not reliable

Do you have any advise on it?

Thanks!

Crashes when cross-section has odd number of intersections

Works fine for most slices, but crashes if it hits one with an odd number of intersections.

File "/usr/share/miniconda/lib/python2.7/site-packages/trimesh/intersections.py", line 37, in mesh_plane_intersection
return intersections.reshape(-1,2,3)
ValueError: total size of new array must be unchanged

Do boolean under windows

Hi Mike,

A quick question - I noticed that the backends of blender or scad are used for boolean operations. While it is trivial to install them in Linux, are you aware that whether there would be a way to get them working in windows?

Thanks!

Shawn

cyassimp missing?

Been having all kinds of trouble installing cyassimp. I've tried many permutations and guesses, pyassimp, assimp, etc. None of them seem to work.

$ conda install -n my-fave-env -c menpo/label/master cyassimp
Fetching package metadata .............
Solving package specifications: 

PackageNotFoundError: Dependency missing in current osx-64 channels: 
  - cyassimp -> assimp 3.0.1270

Close matches found; did you mean one of these?

    assimp: cyassimp

Colors no longer function.

Example program (basically straight from README.md)

import trimesh

mesh = trimesh.load_mesh([PLACE_STL_FILENAME_HERE])

# find groups of coplanar adjacent faces
facets, facets_area = mesh.facets(return_area=True)

# set each facet to a random color
for facet in facets:
  mesh.visual.face_colors[facet] = trimesh.visual.random_color()

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

possible misbehavior after successive transformation and difference operations

i tried to apply some boolean difference operation to a mesh. from a loaded model, i create a bounding cylinder and place a sphere within the scene. then i cut the sphere from the cylinder and rotate the cylinder.
after a couple a iterations, the mesh is surprisingly empty. and nearly all faces are away.

this test i did on ubuntu 16.10 with Python 2.7.12+ with blender and openscad

example.zip

Help on dealing with Qhull error

Hello,

I got Qhull errors when I tried to generate the obb of the following meshes, do you have any suggestions?

Thanks in advance!

Vertices: [[19653.465921167503, 2901.982866568411, 22790.418736883457], [20611.665944905126, 2901.9828184502676, 22794.41878902774], [20612.93810257011, 2896.147085313453, 22489.677315260826], [19654.738078832495, 2896.1471334316, 22485.67726311654], [22372.26582808047, 6.382596408072594, 22857.21861265048], [22373.537985745457, 0.5468632712579691, 22552.47713888356], [21414.065752396516, 6.382729955398236, 22853.218558653392], [21415.337910061502, 0.5469968185836116, 22548.477084886475]]
Faces: [[2, 3, 1], [3, 0, 1], [1, 4, 5], [2, 1, 5], [7, 5, 6], [5, 4, 6], [6, 0, 7], [0, 3, 7], [3, 2, 7], [7, 2, 5], [6, 4, 1], [0, 6, 1]]


Vertices: [[18695.265921167505, 2901.982866568411, 22786.418736883457], [19653.46594490513, 2901.9828184502676, 22790.41878902774], [19654.738102570114, 2896.147085313453, 22485.677315260826], [18696.538078832495, 2896.1471334316, 22481.67726311654], [21414.065828080475, 6.382596408072594, 22853.21861265048], [21415.33798574546, 0.5468632712579691, 22548.47713888356], [20455.86575239652, 6.382729955398236, 22849.218558653396], [20457.137910061505, 0.5469968185836116, 22544.477084886475]]
Faces: [[2, 3, 1], [3, 0, 1], [1, 4, 5], [2, 1, 5], [7, 5, 6], [5, 4, 6], [6, 0, 7], [0, 3, 7], [3, 2, 7], [7, 2, 5], [6, 4, 1], [0, 6, 1]]


Vertices: [[17736.23126780341, 2901.4753593419673, 22780.023931916312], [18694.431251570077, 2901.475351664289, 22786.423959681106], [18696.466715963248, 2896.1036329803596, 22481.67809584849], [17738.266732196586, 2896.103640658043, 22475.278068083695], [20455.03128455919, 5.875370265509883, 22849.223785602746], [20457.066748952362, 0.503651581581277, 22544.477921770136]]
Faces: [[3, 1, 2], [0, 1, 3], [1, 4, 2], [4, 5, 2], [3, 4, 0], [3, 5, 4], [3, 2, 5], [0, 4, 1]]


Vertices: [[17736.50949556136, 2901.736576776612, 22780.02057682472], [20455.30942843662, 6.136314895439643, 22849.220251703195], [20457.0904373139, 0.525761342222264, 22544.477098053758], [17738.290504438646, 2896.1260232233985, 22475.27742317528], [19497.109334775803, 6.136461474350874, 22843.620245025544], [19498.890343653078, 0.5259079211334949, 22538.877091376104]]
Faces: [[0, 1, 3], [1, 2, 3], [1, 4, 5], [1, 5, 2], [3, 4, 0], [3, 5, 4], [5, 3, 2], [4, 1, 0]]

Errors loading .ply files

Hi,

I've not been able to load .ply files with vertex colours, neither ASCII nor binary. These files open fine in Meshlab, Blender and 3D Builder.

Basically what I am trying to do is load and view a mesh with vertex colours. Is that not something that is supported in trimesh or in the viewer? If I load an obj with vertex colours, it just displays the shape, no colours. Converting to .ply files is even less successful:

I'm getting various errors, depending on whether I use binary or ASCII ply files:

mesh = trimesh.load_mesh(r"mesh_ascii.ply")
Traceback (most recent call last):
  File "C:\Program Files (x86)\JetBrains\PyCharm 2017.1\helpers\pydev\_pydevd_bundle\pydevd_exec2.py", line 3, in Exec
    exec(exp, global_vars, local_vars)
  File "<input>", line 1, in <module>
  File "C:\Users\Patrik\Envs\eos-py\lib\site-packages\trimesh\constants.py", line 125, in timed
    result = method(*args, **kwargs)
  File "C:\Users\Patrik\Envs\eos-py\lib\site-packages\trimesh\io\load.py", line 111, in load_mesh
    file_type)
  File "C:\Users\Patrik\Envs\eos-py\lib\site-packages\trimesh\io\ply.py", line 35, in load_ply
    ply_ascii(elements, file_obj)
  File "C:\Users\Patrik\Envs\eos-py\lib\site-packages\trimesh\io\ply.py", line 225, in ply_ascii
    count].reshape((-1, rows)).astype(dtype)[:, 1:]
ValueError: invalid literal for int() with base 10: '0.188808'

Seems like on this one it expects integer colours, but they're given as float's?

Same mesh loading as binary ply:

Traceback (most recent call last):
  File "C:\Program Files (x86)\JetBrains\PyCharm 2017.1\helpers\pydev\_pydevd_bundle\pydevd_exec2.py", line 3, in Exec
    exec(exp, global_vars, local_vars)
  File "<input>", line 1, in <module>
  File "C:\Users\Patrik\Envs\eos-py\lib\site-packages\trimesh\constants.py", line 125, in timed
    result = method(*args, **kwargs)
  File "C:\Users\Patrik\Envs\eos-py\lib\site-packages\trimesh\io\load.py", line 111, in load_mesh
    file_type)
  File "C:\Users\Patrik\Envs\eos-py\lib\site-packages\trimesh\io\ply.py", line 37, in load_ply
    ply_binary(elements, file_obj)
  File "C:\Users\Patrik\Envs\eos-py\lib\site-packages\trimesh\io\ply.py", line 304, in ply_binary
    populate_listsize(file_obj, elements)
  File "C:\Users\Patrik\Envs\eos-py\lib\site-packages\trimesh\io\ply.py", line 267, in populate_listsize
    offset = np.dtype(prior_data).itemsize()
TypeError: 'int' object is not callable

I was getting various other errors as well with this mesh (after converting it from .obj to .ply with Meshlab) and then trying to load it with trimesh.

Are vertex colours not supported by trimesh?

I'm using Win 10, a virtualenv with python-3.6.1 (64-bit), numpy+mkl, scipy and Shapely from here, and pip install trimesh[easy].

error in oriented bounding box

Thanks for this nice work.

I've used it in several models, but for one object, I got an error when I try to get the oriented bounding box:
Error in `python': free(): invalid pointer:

data:
'Faces': [[0, 1, 2],
[0, 2, 3],
[5, 2, 4],
[4, 2, 1],
[4, 6, 5],
[6, 7, 5],
[6, 0, 3],
[7, 6, 3],
[2, 5, 3],
[3, 5, 7],
[4, 1, 0],
[4, 0, 6]],
'Vertices': [[-1.8189894000000002e-12, 152.4, 0.0],
[-8.1854523e-11, 152.4, 2438.4],
[4572.0, 152.4, 2438.4],
[4572.0, 152.4, 0.0],
[185.32131, -152.4, 2438.4],
[4572.0, -152.4, 2438.4],
[185.32131, -152.4, 0.0],
[4572.0, -152.4, 0.0]],

Outdated docstring

In trimesh/ray/ray_triangle.py, function intersects_location.

rays parameter is not used anymore, but ray_origins and ray_directions

Rezeroing some meshes causes mesh to become non-watertight

Weird example here.

I have a particular mesh that is watertight pre-rezero, and non-watertight post-rezero.

I'm not sure why this would occur! It seems that other meshes I try do not have this problem.

Here's the code:

import trimesh
import numpy as np

def main():
  m = trimesh.load_mesh("rezero-test.stl")
  watertight_rezero_test(m)

def watertight_rezero_test(m):
  print(m.is_watertight)

  m.rezero()

  print(m.is_watertight)


main()

The .stl file is here: https://drive.google.com/open?id=0B18jNduc8kjlQXROOVNCMlRENFU

Calling super with self.__class__ in SceneViewer.flip()

Hey,

You're calling super with self.class in trimesh.scene.viewer.SceneViewer.flip() rather than SceneViewer. This means if I inherit from SceneViewer I'm forced to override flip to avoid the infinite recursion when it is called (as self.class is always going to be my inherited class).

Your package is generally awesome and you have called super correctly elsewhere within file so I'm sure its just an oversight.

Cheers,
Rob

Ray.Intersection and Mesh distance form an origin

Dear Mike,

Thank you for the prompt reply on my previous question. I have another question which might again be pyembree related. You commented briefly a potential issue in the ray_pyembree.py module:

# based on an internal tolerance of embree?
# 1e-4 definetly doesn't work
_ray_offset_distance = .01

The problem I am facing is following. By creating a mesh with a large coordinates values and offsetting a ray origin from the mesh surface by 0.001 or 0.01 the ray.intersection_any reports intersection (True) despite the ray origin is offset. This does not happen in all cases. When move the same mesh close to the Cartesian origin (0,0,0) there is no intersection; which is correct. I created a simple example which shows this misbehaviour. The mesh has a single rectangle composed of two triangles. Here is the code:

import numpy as np
import trimesh


def unit_normal_vector(p1, p2, p3):
    x, y, z = np.cross((p2 - p1), (p3 - p1))
    magnitude = np.sqrt(np.square(x) + np.square(y) + np.square(z))
    unit_normal_vector = list(np.array([x, y, z]) / magnitude)
    return unit_normal_vector

# Mesh first vertice 528980.85 183526.1 0
mesh = trimesh.load_mesh('./test_mesh.obj')

# Extract coordinates and determine a plane's unit normal vector
p1o, p2o, p3o = mesh.vertices[0:3]
unv = unit_normal_vector(p1o, p2o, p3o)

# Centroid of a rectangle's bottom edge elevated by 1m from the bottom.
cen = np.mean([p1o, p3o], axis=0)
cen[2] = 1

# Ray origin offset from the mesh rectangle by 0.01 along the unit normal vector
ep = cen + 0.01 * np.array(unv)

# Intersection returns True
intersection = mesh.ray.intersects_any([ep], [unv])
print(intersection)

# Mesh moved towards Cartesian origin (First vertice 80.85 26.1 0)
mesh_moved = trimesh.load_mesh('./test_mesh_moved.obj')

# Extract coordinates and determine a plane's unit normal vector
p1m, p2m, p3m = mesh_moved.vertices[0:3]
unv_moved = unit_normal_vector(p1m, p2m, p3m)

# Centroid of a rectangle's bottom edge elevated by 1m from the bottom.
cen_moved = np.mean([p1m, p3m], axis=0)
cen_moved[2] = 1

# Ray origin offset from the mesh rectangle by 0.001 along the unit normal vector
ep_moved = cen_moved + 0.001 * np.array(unv_moved)

# Intersection returns False (as it should be)
intersection_moved = mesh_moved.ray.intersects_any([ep_moved], [unv_moved])
print(intersection_moved)

test_mesh.obj

v 528980.85 183526.1 0
v 528980.85 183526.1 3
v 528982.3 183523.55 0
v 528982.3 183523.55 3
f 2 1 3
f 2 3 4

test_mesh_moved.obj

v 80.85 26.1 0
v 80.85 26.1 3
v 82.3 23.55 0
v 82.3 23.55 3
f 2 1 3
f 2 3 4

Best regards,
Ivan
p.s. Thank you very much for your effort in developing the trimesh library. It is a great library which I've been using a lot recently.

Extrude a Path2D?

Hi Mike,

Thanks again for the excellent package :)

I was trying to extrude a 2D path today, so I did extruded = section2d.extrude(height=1), which returned 2 primitives. However, when I tried to export the primitives using extruded[0].export('test.stl'), an error shows up saying ValueError: extrude polygon not specified!. The complete code is below:

type(section2d)
Out[13]: trimesh.path.path.Path2D

extruded = section2d.extrude(height=1)

extruded[0].export('test.stl')
Traceback (most recent call last):

  File "<ipython-input-15-4ddd38bd4376>", line 1, in <module>
    extruded[0].export('test.stl')

  File "C:\Anaconda3\lib\site-packages\trimesh\base.py", line 1244, in export
    file_type = file_type)

  File "C:\Anaconda3\lib\site-packages\trimesh\io\export.py", line 31, in export_mesh
    log.info('Exporting %d faces as %s', len(mesh.faces), file_type.upper())

  File "C:\Anaconda3\lib\site-packages\trimesh\primitives.py", line 26, in faces
    self._create_mesh()

  File "C:\Anaconda3\lib\site-packages\trimesh\primitives.py", line 268, in _create_mesh
    mesh = creation.extrude_polygon(self.extrude_polygon,

  File "C:\Anaconda3\lib\site-packages\trimesh\primitives.py", line 244, in extrude_polygon
    raise ValueError('extrude polygon not specified!')

ValueError: extrude polygon not specified!

I do have meshpy installed. Any thoughts?

Thanks!

Shawn

PS: My end goal is to output a Path2D into an STL file with a single surface, so I was thinking about extrude with height=0. I'd really appreciate it if you think there is a less a hack-ish way as well!

Blender CSG

Not an issue, just a question:

Is there any plan to implement CSG using blender instead of OpenSCAD? I've outsourced the CSG I've been doing to Blender, and it's been performing better for me.

I'm not sure if the speedup is just due to the opening time of OpenSCAD, or the actual speed of the CSG operation within it.

Scad and blender interfaces don't work

I am trying to perform boolean operations, however none of the interfaces seem to work properly. I have blender and openscad installed in my system but I get these errors:

SCAD:

> Traceback (most recent call last):
>   File "/home/tomas/Synology/Documents/Python/VPPy/VPPy-3/LPP.py", line 619, in <module>
>     d = geo.difference(sea, engine=engine)
>   File "/usr/local/lib/python2.7/dist-packages/trimesh/base.py", line 1029, in difference
>     engine = engine))
>   File "/usr/local/lib/python2.7/dist-packages/trimesh/boolean.py", line 19, in difference
>     result = _engines[engine](meshes, operation='difference')
>   File "/usr/local/lib/python2.7/dist-packages/trimesh/interfaces/scad.py", line 32, in boolean
>     return interface_scad(meshes, script)
>   File "/usr/local/lib/python2.7/dist-packages/trimesh/interfaces/scad.py", line 21, in interface_scad
>     result = scad.run('openscad $script -o $mesh_post')
>   File "/usr/local/lib/python2.7/dist-packages/trimesh/interfaces/generic.py", line 39, in run
>     check_call(command_run)
>   File "/usr/lib/python2.7/subprocess.py", line 506, in check_call
>     retcode = call(*popenargs, **kwargs)
>   File "/usr/lib/python2.7/subprocess.py", line 493, in call
>     return Popen(*popenargs, **kwargs).wait()
>   File "/usr/lib/python2.7/subprocess.py", line 679, in __init__
>     errread, errwrite)
>   File "/usr/lib/python2.7/subprocess.py", line 1249, in _execute_child
>     raise child_exception
> OSError: [Errno 2] No such file or directory

BLENDER:

> AL lib: pulseaudio.c:331: PulseAudio returned minreq > tlength/2; expect break up
> AL lib: pulseaudio.c:331: PulseAudio returned minreq > tlength/2; expect break up
> Traceback (most recent call last):
>   File "/tmp/tmpZC05uT", line 25, in <module>
>     use_mesh_modifiers = True)
>   File "/usr/lib/blender/scripts/modules/bpy/ops.py", line 180, in __call__
>     ret = op_call(self.idname_py(), None, kw)
> TypeError: Converting py args to operator properties: : keyword "use_mesh_modifiers" unrecognized
> 
> Blender quit

Add functions for searching mesh surfaces

It would be useful to be able to find from a given point (specifying an optional search direction):

  • Nearest vertex
  • Nearest face
  • Nearest face normal vector
  • Nearest triangle center

There all seem easy enough to write up, but interpolation onto the surface would be maybe even more useful. Let me know what you think and I may try to work up a PR.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.