Skip to content

Commit

Permalink
Merge pull request #2126 from mikedh/release/euler
Browse files Browse the repository at this point in the history
Release: Case STL, Symbolic Euler Angles
  • Loading branch information
mikedh authored Jan 26, 2024
2 parents 80a6c1f + b69411f commit 90a7ac5
Show file tree
Hide file tree
Showing 14 changed files with 432 additions and 112 deletions.
7 changes: 7 additions & 0 deletions examples/stl_import_with_mixed_case_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import trimesh

# Load stl file with mixed case name
mesh = trimesh.load_mesh("models/two_objects_mixed_case_names.stl")
# Print some geometry info to test
for k, v in mesh.geometry.items():
print("Sub-mesh Name:", k, "\nSub-mesh Min X value", min(v.vertices[:, 0]))
172 changes: 172 additions & 0 deletions models/two_objects_mixed_case_names.stl
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
solid CubeExportedFromCAD
facet normal -1.000000e+00 -4.336808e-16 -0.000000e+00
outer loop
vertex 4.336809e-16 0.000000e+00 1.000000e+00
vertex 0.000000e+00 1.000000e+00 1.000000e+00
vertex 4.336809e-16 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal -1.000000e+00 -4.336808e-16 0.000000e+00
outer loop
vertex 4.336809e-16 0.000000e+00 0.000000e+00
vertex 0.000000e+00 1.000000e+00 1.000000e+00
vertex 0.000000e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal -0.000000e+00 -1.000000e+00 -0.000000e+00
outer loop
vertex 1.000000e+00 0.000000e+00 1.000000e+00
vertex 4.336809e-16 0.000000e+00 1.000000e+00
vertex 1.000000e+00 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
outer loop
vertex 1.000000e+00 0.000000e+00 0.000000e+00
vertex 4.336809e-16 0.000000e+00 1.000000e+00
vertex 4.336809e-16 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal 1.000000e+00 0.000000e+00 0.000000e+00
outer loop
vertex 1.000000e+00 1.000000e+00 1.000000e+00
vertex 1.000000e+00 0.000000e+00 1.000000e+00
vertex 1.000000e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal 1.000000e+00 0.000000e+00 0.000000e+00
outer loop
vertex 1.000000e+00 1.000000e+00 0.000000e+00
vertex 1.000000e+00 0.000000e+00 1.000000e+00
vertex 1.000000e+00 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal -0.000000e+00 1.000000e+00 0.000000e+00
outer loop
vertex 0.000000e+00 1.000000e+00 1.000000e+00
vertex 1.000000e+00 1.000000e+00 1.000000e+00
vertex 0.000000e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
outer loop
vertex 0.000000e+00 1.000000e+00 0.000000e+00
vertex 1.000000e+00 1.000000e+00 1.000000e+00
vertex 1.000000e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 -0.000000e+00 1.000000e+00
outer loop
vertex 1.000000e+00 0.000000e+00 1.000000e+00
vertex 1.000000e+00 1.000000e+00 1.000000e+00
vertex 4.336809e-16 0.000000e+00 1.000000e+00
endloop
endfacet
facet normal 0.000000e+00 -0.000000e+00 1.000000e+00
outer loop
vertex 4.336809e-16 0.000000e+00 1.000000e+00
vertex 1.000000e+00 1.000000e+00 1.000000e+00
vertex 0.000000e+00 1.000000e+00 1.000000e+00
endloop
endfacet
facet normal -0.000000e+00 -0.000000e+00 -1.000000e+00
outer loop
vertex 1.000000e+00 1.000000e+00 0.000000e+00
vertex 1.000000e+00 0.000000e+00 0.000000e+00
vertex 0.000000e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 0.000000e+00 -1.000000e+00
outer loop
vertex 0.000000e+00 1.000000e+00 0.000000e+00
vertex 1.000000e+00 0.000000e+00 0.000000e+00
vertex 4.336809e-16 0.000000e+00 0.000000e+00
endloop
endfacet
endsolid
solid TranslatedCubeExportedFromCAD
facet normal -1.000000e+00 -4.336808e-16 -0.000000e+00
outer loop
vertex 5.0e+00 0.000000e+00 1.000000e+00
vertex 5.0e+00 1.000000e+00 1.000000e+00
vertex 5.0e+00 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal -1.000000e+00 -4.336808e-16 0.000000e+00
outer loop
vertex 5.0e+00 0.000000e+00 0.000000e+00
vertex 5.0e+00 1.000000e+00 1.000000e+00
vertex 5.0e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal -0.000000e+00 -1.000000e+00 -0.000000e+00
outer loop
vertex 6.000000e+00 0.000000e+00 1.000000e+00
vertex 5.0e+00 0.000000e+00 1.000000e+00
vertex 6.000000e+00 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
outer loop
vertex 6.000000e+00 0.000000e+00 0.000000e+00
vertex 5.0e+00 0.000000e+00 1.000000e+00
vertex 5.0e+00 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal 1.000000e+00 0.000000e+00 0.000000e+00
outer loop
vertex 6.000000e+00 1.000000e+00 1.000000e+00
vertex 6.000000e+00 0.000000e+00 1.000000e+00
vertex 6.000000e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal 1.000000e+00 0.000000e+00 0.000000e+00
outer loop
vertex 6.000000e+00 1.000000e+00 0.000000e+00
vertex 6.000000e+00 0.000000e+00 1.000000e+00
vertex 6.000000e+00 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal -0.000000e+00 1.000000e+00 0.000000e+00
outer loop
vertex 5.000000e+00 1.000000e+00 1.000000e+00
vertex 6.000000e+00 1.000000e+00 1.000000e+00
vertex 5.000000e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
outer loop
vertex 5.000000e+00 1.000000e+00 0.000000e+00
vertex 6.000000e+00 1.000000e+00 1.000000e+00
vertex 6.000000e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 -0.000000e+00 1.000000e+00
outer loop
vertex 6.000000e+00 0.000000e+00 1.000000e+00
vertex 6.000000e+00 1.000000e+00 1.000000e+00
vertex 5.0e+00 0.000000e+00 1.000000e+00
endloop
endfacet
facet normal 0.000000e+00 -0.000000e+00 1.000000e+00
outer loop
vertex 5.0e+00 0.000000e+00 1.000000e+00
vertex 6.000000e+00 1.000000e+00 1.000000e+00
vertex 5.000000e+00 1.000000e+00 1.000000e+00
endloop
endfacet
facet normal -0.000000e+00 -0.000000e+00 -1.000000e+00
outer loop
vertex 6.000000e+00 1.000000e+00 0.000000e+00
vertex 6.000000e+00 0.000000e+00 0.000000e+00
vertex 5.000000e+00 1.000000e+00 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 0.000000e+00 -1.000000e+00
outer loop
vertex 5.000000e+00 1.000000e+00 0.000000e+00
vertex 6.000000e+00 0.000000e+00 0.000000e+00
vertex 5.0e+00 0.000000e+00 0.000000e+00
endloop
endfacet
endsolid
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ requires = ["setuptools >= 61.0", "wheel"]
[project]
name = "trimesh"
requires-python = ">=3.7"
version = "4.0.10"
version = "4.1.0"
authors = [{name = "Michael Dawson-Haggerty", email = "[email protected]"}]
license = {file = "LICENSE.md"}
description = "Import, export, process, analyze and view triangular meshes."
Expand Down
28 changes: 28 additions & 0 deletions tests/test_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,34 @@ def test_concatenate(self):
r = a + b
assert any(r.visual.face_colors.ptp(axis=0) > 1)

