Skip to content

Commit 634c608

Browse files
authored
Merge pull request #2278 from mikedh/fix/scene
Release: Scene Transform
2 parents f7df971 + 21a92ea commit 634c608

File tree

24 files changed

+551
-134
lines changed

24 files changed

+551
-134
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,5 @@ jobs:
143143
tag_name: ${{ steps.set_tag.outputs.tag_name }}
144144
release_name: Release ${{ steps.set_tag.outputs.tag_name }}
145145
draft: false
146+
body: ${{ github.event.head_commit.message }}
146147
prerelease: false

Dockerfile

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ RUN python3.12 -m venv venv
2121

2222
# So scripts installed from pip are in $PATH
2323
ENV PATH="/home/user/venv/bin:$PATH"
24+
ENV VIRTUAL_ENV="/home/user/venv"
2425

2526
# Install helper script to PATH.
2627
COPY --chmod=755 docker/trimesh-setup /home/user/venv/bin
@@ -29,24 +30,15 @@ COPY --chmod=755 docker/trimesh-setup /home/user/venv/bin
2930
## install things that need building
3031
FROM base AS build
3132

32-
USER root
33-
# install wget for fetching wheels
34-
RUN apt-get update && \
35-
apt-get install --no-install-recommends -qq -y wget ca-certificates && \
36-
apt-get clean -y
37-
USER user
38-
3933
# copy in essential files
4034
COPY --chown=499 trimesh/ /home/user/trimesh
4135
COPY --chown=499 pyproject.toml /home/user/
4236

4337
# install trimesh into the venv
4438
RUN pip install /home/user[easy]
4539

46-
# install FCL, which currently has broken wheels on pypi
47-
RUN wget https://github.com/BerkeleyAutomation/python-fcl/releases/download/v0.7.0.7/python_fcl-0.7.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl && \
48-
pip install python_fcl*.whl && \
49-
rm python_fcl*.whl
40+
# install FCL which currently has broken wheels on PyPi
41+
RUN pip install https://github.com/BerkeleyAutomation/python-fcl/releases/download/v0.7.0.7/python_fcl-0.7.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
5042

5143
####################################
5244
### Build output image most things should run on

docs/content/contributing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ When you remove the embed and see the profile result you can then tweak the line
7373
### Automatic Formatting
7474
Trimesh uses `ruff` for both linting and formatting which is configured in `pyproject.toml`, you can run with:
7575
```
76-
ruff . --fix
77-
ruff format .
76+
ruff check --fix
77+
ruff format
7878
```
7979

8080
## Docstrings

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ requires = ["setuptools >= 61.0", "wheel"]
55
[project]
66
name = "trimesh"
77
requires-python = ">=3.8"
8-
version = "4.4.8"
8+
version = "4.4.9"
99
authors = [{name = "Michael Dawson-Haggerty", email = "[email protected]"}]
1010
license = {file = "LICENSE.md"}
1111
description = "Import, export, process, analyze and view triangular meshes."

tests/test_camera.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@ def test_ray_index(self):
116116
assert all(rid.min(axis=0) == 0)
117117
assert all(rid.max(axis=0) == current - 1)
118118

119+
def test_scaled_copy(self):
120+
s = g.get_mesh("cycloidal.3DXML")
121+
122+
s.units = "mm"
123+
assert s.camera_transform.shape == (4, 4)
124+
125+
# the camera node should have been removed on copy
126+
b = s.convert_units("m")
127+
assert b.camera_transform.shape == (4, 4)
128+
119129

120130
if __name__ == "__main__":
121131
g.trimesh.util.attach_to_log()

tests/test_gltf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ def test_load_empty_nodes(self):
743743
def test_same_name(self):
744744
s = g.get_mesh("TestScene.gltf")
745745
# hardcode correct bounds to check against
746-
bounds = s.dump(concatenate=True).bounds
746+
bounds = s.to_mesh().bounds
747747

748748
# icosahedrons have two primitives each
749749
g.log.debug(len(s.geometry), len(s.graph.nodes_geometry))

tests/test_inertia.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ def test_scene(self):
402402
s._cache.clear()
403403

404404
with g.Profiler() as P:
405-
ms = s.dump(concatenate=True)
405+
ms = s.to_mesh()
406406
total_dump = ms.moment_inertia
407407
g.log.debug(P.output_text())
408408

tests/test_scene.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ def test_exact_bounds(self):
461461
m = g.get_mesh("cycloidal.3DXML")
462462
assert isinstance(m, g.trimesh.Scene)
463463

464-
dump = m.dump(concatenate=True)
464+
dump = m.to_mesh()
465465
assert isinstance(dump, g.trimesh.Trimesh)
466466

467467
# scene bounds should exactly match mesh bounds
@@ -475,7 +475,7 @@ def test_concatenate_mixed(self):
475475
]
476476
)
477477

478-
dump = scene.dump(concatenate=True)
478+
dump = scene.to_mesh()
479479
assert isinstance(dump, g.trimesh.Trimesh)
480480

481481
def test_append_scenes(self):
@@ -493,7 +493,7 @@ def test_scene_concat(self):
493493
a = g.trimesh.Scene(
494494
[g.trimesh.primitives.Sphere(center=[5, 5, 5]), g.trimesh.primitives.Box()]
495495
)
496-
c = a.dump(concatenate=True)
496+
c = a.to_mesh()
497497
assert isinstance(c, g.trimesh.Trimesh)
498498
assert g.np.allclose(c.bounds, a.bounds)
499499

@@ -502,7 +502,7 @@ def test_scene_concat(self):
502502

503503
# scene 2D
504504
scene_2D = g.trimesh.Scene(g.get_mesh("2D/250_cycloidal.DXF").split())
505-
concat = scene_2D.dump(concatenate=True)
505+
concat = scene_2D.to_geometry()
506506
assert isinstance(concat, g.trimesh.path.Path2D)
507507

508508
dump = scene_2D.dump(concatenate=False)
@@ -518,7 +518,7 @@ def test_scene_concat(self):
518518
assert len(dump) >= 5
519519
assert all(isinstance(i, g.trimesh.path.Path3D) for i in dump)
520520

521-
concat = scene_3D.dump(concatenate=True)
521+
concat = scene_3D.to_geometry()
522522
assert isinstance(concat, g.trimesh.path.Path3D)
523523

524524
mixed = list(scene_2D.geometry.values())
@@ -528,7 +528,7 @@ def test_scene_concat(self):
528528
dump = scene_mixed.dump(concatenate=False)
529529
assert len(dump) == len(mixed)
530530

531-
concat = scene_mixed.dump(concatenate=True)
531+
concat = scene_mixed.to_geometry()
532532
assert isinstance(concat, g.trimesh.path.Path3D)
533533

534534

tests/test_scenegraph.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def test_scene_transform(self):
142142
# copy the original bounds of the scene's convex hull
143143
b = scene.convex_hull.bounds.tolist()
144144
# dump it into a single mesh
145-
m = scene.dump(concatenate=True)
145+
m = scene.to_mesh()
146146

147147
# mesh bounds should match exactly
148148
assert g.np.allclose(m.bounds, b)
@@ -160,6 +160,18 @@ def test_scene_transform(self):
160160
# should have moved from original position
161161
assert not g.np.allclose(m.convex_hull.bounds, b)
162162

163+
def test_simplify(self):
164+
if not g.trimesh.util.has_module("fast_simplification"):
165+
return
166+
167+
# get a scene graph
168+
scene: g.trimesh.Scene = g.get_mesh("cycloidal.3DXML")
169+
170+
original = scene.to_mesh()
171+
172+
scene.simplify_quadric_decimation(percent=0.0, aggression=0)
173+
assert len(scene.to_mesh().vertices) < len(original.vertices)
174+
163175
def test_reverse(self):
164176
tf = g.trimesh.transformations
165177

@@ -298,6 +310,24 @@ def test_translation_origin(self):
298310
s.apply_translation(-s.bounds[0])
299311
assert g.np.allclose(s.bounds[0], 0)
300312

313+
def test_reconstruct(self):
314+
original = g.get_mesh("cycloidal.3DXML")
315+
assert isinstance(original, g.trimesh.Scene)
316+
317+
# get the scene as "baked" meshes with no scene graph
318+
dupe = g.trimesh.Scene(original.dump())
319+
assert len(dupe.geometry) > len(original.geometry)
320+
321+
with g.Profiler() as P:
322+
# reconstruct the instancing using `duplicate_nodes` and `procrustes`
323+
rec = dupe.reconstruct_instances()
324+
g.log.info(P.output_text())
325+
326+
assert len(rec.graph.nodes_geometry) == len(original.graph.nodes_geometry)
327+
assert len(rec.geometry) == len(original.geometry)
328+
assert g.np.allclose(rec.extents, original.extents, rtol=1e-8)
329+
assert g.np.allclose(rec.center_mass, original.center_mass, rtol=1e-8)
330+
301331

302332
if __name__ == "__main__":
303333
g.trimesh.util.attach_to_log()

tests/test_voxel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def test_as_boxes(self):
163163
voxel = g.trimesh.voxel
164164

165165
pitch = 0.1
166-
origin = (0, 0, 1)
166+
origin = [0, 0, 1]
167167

168168
matrix = g.np.eye(9, dtype=bool).reshape((-1, 3, 3))
169169
centers = g.trimesh.voxel.ops.matrix_to_points(

0 commit comments

Comments
 (0)