Skip to content

Commit

Permalink
Merge pull request #2220 from mikedh/feat/np2
Browse files Browse the repository at this point in the history
Release: Numpy 2.0 Compatibility
  • Loading branch information
mikedh committed May 17, 2024
2 parents e7568c6 + aea9ab9 commit 5ee5c91
Show file tree
Hide file tree
Showing 66 changed files with 400 additions and 192 deletions.
10 changes: 9 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ USER user
# install trimesh into .local
# then delete any included test directories
# and remove Cython after all the building is complete


# TODO
# remove mapbox-earcut fork when this is merged:
# https://github.com/skogler/mapbox_earcut_python/pull/15
RUN pip install --user /home/user[easy] && \
pip install --user --force-reinstall git+https://github.com/mikedh/mapbox_earcut_python.git && \
find /home/user/.local -type d -name tests -prune -exec rm -rf {} \;

####################################
Expand Down Expand Up @@ -67,7 +73,9 @@ RUN trimesh-setup --install=test,gmsh,gltf_validator,llvmpipe,binvox
USER user

# install things like pytest
RUN pip install -e .[all]
# install prerelease for tests and make sure we're on Numpy 2.X
RUN pip install --pre --upgrade .[all] && \
python -c "import numpy as n; assert(n.__version__.startswith('1'))"

# check for lint problems
RUN ruff check trimesh
Expand Down
3 changes: 2 additions & 1 deletion docker/trimesh-setup
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ config_json = """
"build": [
"build-essential",
"g++",
"make"
"make",
"git"
],
"docs": [
"make",
Expand Down
10 changes: 5 additions & 5 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ recommonmark==0.7.1
jupyter==1.0.0

# get sphinx version range from furo install
furo==2024.1.29
myst-parser==2.0.0
furo==2024.5.6
myst-parser==3.0.1
pyopenssl==24.1.0
autodocsumm==0.2.12
jinja2==3.1.3
matplotlib==3.8.3
nbconvert==7.16.2
jinja2==3.1.4
matplotlib==3.8.4
nbconvert==7.16.4

2 changes: 1 addition & 1 deletion examples/raytrace.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
a = np.zeros(scene.camera.resolution, dtype=np.uint8)

# scale depth against range (0.0 - 1.0)
depth_float = (depth - depth.min()) / depth.ptp()
depth_float = (depth - depth.min()) / np.ptp(depth)

# convert depth into 0 - 255 uint8
depth_int = (depth_float * 255).round().astype(np.uint8)
Expand Down
Binary file added models/Mesh_PrimitiveMode_04.bin
Binary file not shown.
71 changes: 71 additions & 0 deletions models/Mesh_PrimitiveMode_04.gltf
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 4,
"type": "VEC3",
"max": [
0.5,
0.5,
0.0
],
"min": [
-0.5,
-0.5,
0.0
],
"name": "Positions Accessor"
}
],
"asset": {
"generator": "glTF Asset Generator",
"version": "2.0"
},
"buffers": [
{
"uri": "Mesh_PrimitiveMode_04.bin",
"byteLength": 48
}
],
"bufferViews": [
{
"buffer": 0,
"byteLength": 48,
"name": "Positions"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"metallicFactor": 0.0
}
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"POSITION": 0
},
"material": 0,
"mode": 5
}
]
}
],
"nodes": [
{
"mesh": 0
}
],
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
]
}
Binary file added models/no_indices_3storybuilding.glb
Binary file not shown.
10 changes: 6 additions & 4 deletions 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.3.2"
version = "4.4.0"
authors = [{name = "Michael Dawson-Haggerty", email = "mikedh@kerfed.com"}]
license = {file = "LICENSE.md"}
description = "Import, export, process, analyze and view triangular meshes."
Expand Down Expand Up @@ -102,18 +102,20 @@ test = [
"coveralls",
"pyright",
"ezdxf",
"gmsh>=4.12.1",
"pytest",
"pymeshlab",
#"pymeshlab",
"pyinstrument",
"matplotlib",
"ruff",
"pytest-beartype; python_version>='3.10'"
]

# interfaces.gmsh will be dropped Jan 2025
deprecated = ["gmsh==4.12.2"]

# requires pip >= 21.2
# https://hynek.me/articles/python-recursive-optional-dependencies/
all = ["trimesh[easy,recommend,test]"]
all = ["trimesh[easy,recommend,test,deprecated]"]

[tool.ruff]
target-version = "py37"
Expand Down
6 changes: 3 additions & 3 deletions tests/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def output_text(*args, **kwargs):
include_rendering = "INCLUDE_RENDERING" in argv

if all_dependencies and not trimesh.ray.has_embree:
raise ValueError("missing embree!")
import embreex

try:
import sympy as sp
Expand Down Expand Up @@ -512,7 +512,7 @@ def check_fuze(fuze):
# UV coordinates should be unmerged correctly
assert len(fuze.visual.uv) == 664
# UV coordinates shouldn't be all zero- ish
assert fuze.visual.uv[:, :2].ptp(axis=0).min() > 0.1
assert np.ptp(fuze.visual.uv[:, :2], axis=0).min() > 0.1
# UV coordinates should be between zero and 1
assert fuze.visual.uv.min() > -tol.merge
assert fuze.visual.uv.max() < 1 + tol.merge
Expand All @@ -538,7 +538,7 @@ def check_fuze(fuze):
viz = fuze.visual.to_color()
assert viz.kind == "vertex"
# should be actual colors defined
assert viz.vertex_colors.ptp(axis=0).ptp() != 0
assert np.ptp(np.ptp(viz.vertex_colors, axis=0)) != 0
# shouldn't crash
fuze.visual.copy()
fuze.visual.material.copy()
Expand Down
8 changes: 4 additions & 4 deletions tests/test_bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def test_2D(self):
points = g.random((10, 2)) * [5, 1]

# save the basic AABB of the points before rotation
rectangle_pre = points.ptp(axis=0)
rectangle_pre = g.np.ptp(points, axis=0)

# rotate them by an increment
TR = g.trimesh.transformations.planar_matrix(theta=theta)
Expand All @@ -129,10 +129,10 @@ def test_2D(self):
# apply the calculated OBB
oriented = g.trimesh.transform_points(points, T)

origin = oriented.min(axis=0) + oriented.ptp(axis=0) / 2.0
origin = oriented.min(axis=0) + g.np.ptp(oriented, axis=0) / 2.0

# check to make sure the returned rectangle size is right
assert g.np.allclose(oriented.ptp(axis=0), rectangle)
assert g.np.allclose(g.np.ptp(oriented, axis=0), rectangle)
# check to make sure the OBB consistently returns the
# long axis in the same direction
assert rectangle[0] > rectangle[1]
Expand Down Expand Up @@ -199,7 +199,7 @@ def test_bounding_egg(self):
r = p.bounding_cylinder

# transformed height should match source mesh
assert g.np.isclose(i.vertices[:, 2].ptp(), r.primitive.height, rtol=1e-6)
assert g.np.isclose(g.np.ptp(i.vertices[:, 2]), r.primitive.height, rtol=1e-6)
# slightly inflated cylinder should contain all
# vertices of the source mesh
assert r.buffer(0.01).contains(p.vertices).all()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_lookat(self):
T = g.trimesh.scene.cameras.look_at(points + offset, fov)

# check using trig
check = (points.ptp(axis=0)[:2] / 2.0) / g.np.tan(np.radians(fov / 2))
check = (np.ptp(points, axis=0)[:2] / 2.0) / g.np.tan(np.radians(fov / 2))
check += points[:, 2].mean()

# Z should be the same as maximum trig option
Expand Down
10 changes: 5 additions & 5 deletions tests/test_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_concatenate(self):

a.visual.face_colors = [255, 0, 0]
r = a + b
assert any(r.visual.face_colors.ptp(axis=0) > 1)
assert any(g.np.ptp(r.visual.face_colors, axis=0) > 1)

def test_concatenate_empty_mesh(self):
box = g.get_mesh("box.STL")
Expand Down Expand Up @@ -153,23 +153,23 @@ def test_smooth(self):
# will put smoothed mesh into visuals cache
s = m.smooth_shaded
# every color should be default color
assert s.visual.face_colors.ptp(axis=0).max() == 0
assert g.np.ptp(s.visual.face_colors, axis=0).max() == 0

# set one face to a different color
m.visual.face_colors[0] = [255, 0, 0, 255]

# cache should be dumped yo
s1 = m.smooth_shaded
assert s1.visual.face_colors.ptp(axis=0).max() != 0
assert g.np.ptp(s1.visual.face_colors, axis=0).max() != 0

# do the same check on vertex color
m = g.get_mesh("featuretype.STL")
s = m.smooth_shaded
# every color should be default color
assert s.visual.vertex_colors.ptp(axis=0).max() == 0
assert g.np.ptp(s.visual.vertex_colors, axis=0).max() == 0
m.visual.vertex_colors[g.np.arange(10)] = [255, 0, 0, 255]
s1 = m.smooth_shaded
assert s1.visual.face_colors.ptp(axis=0).max() != 0
assert g.np.ptp(s1.visual.face_colors, axis=0).max() != 0

def test_vertex(self):
m = g.get_mesh("torus.STL")
Expand Down
2 changes: 1 addition & 1 deletion tests/test_dae.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def test_obj_roundtrip(self):
s.export(path)
# bring it back from outer space
rec = g.trimesh.load(path, force="mesh")
assert rec.visual.uv.ptp(axis=0).ptp() > 1e-5
assert g.np.ptp(g.np.ptp(rec.visual.uv, axis=0)) > 1e-5
assert s.visual.material.baseColorTexture.size == rec.visual.material.image.size

conv = s.convert_units("inch")
Expand Down
2 changes: 1 addition & 1 deletion tests/test_dxf.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def test_versions(self):
# count the number of entities in the path
# this should be the same for every version
E = g.np.array([len(paths[i].entities) for i in group], dtype=g.np.int64)
assert E.ptp() == 0
assert g.np.ptp(E) == 0

def test_bulge(self):
"""
Expand Down
12 changes: 11 additions & 1 deletion tests/test_gltf.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ def test_vertex_colors(self):
)
# original mesh should have vertex colors
assert m.visual.kind == "face"
assert m.visual.vertex_colors.ptp(axis=0).ptp() > 0
assert g.np.ptp(g.np.ptp(m.visual.vertex_colors, axis=0)) > 0
# vertex colors should have survived import-export
assert g.np.allclose(m.visual.vertex_colors, r.visual.vertex_colors)

Expand Down Expand Up @@ -1058,6 +1058,16 @@ def test_unitize_normals_null_values(self):
# Check that the normals are still null
assert g.np.allclose(reimported_mesh.vertex_normals[0], [0, 0, 0])

def test_no_indices(self):
# test mesh with no indices (faces should be generated correctly)
mesh = g.get_mesh("no_indices_3storybuilding.glb")
assert len(mesh.triangles) == 72

# the mesh is actually mode 5 with 4 vertices
# which as triangle strips would be 2 faces
mesh = g.get_mesh("Mesh_PrimitiveMode_04.gltf")
assert len(mesh.triangles) == 2


if __name__ == "__main__":
g.trimesh.util.attach_to_log()
Expand Down
6 changes: 3 additions & 3 deletions tests/test_grouping.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,11 @@ def test_group_rows(self):

gr = g.trimesh.grouping.group_rows(c)
assert g.np.shape(gr) == (100, 2)
assert g.np.allclose(c[gr].ptp(axis=1), 0.0)
assert g.np.allclose(g.np.ptp(c[gr], axis=1), 0.0)

gr = g.trimesh.grouping.group_rows(c, require_count=2)
assert gr.shape == (100, 2)
assert g.np.allclose(c[gr].ptp(axis=1), 0.0)
assert g.np.allclose(g.np.ptp(c[gr], axis=1), 0.0)

c = g.np.vstack((c, [1, 2, 3]))
gr = g.trimesh.grouping.group_rows(c, require_count=2)
Expand All @@ -229,7 +229,7 @@ def test_group_rows(self):
# should get the single element correctly
assert len(grd) == 101
assert sum(1 for i in grd if len(i) == 2) == 100
assert g.np.allclose(c[gr].ptp(axis=1), 0.0)
assert g.np.allclose(g.np.ptp(c[gr], axis=1), 0.0)

def test_group_vector(self):
x = g.np.linspace(-100, 100, 100)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_medial.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_medial(self):
# with midpoint at origin
assert len(med.vertices) == 2
assert len(med.entities) == 1
assert float(med.vertices.mean(axis=0).ptp()) < 1e-8
assert float(g.np.ptp(med.vertices.mean(axis=0))) < 1e-8


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion tests/test_minimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def test_path_exc(self):
bounds, inserted = packing.rectangles_single([[1, 1], [2, 2]], size=[2, 4])
assert inserted.all()

extents = bounds.reshape((-1, 2)).ptp(axis=0)
extents = np.ptp(bounds.reshape((-1, 2)), axis=0)
assert np.allclose(extents, [2, 3])
assert bounds.shape == (2, 2, 2)
density = 5.0 / np.prod(extents)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_no_img(self):
assert m.visual.uv.max() < (1 + 1e-5)
assert m.visual.uv.min() > -1e-5
# check to make sure it's not all zeros
assert m.visual.uv.ptp() > 0.5
assert g.np.ptp(m.visual.uv) > 0.5
rec = g.roundtrip(m.export(file_type="obj"), file_type="obj")
assert g.np.isclose(m.area, rec.area)

Expand All @@ -48,7 +48,7 @@ def test_obj_groups(self):
# assert len(mesh.metadata['face_groups']) == len(mesh.faces)

# check to make sure there is signal not just zeros
# assert mesh.metadata['face_groups'].ptp() > 0
# assert g.np.ptp(mesh.metadata['face_groups']) > 0

def test_obj_negative_indices(self):
# a wavefront file with negative indices
Expand Down
Loading

0 comments on commit 5ee5c91

Please sign in to comment.