def test_concatenate_empty_mesh(self):
box = g.get_mesh("box.STL")

mesh_fcolor = box.copy()
mesh_fcolor.visual.face_colors = [255, 0, 0]

mesh_vcolor = box.copy()
mesh_vcolor.visual.vertex_colors = [0, 0, 255]

mesh_empty = g.trimesh.Trimesh()

r_left_fcolor = mesh_fcolor + mesh_empty
r_right_fcolor = mesh_empty + mesh_fcolor
r_left_vcolor = mesh_vcolor + mesh_empty
r_right_vcolor = mesh_empty + mesh_vcolor
r_empty = mesh_empty + mesh_empty

for visual_face in [r_left_fcolor.visual, r_right_fcolor.visual]:
assert (visual_face.face_colors == mesh_fcolor.visual.face_colors).all()
assert visual_face.kind == "face"

for visual_vert in [r_left_vcolor.visual, r_right_vcolor.visual]:
assert (visual_vert.vertex_colors == mesh_vcolor.visual.vertex_colors).all()
assert visual_vert.kind == "vertex"

assert len(r_empty.visual.face_colors) == 0
assert r_empty.visual.kind is None

def test_data_model(self):
"""
Test the probably too- magical color caching and storage
Expand Down
13 changes: 13 additions & 0 deletions tests/test_gltf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,19 @@ def test_relative_paths(self):
r = g.trimesh.load(path)
assert g.np.isclose(r.volume, 1.0)

def test_postprocess(self):
# check to see if keys we expect exist
s = g.get_mesh("cycloidal.3DXML")

def post(tree):
# should have exported meshes here
assert len(tree["meshes"]) == len(s.geometry)
# should have buffers
assert len(tree["buffers"]) >= 1

# export with a postprocessor
s.export(file_type="glb", tree_postprocessor=post)


if __name__ == "__main__":
g.trimesh.util.attach_to_log()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_stl.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def test_attrib(self):
def test_ascii_multibody(self):
s = g.get_mesh("multibody.stl")
assert len(s.geometry) == 2
assert set(s.geometry.keys()) == {"bodya", "bodyb"}
assert set(s.geometry.keys()) == {"bodyA", "bodyB"}

def test_empty(self):
# demo files to check
Expand Down
43 changes: 43 additions & 0 deletions tests/test_transformations.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,49 @@ def test_angle(self):
0.0,
)

def test_symbolic_rotation(self):
# you can pass `sympy.Symbol` to `trimesh.transformation.rotation_matrix`
try:
import sympy as sp
except BaseException:
return
tf = g.trimesh.transformations

a = sp.Symbol("a")
vector = [1, 1, 1]
m = tf.rotation_matrix(a, vector)
for v in [0.0, 1.1, 1.234, g.np.pi]:
# evaluate the symbolic matrix with a value
s = g.np.array(m.subs({a: v}).evalf(), dtype=g.np.float64)
# call rotation matrix with a scalar
n = tf.rotation_matrix(v, vector)

# they should be the same matrix
assert g.np.allclose(s, n)

def test_symbolic_euler(self):
# some of the functions have been modified to support `sympy.Symbol`
# values which is useful for calculating final rotations symbolically
try:
import sympy as sp
except BaseException:
return

euler = g.trimesh.transformations.euler_matrix

ra, rb, rc = sp.symbols("ra rb rc")
m = euler(ra, rb, rc)
for rot in g.random((100, 3)):
# get the euler matrix evaluated from the symbolic matrix
s = g.np.array(
m.subs({ra: rot[0], rb: rot[1], rc: rot[2]}).evalf(), dtype=g.np.float64
)
# get it from a numeric scalar
n = euler(*rot)

# they should be the same matrix
assert g.np.allclose(s, n)


if __name__ == "__main__":
g.trimesh.util.attach_to_log()
Expand Down
4 changes: 2 additions & 2 deletions trimesh/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ def vertex_faces(self) -> NDArray[int64]:
return vertex_faces

@caching.cache_decorator
def bounds(self) -> NDArray[float64]:
def bounds(self) -> Optional[NDArray[float64]]:
"""
The axis aligned bounds of the faces of the mesh.
Expand All @@ -547,7 +547,7 @@ def bounds(self) -> NDArray[float64]:
return np.array([in_mesh.min(axis=0), in_mesh.max(axis=0)])

@caching.cache_decorator
def extents(self) -> NDArray[float64]:
def extents(self) -> Optional[NDArray[float64]]:
"""
The length, width, and height of the axis aligned
bounding box of the mesh.
Expand Down
8 changes: 4 additions & 4 deletions trimesh/exchange/gltf.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,6 @@ def export_glb(
extension_webp=extension_webp,
)

# allow custom postprocessing
if tree_postprocessor is not None:
tree_postprocessor(tree)

# A bufferView is a slice of a file
views = _build_views(buffer_items)

Expand All @@ -218,6 +214,10 @@ def export_glb(
tree["buffers"] = [{"byteLength": len(buffer_data)}]
tree["bufferViews"] = views

# allow custom postprocessing
if tree_postprocessor is not None:
tree_postprocessor(tree)

# export the tree to JSON for the header
content = util.jsonify(tree, separators=(",", ":"))
# add spaces to content, so the start of the data
Expand Down
Loading

0 comments on commit 90a7ac5

Please sign in to comment.