trimesh
This commit is contained in:
parent
7a8506e34c
commit
8ba3ea16c4
6 changed files with 287 additions and 72 deletions
|
|
@ -62,6 +62,13 @@ wheels = [
|
|||
"./wheels/shapely_polyskel-0.1.2-py3-none-any.whl",
|
||||
"./wheels/euclid3-0.1-py3-none-any.whl",
|
||||
"./wheels/pyransac3d-0.6.0-py3-none-any.whl",
|
||||
"./wheels/trimesh-5.1.0-py3-none-any.whl",
|
||||
"./wheels/manifold3d-3.5.4-cp311-cp311-win_amd64.whl",
|
||||
"./wheels/manifold3d-3.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl",
|
||||
"./wheels/manifold3d-3.5.4-cp311-cp311-macosx_11_0_arm64.whl",
|
||||
"./wheels/manifold3d-3.5.4-cp313-cp313-win_amd64.whl",
|
||||
"./wheels/manifold3d-3.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl",
|
||||
"./wheels/manifold3d-3.5.4-cp313-cp313-macosx_11_0_arm64.whl",
|
||||
]
|
||||
|
||||
# Optional: add-ons can list which resources they will require:
|
||||
|
|
|
|||
175
core/slice.py
175
core/slice.py
|
|
@ -6,6 +6,8 @@ import pyclipper
|
|||
from typing import Any, Literal, cast
|
||||
import copy
|
||||
from shapely.geometry import Polygon, MultiPolygon, LineString, LinearRing, MultiLineString, GeometryCollection, Point
|
||||
import manifold3d
|
||||
import trimesh
|
||||
|
||||
from ..infra.blender.blender_mesh_capture import PdfProperties
|
||||
|
||||
|
|
@ -655,81 +657,116 @@ class SlicePaths(list[np.ndarray]):
|
|||
|
||||
return self.copy_with(new_paths, extrusion_rate=new_ext, feedrate=new_feed, extra_attributes=new_extra)
|
||||
|
||||
def slice_at_range(models, z_values, ignore_holes: bool = False) -> list[list[np.ndarray]]:
|
||||
def _slice_single_mesh(mesh_array: np.ndarray, z_values: np.ndarray | list[float], ignore_holes: bool = False) -> list[np.ndarray]:
|
||||
"""
|
||||
Slices a single 3D triangle mesh (shape (F, 4, 3) or (F, 3, 3)) across multiple Z heights.
|
||||
Uses manifold3d for robust solid slicing with exact coplanar/topological resolution,
|
||||
and falls back to trimesh for open/non-manifold surface meshes.
|
||||
"""
|
||||
if len(mesh_array) == 0:
|
||||
return []
|
||||
|
||||
z_arr = np.asarray(z_values, dtype=np.float64)
|
||||
if len(z_arr) == 0:
|
||||
return []
|
||||
|
||||
tris = mesh_array[:, :3, :] # Shape (F, 3, 3)
|
||||
flat = np.ascontiguousarray(tris.reshape(-1, 3), dtype=np.float32)
|
||||
void_dtype = np.dtype((np.void, flat.dtype.itemsize * flat.shape[1]))
|
||||
flat_void = flat.view(void_dtype).ravel()
|
||||
unique_voids, inv = np.unique(flat_void, return_inverse=True)
|
||||
unique_verts = unique_voids.view(flat.dtype).reshape(-1, 3)
|
||||
faces = inv.reshape(-1, 3).astype(np.uint32)
|
||||
|
||||
all_paths: list[np.ndarray] = []
|
||||
|
||||
# 1. Primary: manifold3d for solid meshes (fastest, guaranteed watertight topological output)
|
||||
try:
|
||||
mesh_m = manifold3d.Mesh(
|
||||
vert_properties=np.ascontiguousarray(unique_verts),
|
||||
tri_verts=np.ascontiguousarray(faces)
|
||||
)
|
||||
m = manifold3d.Manifold(mesh_m)
|
||||
# If mesh has inverted normals (negative volume), flip triangle winding
|
||||
if m.status() == manifold3d.Error.NoError and m.volume() < 0:
|
||||
faces_flipped = faces[:, [0, 2, 1]]
|
||||
mesh_m_flipped = manifold3d.Mesh(
|
||||
vert_properties=np.ascontiguousarray(unique_verts),
|
||||
tri_verts=np.ascontiguousarray(faces_flipped)
|
||||
)
|
||||
m = manifold3d.Manifold(mesh_m_flipped)
|
||||
|
||||
if m.status() == manifold3d.Error.NoError and not m.is_empty():
|
||||
min_z = float(np.min(unique_verts[:, 2]))
|
||||
max_z = float(np.max(unique_verts[:, 2]))
|
||||
|
||||
for z in z_arr:
|
||||
zf = float(z)
|
||||
if zf < min_z - 1e-4 or zf > max_z + 1e-4:
|
||||
continue
|
||||
# If slicing at boundary face, apply micro-epsilon so boundary is cleanly captured
|
||||
slice_z = zf - 1e-5 if abs(zf - max_z) < 1e-5 else (zf + 1e-5 if abs(zf - min_z) < 1e-5 else zf)
|
||||
cs = m.slice(slice_z)
|
||||
polys = cs.to_polygons()
|
||||
for poly in polys:
|
||||
if len(poly) < 3:
|
||||
continue
|
||||
pts_scaled = (poly * SCALE).round().astype(np.int64).tolist()
|
||||
if ignore_holes and not pyclipper.Orientation(pts_scaled):
|
||||
continue
|
||||
p3d = np.column_stack([poly.astype(np.float64), np.full(len(poly), zf, dtype=np.float64)])
|
||||
p3d = np.vstack([p3d, p3d[0]])
|
||||
p3d = align_path_seam_sharpest(p3d)
|
||||
all_paths.append(p3d)
|
||||
return all_paths
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. Fallback: trimesh multiplane slicing for open sheets / non-manifold meshes
|
||||
try:
|
||||
min_z = float(np.min(unique_verts[:, 2]))
|
||||
max_z = float(np.max(unique_verts[:, 2]))
|
||||
slice_heights = np.where(
|
||||
np.abs(z_arr - max_z) < 1e-5,
|
||||
z_arr - 1e-5,
|
||||
np.where(np.abs(z_arr - min_z) < 1e-5, z_arr + 1e-5, z_arr)
|
||||
)
|
||||
tm = trimesh.Trimesh(vertices=unique_verts, faces=faces, process=False)
|
||||
lines, _, _ = trimesh.intersections.mesh_multiplane(
|
||||
mesh=tm,
|
||||
plane_origin=[0.0, 0.0, 0.0],
|
||||
plane_normal=[0.0, 0.0, 1.0],
|
||||
heights=slice_heights
|
||||
)
|
||||
for z, line_segs in zip(z_arr, lines):
|
||||
if line_segs is None or len(line_segs) == 0:
|
||||
continue
|
||||
zf = float(z)
|
||||
seg2d = np.asarray(line_segs, dtype=np.float64)
|
||||
z_col = np.full((len(seg2d), 2, 1), zf, dtype=np.float64)
|
||||
seg3d = np.concatenate([seg2d, z_col], axis=-1)
|
||||
slice_paths = segments_to_slice(seg3d, ignore_holes=ignore_holes)
|
||||
all_paths.extend(slice_paths)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return all_paths
|
||||
|
||||
|
||||
def slice_at_range(models: list[np.ndarray], z_values: np.ndarray | list[float], ignore_holes: bool = False) -> list[list[np.ndarray]]:
|
||||
"""
|
||||
Top-level slicing function that slices multiple model meshes across given Z heights.
|
||||
Returns a list of path lists (one per model).
|
||||
"""
|
||||
if not models:
|
||||
return []
|
||||
|
||||
mmps: list[list[np.ndarray]] = []
|
||||
|
||||
for model in models:
|
||||
model_edges: np.ndarray = edges_from_model(model)
|
||||
layered_edges: np.ndarray = layered_model(model_edges, z_values)
|
||||
sliced_edges: list[np.ndarray] = slice_at_z(layered_edges)
|
||||
|
||||
all_model_paths: list[np.ndarray] = []
|
||||
|
||||
for z, e in zip(z_values, sliced_edges):
|
||||
if e is None or len(e) == 0:
|
||||
continue
|
||||
|
||||
zf = float(z)
|
||||
|
||||
# Build 3D segments directly
|
||||
seg3d = e.copy()
|
||||
seg3d[..., 2] = zf
|
||||
|
||||
# Build geometry for this slice
|
||||
slice_paths = segments_to_slice(seg3d, ignore_holes=ignore_holes)
|
||||
all_model_paths.extend(slice_paths)
|
||||
|
||||
mmps.append(all_model_paths)
|
||||
model_paths = _slice_single_mesh(model, z_values, ignore_holes=ignore_holes)
|
||||
mmps.append(model_paths)
|
||||
return mmps
|
||||
|
||||
def edges_from_model(model): # returns a numpy: (facet, 3, 2, xyz)
|
||||
pairs_indices = np.array([[0, 1], [1, 2], [2, 0]]) # Create an array of indices to select the pairs
|
||||
return model[:, pairs_indices, :] # Use advanced indexing to create the array
|
||||
|
||||
def layered_model(models: np.ndarray, z_values: np.ndarray) -> np.ndarray:
|
||||
models_array: np.ndarray = np.array(models)[None, :] # (1, F, 3, 2, 3)
|
||||
m = len(z_values)
|
||||
# array of zeros with shape (m, 1, 1, 1, 3)
|
||||
z_offsets_full = np.zeros((m, 1, 1, 1, 3))
|
||||
z_offsets_full[..., 2] = z_values[:, None, None, None]
|
||||
|
||||
layered = models_array - z_offsets_full # (m, F, 3, 2, 3)
|
||||
return layered
|
||||
|
||||
def slice_at_z(edges, normal_vector=np.array([0,0,1]), tolerance=1e-15):
|
||||
edges_offs = edges # Shape (m, n, 3, 2, 3)
|
||||
|
||||
# Obtain intersection points
|
||||
line_vector = edges_offs[..., 1, :] - edges_offs[..., 0, :]
|
||||
|
||||
numerator = np.sum(normal_vector * -edges_offs[..., 0, :], axis=-1)
|
||||
denominator = np.sum(normal_vector[np.newaxis, np.newaxis, :] * line_vector, axis=-1)
|
||||
|
||||
# Avoid division by zero
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
t = numerator / denominator
|
||||
t[denominator == 0] = np.nan # Assign NaN where there's no valid intersection
|
||||
t[(t < 0) | (t > 1)] = np.nan
|
||||
|
||||
intersections = edges_offs[..., 0, :] + t[..., np.newaxis] * line_vector
|
||||
intersections[np.abs(intersections) < tolerance] = 0 # Zero out small values
|
||||
|
||||
all_segments = []
|
||||
for itx in intersections:
|
||||
mask = ~np.any(np.isnan(itx), axis=-1)
|
||||
filtered_arr = itx[mask]
|
||||
|
||||
segment_count = filtered_arr.shape[-2] // 2
|
||||
segments = filtered_arr.reshape(segment_count, 2, 3)
|
||||
# Quantize to micron resolution
|
||||
segments = np.round(segments * SCALE) / SCALE
|
||||
all_segments.append(segments)
|
||||
|
||||
return all_segments
|
||||
|
||||
def link_segments(segments: np.ndarray) -> list[np.ndarray]:
|
||||
"""Links segments (N, 2, 3) into continuous paths using 1-micron integer grid."""
|
||||
if len(segments) == 0: return []
|
||||
|
|
@ -825,6 +862,8 @@ def segments_to_slice(segments: np.ndarray, ignore_holes: bool = False) -> list[
|
|||
path_pts = p[:, :2]
|
||||
path_pts = path_pts[:-1]
|
||||
scaled = (path_pts * SCALE).round().astype(np.int64).tolist()
|
||||
if abs(pyclipper.Area(scaled)) < 1:
|
||||
continue
|
||||
pc.AddPath(scaled, pyclipper.PT_SUBJECT, closed = True) # pyright: ignore[reportAttributeAccessIssue]
|
||||
|
||||
if ignore_holes:
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ This file provides a mapping of modules to their primary responsibilities and ke
|
|||
## `/core` (Geometry & Slicing)
|
||||
|
||||
- `slice.py`:
|
||||
- `slice_at_range()`: Top-level slicing function that iterates through Z-heights (supports `ignore_holes` option).
|
||||
- `slice_at_z()`: Vectorized plane-mesh intersection using NumPy.
|
||||
- `slice_at_range()`: Top-level slicing function that iterates through Z-heights using `manifold3d` for guaranteed watertight solid slicing (resolving coplanar and floating-point degeneracies) with a fallback to `trimesh` multiplane slicing for open sheets (supports `ignore_holes` option).
|
||||
- `_slice_single_mesh()`: Mesh slicer implementing `manifold3d` solid slicing and `trimesh` multiplane intersection with micro-epsilon perturbation at boundary faces.
|
||||
- `align_path_seam_sharpest()`: High-performance vectorized heuristic that positions the start vertex (seam) of closed polygon loops at the sharpest concave angle (inner corner), with fallback to sharpest angle overall.
|
||||
- `SlicePaths.align_seams()`: Method on `SlicePaths` to align path seams to sharpest concave angles while preserving per-vertex attributes and metadata.
|
||||
- `Pather`: Class that converts toolpaths into G-code strings. Supports per-segment extrusion rates, feedrates, and custom extra attributes.
|
||||
|
|
|
|||
|
|
@ -13,4 +13,6 @@ dependencies = [
|
|||
"shapely-polyskel>=0.1.2",
|
||||
"euclid3>=0.1",
|
||||
"pyransac3d>=0.6.0",
|
||||
"manifold3d>=3.5.4",
|
||||
"trimesh>=5.1.0",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1872,6 +1872,102 @@ class TestCodeReviewBugFixes(unittest.TestCase):
|
|||
gname = get_gcode_name_from_metadata(gc)
|
||||
self.assertEqual(gname, "BracketA-PLA-MK4")
|
||||
|
||||
def test_robust_mesh_slicing_coplanar_cube(self):
|
||||
from NodeSlicer.core.slice import slice_at_range, is_closed_path
|
||||
# 10x10x10 cube
|
||||
verts = np.array([
|
||||
[0, 0, 0], [10, 0, 0], [10, 10, 0], [0, 10, 0],
|
||||
[0, 0, 10], [10, 0, 10], [10, 10, 10], [0, 10, 10]
|
||||
], dtype=float)
|
||||
faces = [
|
||||
[0, 2, 1], [0, 3, 2], [4, 5, 6], [4, 6, 7],
|
||||
[0, 1, 5], [0, 5, 4], [1, 2, 6], [1, 6, 5],
|
||||
[2, 3, 7], [2, 7, 6], [3, 0, 4], [3, 4, 7]
|
||||
]
|
||||
triangles = np.zeros((len(faces), 4, 3))
|
||||
for i, f in enumerate(faces):
|
||||
triangles[i, 0] = verts[f[0]]
|
||||
triangles[i, 1] = verts[f[1]]
|
||||
triangles[i, 2] = verts[f[2]]
|
||||
|
||||
# Slice at flat bottom (z=0.0), middle (z=5.0), and flat top (z=10.0)
|
||||
res = slice_at_range([triangles], [0.0, 5.0, 10.0])
|
||||
self.assertEqual(len(res), 1)
|
||||
paths = res[0]
|
||||
self.assertEqual(len(paths), 3)
|
||||
for p in paths:
|
||||
self.assertTrue(is_closed_path(p))
|
||||
self.assertTrue(len(p) >= 4)
|
||||
|
||||
def test_robust_mesh_slicing_octahedron_coplanar_vertices_and_edges(self):
|
||||
from NodeSlicer.core.slice import slice_at_range, is_closed_path
|
||||
# Octahedron centered at origin with 4 vertices lying exactly at z=0.0
|
||||
verts = np.array([
|
||||
[5, 0, 0], [-5, 0, 0], [0, 5, 0], [0, -5, 0], [0, 0, 5], [0, 0, -5]
|
||||
], dtype=float)
|
||||
faces = [
|
||||
[0, 2, 4], [2, 1, 4], [1, 3, 4], [3, 0, 4],
|
||||
[2, 0, 5], [1, 2, 5], [3, 1, 5], [0, 3, 5],
|
||||
]
|
||||
triangles = np.zeros((len(faces), 4, 3))
|
||||
for i, f in enumerate(faces):
|
||||
triangles[i, 0] = verts[f[0]]
|
||||
triangles[i, 1] = verts[f[1]]
|
||||
triangles[i, 2] = verts[f[2]]
|
||||
|
||||
res = slice_at_range([triangles], [0.0])
|
||||
self.assertEqual(len(res), 1)
|
||||
paths = res[0]
|
||||
self.assertEqual(len(paths), 1)
|
||||
self.assertTrue(is_closed_path(paths[0]))
|
||||
|
||||
def test_robust_mesh_slicing_staircase_step(self):
|
||||
from NodeSlicer.core.slice import slice_at_range, is_closed_path
|
||||
# Stepped mesh with a plateau at z=5.0
|
||||
verts = np.array([
|
||||
[0, 0, 0], [10, 0, 0], [10, 10, 0], [0, 10, 0],
|
||||
[20, 0, 0], [20, 10, 0],
|
||||
[10, 0, 5], [20, 0, 5], [20, 10, 5], [10, 10, 5],
|
||||
[0, 0, 10], [10, 0, 10], [10, 10, 10], [0, 10, 10]
|
||||
], dtype=float)
|
||||
faces = [
|
||||
[0, 2, 1], [0, 3, 2], [1, 5, 4], [1, 2, 5],
|
||||
[10, 11, 12], [10, 12, 13],
|
||||
[6, 7, 8], [6, 8, 9],
|
||||
[0, 1, 6], [0, 6, 10], [10, 6, 11],
|
||||
[1, 4, 7], [1, 7, 6],
|
||||
[4, 5, 8], [4, 8, 7],
|
||||
[5, 2, 9], [5, 9, 8],
|
||||
[2, 3, 13], [2, 13, 9], [9, 13, 12],
|
||||
[3, 0, 10], [3, 10, 13],
|
||||
[11, 6, 9], [11, 9, 12]
|
||||
]
|
||||
triangles = np.zeros((len(faces), 4, 3))
|
||||
for i, f in enumerate(faces):
|
||||
triangles[i, 0] = verts[f[0]]
|
||||
triangles[i, 1] = verts[f[1]]
|
||||
triangles[i, 2] = verts[f[2]]
|
||||
|
||||
res = slice_at_range([triangles], [0.0, 2.5, 5.0, 7.5])
|
||||
self.assertEqual(len(res), 1)
|
||||
paths = res[0]
|
||||
self.assertEqual(len(paths), 4)
|
||||
for p in paths:
|
||||
self.assertTrue(is_closed_path(p))
|
||||
|
||||
def test_robust_mesh_slicing_open_sheet(self):
|
||||
from NodeSlicer.core.slice import slice_at_range
|
||||
# Open triangle in 3D
|
||||
open_tri = np.zeros((1, 4, 3))
|
||||
open_tri[0, 0] = [0, 0, 0]
|
||||
open_tri[0, 1] = [10, 0, 10]
|
||||
open_tri[0, 2] = [0, 10, 5]
|
||||
|
||||
res = slice_at_range([open_tri], [3.0])
|
||||
self.assertEqual(len(res), 1)
|
||||
self.assertEqual(len(res[0]), 1)
|
||||
self.assertEqual(len(res[0][0]), 2) # 1 line segment with 2 endpoints
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
|||
75
uv.lock
generated
75
uv.lock
generated
|
|
@ -2,7 +2,8 @@ version = 1
|
|||
revision = 3
|
||||
requires-python = ">=3.10"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.11'",
|
||||
"python_full_version >= '3.12'",
|
||||
"python_full_version == '3.11.*'",
|
||||
"python_full_version < '3.11'",
|
||||
]
|
||||
|
||||
|
|
@ -21,6 +22,58 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/77/73/2f8c20f53ee3a9692cab552d02c7178afd15e1b15bb335b5d90feb943bbe/fake_bpy_module-20260317-py3-none-any.whl", hash = "sha256:399ac3f8479c236a023f632733df6bbb40af8340cc3c3c05b41f3b63bb732127", size = 1013802, upload-time = "2026-03-17T06:53:22.489Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "manifold3d"
|
||||
version = "3.5.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f3/0f/1f7e51a8f9a6f25e837159c5bf7c95a3ae948aa12e1c5fdeee0fd90ca5f9/manifold3d-3.5.4.tar.gz", hash = "sha256:5bd88c482c6fd7fc01aa7b715b3e31c2585ee98343d5559d286ba30db0d0b6f0", size = 307695, upload-time = "2026-09-25T10:46:09.083Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/46/43/cd66885a0814c5c0288f6926d5a57cef817370f948a09e2ac532151b8b51/manifold3d-3.5.4-cp310-cp310-macosx_10_14_universal2.whl", hash = "sha256:7e43cdeeab20fd58bf3cb513de18ea04db194f4f64f6f1b4d8080590c4206389", size = 1829635, upload-time = "2026-09-25T10:45:07.23Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/5a/43625c7dbefed9d68985847fb4edef7005931779ffdbc56e8b986785948c/manifold3d-3.5.4-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:afe19a01f74b0223525ca3511b24cc119d8cbfa51a141fe2727464860fc54c5b", size = 991040, upload-time = "2026-09-25T10:45:08.821Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/b5/1e81f1937afb60e1bb2495dab78cc1ec784d8795e7a030b89114e22049ab/manifold3d-3.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0d8e25a2ea72ecba7c543c624e665c5c5078a38247e979f3c968b6480feebf97", size = 871023, upload-time = "2026-09-25T10:45:10.257Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/ab/e846ef088ca8183833d0249b3cc129c136b6a131c317576c1d33ec1ecbe1/manifold3d-3.5.4-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d4a0e3a982bdce4b97877164f6a1d5e2b1efc2a9144ffd7b6c3d0eb714ce850", size = 1303309, upload-time = "2026-09-25T10:45:11.709Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/97/1c7a5b11768c47f09073bc996b0fc87778fd8b4ca0c1cea5722b89532268/manifold3d-3.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b1eb3c3c2de01c048244fd9e8b21e45dc37043e5bc47106df35e564377ef85a9", size = 1411677, upload-time = "2026-09-25T10:45:13.405Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/3a/e85795e0bf86b72e5d46868cca46cd763ae5eeb926254101dc15a5fff487/manifold3d-3.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:06ee0b2ebec889e4ab447b1b4e9e845dd2e1ee05d02454e838fb63723848c579", size = 1022593, upload-time = "2026-09-25T10:45:15.198Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/c9/eae4830cc72c6cc1f8846795628cb4eea57bda450a138bc4d32597f669cf/manifold3d-3.5.4-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:1b948c0dab18195d1ea22e57cf89b13757c2b062e8f64d68ef55cd952a4f5a32", size = 1843741, upload-time = "2026-09-25T10:45:16.781Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/13/50980f98bbd90539bbe3efddba61819f077ea3a50aa24f8e5b4da227bad1/manifold3d-3.5.4-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:b4b2a9ed95c51b6dbb6f22dbc01ec7d696c10041fd1d29a7cc31ae75a33943d3", size = 1005422, upload-time = "2026-09-25T10:45:18.148Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/7f/4af914c508e3bc8532efeb5bef1767281e6605da82db9f2dac58ab90711f/manifold3d-3.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b9eedfdd50868b553e5bd5c83b6d99f8d2d70007cba3bf849ae90722a57ecfc", size = 885164, upload-time = "2026-09-25T10:45:19.393Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/a4/331345b949affcf279de06c1d98e9f944c2b2a0c1f3be098e051bd2849c5/manifold3d-3.5.4-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2498cc4c2e9b623e5b557d57ef91af9e5881dc1b592e720f74e004bc145a73c", size = 1317453, upload-time = "2026-09-25T10:45:20.886Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/f1/28ee6afc0deef7ad65cb1ee2f757d9c2d50518bd64b08bdfcf5e8e0ff278/manifold3d-3.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d021c4096aa0e40748c0720b8386002de349b788b3ad7d92a3dba214110d0f0e", size = 1426317, upload-time = "2026-09-25T10:45:22.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/42/8dc2e031840b8d2a8491df31ae0ea19d95e4da70d33711ff798055180f8f/manifold3d-3.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:34ef0b1b70b6d153eb12a72706e128e9b629c562cd4ab103083afd17ca071fbd", size = 1037097, upload-time = "2026-09-25T10:45:24.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/62/cc6328443b8122183760cc26ccb0b239f8531492c7d0ad1cedc5c15f41ad/manifold3d-3.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:df86fafec8268e6b164a7338b1dd9eb77139870ed5fa0e0d343a7d9870f3a833", size = 1146339, upload-time = "2026-09-29T05:41:53.512Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/f4/3bde89d5e37eb223ef7493b9ba782462dd8aea5af07d0370e6ce8a847186/manifold3d-3.5.4-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:d4b47d7b0aba1ba1b575e7d549be8c9ba2d552b03e4043e3022454fdaad13447", size = 1843283, upload-time = "2026-09-25T10:45:25.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/95/3b2e63c154d3b2e70cb8e52fc72a061eaf1bc82e826c955b324381162dc7/manifold3d-3.5.4-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:b1ae587cf7cfa489fb50bba84a0e048d9daadd105b6a047a312e45a7602163fa", size = 1005349, upload-time = "2026-09-25T10:45:27.09Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/28/6ec4f751be1b5868964eb92925da789c985efdaff5f0a5f85b47cbd87428/manifold3d-3.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d440e37d1e9888f90f42c70b4b3da98655be54105616e7739d84692bf7bda671", size = 884727, upload-time = "2026-09-25T10:45:28.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/f2/15ab84b2a80dcdc91ef53cde5fafdeb63cc3989646f17da7542011522b58/manifold3d-3.5.4-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f4e8ca1c59d68047b262dbdb6e917ac4abff2942a65e7e469f4a398ce1f0b9b", size = 1317923, upload-time = "2026-09-25T10:45:30.034Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/04/91323ab10f23d185931b572622589673ec561a8106fa1ae0f2fc876fb960/manifold3d-3.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a985892699268bbf07192b78b5e5569034c41ce74bf8183495123e8095af4fe", size = 1427177, upload-time = "2026-09-25T10:45:31.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/35/6b67f3af439f209b6f4ba653a5f3500f56464c99274fb4aa21323886c762/manifold3d-3.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:5be1025860df8a83ed3c4fd585d87376b075e23ea45e833578fe77eacc49bee1", size = 1036128, upload-time = "2026-09-25T10:45:32.819Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/55/aacf4b263a8bef710108cdcf1469156b53e44a63af574b435262b0e46820/manifold3d-3.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:559b6cd07976a6ab3edf9806e5f1757432c397589069e01a0045e78d326ff510", size = 1145058, upload-time = "2026-09-29T05:41:55.705Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/a7/12b1585a876eca4a1cf4985c45ce165a9c13583dd7b74f0c1f19cbf7c23e/manifold3d-3.5.4-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:f26933565a268f91e222a494570bebe494bde204277134b3e32e0d6ac686a9ad", size = 1843251, upload-time = "2026-09-25T10:45:34.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/3b/4f030178617d1309883a857318e2764ca9ddb1080e9d9be66f326ce30286/manifold3d-3.5.4-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:95b512975ae13d5ecc57a9d388cf84be4cc717add5173d7d5fa408e6d6a07677", size = 1005296, upload-time = "2026-09-25T10:45:35.763Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/55/6f434b0b4750565ac57dd5dcd3b2770b7de408988f0d9a044bf587863e7d/manifold3d-3.5.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed0736f472c3362b8084867f10a724a9a483927bdb7cbfceb564acc52a7f4273", size = 884711, upload-time = "2026-09-25T10:45:37.364Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/91/f5ccf45874cd667005107b654390e6379a2d970ed5fe6e5989bf24f4c7d6/manifold3d-3.5.4-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b46d2f19017fc2f3095d71cc111b8574e92afd5515d70529720324da5b1c0ed", size = 1317898, upload-time = "2026-09-25T10:45:38.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/9e/993cc841e4d88e2c8e6b1c5dfc52d4d0e8f78a46fbeaa98ff9bd539e3248/manifold3d-3.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a5469641713f1bc291fea873cb134c435704df36828d134190fc2475b304259", size = 1427121, upload-time = "2026-09-25T10:45:40.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/7a/316e60dea127721fcd523210c2e6c641ab2fa5f6c957eb8a83222f0d7ee4/manifold3d-3.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:76975aa4ebd18dcb0481e43dac3ed70d78066e9ad047e73a5f7dbc0df1553cf4", size = 1036021, upload-time = "2026-09-25T10:45:41.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/e7/a96928beac5287485cc0f61ac34c3b2ee42f57221d279a7d3bb176739a4d/manifold3d-3.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:552ffb1f79ef77f1c7e05d638ee43e7fe88c652b1f9b042a88aab1fef85fc28c", size = 1144955, upload-time = "2026-09-29T05:41:57.75Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/20/86f3ec8c2481a002b378aeb28341f613eb8c10a2dec6945be7c1a4db0647/manifold3d-3.5.4-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7c96a28ef7ed7a34e37cad775db95831051f03ca9e188f572b0a08b3561f4763", size = 1843034, upload-time = "2026-09-25T10:45:43.258Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/de/f57e7aebf0092bca0a6a3435d45105043a1f902cc89289264ab68823b07e/manifold3d-3.5.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:487b351a9b064353c9a8cb86b9f004ec0064511d121355a0fe738e96a5ca5306", size = 1004990, upload-time = "2026-09-25T10:45:44.55Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/83/6d168952066c22749fdc570a67ba580271057b3da9da299d9cac32e3b374/manifold3d-3.5.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3d6a81f79f4812dd9e88c8093588c31c6e863c410f61a274656f9cdff76b80c0", size = 884652, upload-time = "2026-09-25T10:45:46.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/cd/475da5e3d38586edd0c487e116a77896d28dfc0f90fdbf9bb1f1a64057c9/manifold3d-3.5.4-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7869f140b830faf93a76da3705d814b8e1652f3f68aab39351bfc6f05d93e01", size = 1319035, upload-time = "2026-09-25T10:45:47.572Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/9f/d91de303052c3b6d851c14cc60c4ff9aabcdb4ed79e6a6122fde5c668aa9/manifold3d-3.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c3fc18f0712b5ff10fdc5307f9abc8a40affe3a5f8559a19b9c03e3a82e4513b", size = 1427465, upload-time = "2026-09-25T10:45:48.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/a6/396f72fbcab489cb2678c066468fe2e33d7ce577351d11f948f0a7527d21/manifold3d-3.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:a04a30eb686e913e1872dce6f537947d64a01c5b4a882af0be47beef2309b074", size = 1068405, upload-time = "2026-09-25T10:45:50.573Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/e8/f6751f97187a8e7fddc446ac5d73f5000cf4dcc033dec5683fea3fe906ff/manifold3d-3.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:8c309c698837fdbca297ad7387f5348526e6635e49ce28476e2c9dcad30a947e", size = 1181481, upload-time = "2026-09-29T05:41:59.352Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/00/5f7651472bab7378cc57b6a15edba155d056542b4c0c117b743f5cdc8a44/manifold3d-3.5.4-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ee967a8f1b526194ea8a598e04d2fd6920a9a78e9d465a7b78e2a6827585b8f3", size = 1853599, upload-time = "2026-09-25T10:45:51.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/b4/63fa966d1138716a78c4e856c7846088f74a6797cc18346e6dbc8601f82f/manifold3d-3.5.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a55945fccc1ad20c958bedf61397a12ea5329f897a29f2237c70e12bdf7bd51d", size = 1011076, upload-time = "2026-09-25T10:45:53.499Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/2f/e200a5f4204d935435df2c6d58b35da8f2bfaaab61839365b2c1bac614e9/manifold3d-3.5.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5284a8a14fc86798d1698b37729e519516ba6c0574624ae16542032764adf189", size = 890394, upload-time = "2026-09-25T10:45:55.033Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/8d/3856eefc139ab1f18e928abcc4531515e75d4a43db31a6180583c33d90f0/manifold3d-3.5.4-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd68576b104f563a8d2a9629f7de565c5ac704f276394f9ffdb9f0c5a05c20d8", size = 1326941, upload-time = "2026-09-25T10:45:56.43Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/24/8c9d993e89cf758c3dbbd4470dcf3efc5efef11dcd8f89e208539fd11b21/manifold3d-3.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ae80814e5c6f85f4c53f632b71009fdfa185645a1d68c673d0d7a6171c29f78b", size = 1433600, upload-time = "2026-09-25T10:45:57.904Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/f0/fac6f8eaff4617eeab741c87bfae8a6afd1057648b0551f206c4247b0407/manifold3d-3.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:d034b1d74df1aad9377f30b2228c33b3887bf5057c7efd98a86892604edd86aa", size = 1070681, upload-time = "2026-09-25T10:45:59.412Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeslicer"
|
||||
version = "0.1.0"
|
||||
|
|
@ -28,6 +81,7 @@ source = { virtual = "." }
|
|||
dependencies = [
|
||||
{ name = "euclid3" },
|
||||
{ name = "fake-bpy-module" },
|
||||
{ name = "manifold3d" },
|
||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "pyclipper" },
|
||||
|
|
@ -35,18 +89,21 @@ dependencies = [
|
|||
{ name = "python-libnest2d" },
|
||||
{ name = "shapely" },
|
||||
{ name = "shapely-polyskel" },
|
||||
{ name = "trimesh" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "euclid3", specifier = ">=0.1" },
|
||||
{ name = "fake-bpy-module", specifier = ">=20260317" },
|
||||
{ name = "manifold3d", specifier = ">=3.5.4" },
|
||||
{ name = "numpy", specifier = ">=2.2.6" },
|
||||
{ name = "pyclipper", specifier = ">=1.4.0" },
|
||||
{ name = "pyransac3d", specifier = ">=0.6.0" },
|
||||
{ name = "python-libnest2d", specifier = ">=0.1.3" },
|
||||
{ name = "shapely", specifier = ">=2.1.2" },
|
||||
{ name = "shapely-polyskel", specifier = ">=0.1.2" },
|
||||
{ name = "trimesh", specifier = ">=5.1.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -119,7 +176,8 @@ name = "numpy"
|
|||
version = "2.4.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.11'",
|
||||
"python_full_version >= '3.12'",
|
||||
"python_full_version == '3.11.*'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/10/8b/c265f4823726ab832de836cdd184d0986dcf94480f81e8739692a7ac7af2/numpy-2.4.3.tar.gz", hash = "sha256:483a201202b73495f00dbc83796c6ae63137a9bdade074f7648b3e32613412dd", size = 20727743, upload-time = "2026-03-09T07:58:53.426Z" }
|
||||
wheels = [
|
||||
|
|
@ -343,3 +401,16 @@ sdist = { url = "https://files.pythonhosted.org/packages/d3/d4/c88f0e2b1919ec110
|
|||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/01/90d3f3f93990aed34e49fa5071a842f76a5dfc2fdcb026f44a5115c9d969/shapely_polyskel-0.1.2-py3-none-any.whl", hash = "sha256:827400d0d733a41ca7cea40141aa2563d4c709f53feb42d54ccfd436b5cab1bc", size = 14418, upload-time = "2025-01-05T19:43:35.529Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trimesh"
|
||||
version = "5.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/21/3f/a13d797349fdd00a8586b60d6b71df849e1766e2002729697a265b27a773/trimesh-5.1.0.tar.gz", hash = "sha256:927aa8748af8b84cb969c8f53270d929e546f2beada77a2bc234b605fddf1454", size = 870501, upload-time = "2026-08-31T18:28:55.464Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/c8/5ede3b786ef58e0076e9ca748b2c1787183f85e5ad8133373715c31248f9/trimesh-5.1.0-py3-none-any.whl", hash = "sha256:0fa85d1d131e321b683c00747c20090200b92071298b905ea588f609eb204c89", size = 750004, upload-time = "2026-08-31T18:28:53.746Z" },
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue