Skip to content

Commit

Permalink
Merge pull request #2278 from mikedh/fix/scene
Browse files Browse the repository at this point in the history
Release: Scene Transform
  • Loading branch information
mikedh authored Sep 4, 2024
2 parents f7df971 + 21a92ea commit 634c608
Show file tree
Hide file tree
Showing 24 changed files with 551 additions and 134 deletions.
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,5 @@ jobs:
tag_name: ${{ steps.set_tag.outputs.tag_name }}
release_name: Release ${{ steps.set_tag.outputs.tag_name }}
draft: false
body: ${{ github.event.head_commit.message }}
prerelease: false
14 changes: 3 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ RUN python3.12 -m venv venv

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

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

USER root
# install wget for fetching wheels
RUN apt-get update && \
apt-get install --no-install-recommends -qq -y wget ca-certificates && \
apt-get clean -y
USER user

# copy in essential files
COPY --chown=499 trimesh/ /home/user/trimesh
COPY --chown=499 pyproject.toml /home/user/

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

# install FCL, which currently has broken wheels on pypi
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 && \
pip install python_fcl*.whl && \
rm python_fcl*.whl
# install FCL which currently has broken wheels on PyPi
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

####################################
### Build output image most things should run on
Expand Down
4 changes: 2 additions & 2 deletions docs/content/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ When you remove the embed and see the profile result you can then tweak the line
### Automatic Formatting
Trimesh uses `ruff` for both linting and formatting which is configured in `pyproject.toml`, you can run with:
```
ruff . --fix
ruff format .
ruff check --fix
ruff format
```

## Docstrings
Expand Down
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.8"
version = "4.4.8"
version = "4.4.9"
authors = [{name = "Michael Dawson-Haggerty", email = "[email protected]"}]
license = {file = "LICENSE.md"}
description = "Import, export, process, analyze and view triangular meshes."
Expand Down
10 changes: 10 additions & 0 deletions tests/test_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ def test_ray_index(self):
assert all(rid.min(axis=0) == 0)
assert all(rid.max(axis=0) == current - 1)

def test_scaled_copy(self):
s = g.get_mesh("cycloidal.3DXML")

s.units = "mm"
assert s.camera_transform.shape == (4, 4)

# the camera node should have been removed on copy
b = s.convert_units("m")
assert b.camera_transform.shape == (4, 4)


if __name__ == "__main__":
g.trimesh.util.attach_to_log()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_gltf.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ def test_load_empty_nodes(self):
def test_same_name(self):
s = g.get_mesh("TestScene.gltf")
# hardcode correct bounds to check against
bounds = s.dump(concatenate=True).bounds
bounds = s.to_mesh().bounds

# icosahedrons have two primitives each
g.log.debug(len(s.geometry), len(s.graph.nodes_geometry))
Expand Down
2 changes: 1 addition & 1 deletion tests/test_inertia.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ def test_scene(self):
s._cache.clear()

with g.Profiler() as P:
ms = s.dump(concatenate=True)
ms = s.to_mesh()
total_dump = ms.moment_inertia
g.log.debug(P.output_text())

Expand Down
12 changes: 6 additions & 6 deletions tests/test_scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ def test_exact_bounds(self):
m = g.get_mesh("cycloidal.3DXML")
assert isinstance(m, g.trimesh.Scene)

dump = m.dump(concatenate=True)
dump = m.to_mesh()
assert isinstance(dump, g.trimesh.Trimesh)

# scene bounds should exactly match mesh bounds
Expand All @@ -475,7 +475,7 @@ def test_concatenate_mixed(self):
]
)

dump = scene.dump(concatenate=True)
dump = scene.to_mesh()
assert isinstance(dump, g.trimesh.Trimesh)

def test_append_scenes(self):
Expand All @@ -493,7 +493,7 @@ def test_scene_concat(self):
a = g.trimesh.Scene(
[g.trimesh.primitives.Sphere(center=[5, 5, 5]), g.trimesh.primitives.Box()]
)
c = a.dump(concatenate=True)
c = a.to_mesh()
assert isinstance(c, g.trimesh.Trimesh)
assert g.np.allclose(c.bounds, a.bounds)

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

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

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

concat = scene_3D.dump(concatenate=True)
concat = scene_3D.to_geometry()
assert isinstance(concat, g.trimesh.path.Path3D)

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

concat = scene_mixed.dump(concatenate=True)
concat = scene_mixed.to_geometry()
assert isinstance(concat, g.trimesh.path.Path3D)


Expand Down
32 changes: 31 additions & 1 deletion tests/test_scenegraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def test_scene_transform(self):
# copy the original bounds of the scene's convex hull
b = scene.convex_hull.bounds.tolist()
# dump it into a single mesh
m = scene.dump(concatenate=True)
m = scene.to_mesh()

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

def test_simplify(self):
if not g.trimesh.util.has_module("fast_simplification"):
return

# get a scene graph
scene: g.trimesh.Scene = g.get_mesh("cycloidal.3DXML")

original = scene.to_mesh()

scene.simplify_quadric_decimation(percent=0.0, aggression=0)
assert len(scene.to_mesh().vertices) < len(original.vertices)

def test_reverse(self):
tf = g.trimesh.transformations

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

def test_reconstruct(self):
original = g.get_mesh("cycloidal.3DXML")
assert isinstance(original, g.trimesh.Scene)

# get the scene as "baked" meshes with no scene graph
dupe = g.trimesh.Scene(original.dump())
assert len(dupe.geometry) > len(original.geometry)

with g.Profiler() as P:
# reconstruct the instancing using `duplicate_nodes` and `procrustes`
rec = dupe.reconstruct_instances()
g.log.info(P.output_text())

assert len(rec.graph.nodes_geometry) == len(original.graph.nodes_geometry)
assert len(rec.geometry) == len(original.geometry)
assert g.np.allclose(rec.extents, original.extents, rtol=1e-8)
assert g.np.allclose(rec.center_mass, original.center_mass, rtol=1e-8)


if __name__ == "__main__":
g.trimesh.util.attach_to_log()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_voxel.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def test_as_boxes(self):
voxel = g.trimesh.voxel

pitch = 0.1
origin = (0, 0, 1)
origin = [0, 0, 1]

matrix = g.np.eye(9, dtype=bool).reshape((-1, 3, 3))
centers = g.trimesh.voxel.ops.matrix_to_points(
Expand Down
11 changes: 9 additions & 2 deletions trimesh/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# avoid a circular import in trimesh.base
from . import (
boolean,
bounds,
caching,
collision,
comparison,
Expand All @@ -24,6 +25,7 @@
grouping,
inertia,
intersections,
nsphere,
permutate,
poses,
primitives,
Expand All @@ -45,7 +47,13 @@
from .constants import tol

# loader functions
from .exchange.load import available_formats, load, load_mesh, load_path, load_remote
from .exchange.load import (
available_formats,
load,
load_mesh,
load_path,
load_remote,
)

# geometry objects
from .parent import Geometry
Expand Down Expand Up @@ -118,6 +126,5 @@
"unitize",
"units",
"util",
"utilScene",
"voxel",
]
6 changes: 3 additions & 3 deletions trimesh/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1112,9 +1112,9 @@ def merge_vertices(
self,
merge_tex: Optional[bool] = None,
merge_norm: Optional[bool] = None,
digits_vertex: Optional[bool] = None,
digits_norm: Optional[bool] = None,
digits_uv: Optional[bool] = None,
digits_vertex: Optional[Integer] = None,
digits_norm: Optional[Integer] = None,
digits_uv: Optional[Integer] = None,
) -> None:
"""
Removes duplicate vertices grouped by position and
Expand Down
3 changes: 3 additions & 0 deletions trimesh/bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ def oriented_bounds_2D(points, qhull_options="QbB"):
"""
Find an oriented bounding box for an array of 2D points.
Details on qhull options:
http://www.qhull.org/html/qh-quick.htm#options
Parameters
----------
points : (n,2) float
Expand Down
Loading

0 comments on commit 634c608

Please sign in to comment.