mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-05 18:20:46 -08:00
Replacing Mixin1D.discretize with enhanced Minxin1D.positions
Some checks failed
benchmarks / benchmarks (macos-14, 3.12) (push) Has been cancelled
benchmarks / benchmarks (macos-15-intel, 3.12) (push) Has been cancelled
benchmarks / benchmarks (ubuntu-latest, 3.12) (push) Has been cancelled
benchmarks / benchmarks (windows-latest, 3.12) (push) Has been cancelled
Upload coverage reports to Codecov / run (push) Has been cancelled
pylint / lint (3.10) (push) Has been cancelled
Run type checker / typecheck (3.10) (push) Has been cancelled
Run type checker / typecheck (3.13) (push) Has been cancelled
Wheel building and publishing / Build wheel on ubuntu-latest (push) Has been cancelled
tests / tests (macos-14, 3.10) (push) Has been cancelled
tests / tests (macos-14, 3.13) (push) Has been cancelled
tests / tests (macos-15-intel, 3.10) (push) Has been cancelled
tests / tests (macos-15-intel, 3.13) (push) Has been cancelled
tests / tests (ubuntu-latest, 3.10) (push) Has been cancelled
tests / tests (ubuntu-latest, 3.13) (push) Has been cancelled
tests / tests (windows-latest, 3.10) (push) Has been cancelled
tests / tests (windows-latest, 3.13) (push) Has been cancelled
Wheel building and publishing / upload_pypi (push) Has been cancelled
Some checks failed
benchmarks / benchmarks (macos-14, 3.12) (push) Has been cancelled
benchmarks / benchmarks (macos-15-intel, 3.12) (push) Has been cancelled
benchmarks / benchmarks (ubuntu-latest, 3.12) (push) Has been cancelled
benchmarks / benchmarks (windows-latest, 3.12) (push) Has been cancelled
Upload coverage reports to Codecov / run (push) Has been cancelled
pylint / lint (3.10) (push) Has been cancelled
Run type checker / typecheck (3.10) (push) Has been cancelled
Run type checker / typecheck (3.13) (push) Has been cancelled
Wheel building and publishing / Build wheel on ubuntu-latest (push) Has been cancelled
tests / tests (macos-14, 3.10) (push) Has been cancelled
tests / tests (macos-14, 3.13) (push) Has been cancelled
tests / tests (macos-15-intel, 3.10) (push) Has been cancelled
tests / tests (macos-15-intel, 3.13) (push) Has been cancelled
tests / tests (ubuntu-latest, 3.10) (push) Has been cancelled
tests / tests (ubuntu-latest, 3.13) (push) Has been cancelled
tests / tests (windows-latest, 3.10) (push) Has been cancelled
tests / tests (windows-latest, 3.13) (push) Has been cancelled
Wheel building and publishing / upload_pypi (push) Has been cancelled
This commit is contained in:
commit
a8fc16b344
2 changed files with 100 additions and 36 deletions
|
|
@ -120,6 +120,9 @@ from OCP.GeomAbs import (
|
|||
GeomAbs_C0,
|
||||
GeomAbs_C1,
|
||||
GeomAbs_C2,
|
||||
GeomAbs_C3,
|
||||
GeomAbs_CN,
|
||||
GeomAbs_C1,
|
||||
GeomAbs_G1,
|
||||
GeomAbs_G2,
|
||||
GeomAbs_JoinType,
|
||||
|
|
@ -545,31 +548,6 @@ class Mixin1D(Shape[TOPODS]):
|
|||
|
||||
return result
|
||||
|
||||
def discretize(self, deflection: float = 0.1, quasi=True) -> list[Vector]:
|
||||
"""Discretize the shape into a list of points"""
|
||||
if self.wrapped is None:
|
||||
raise ValueError("Cannot discretize an empty shape")
|
||||
curve = self.geom_adaptor()
|
||||
if quasi:
|
||||
discretizer = GCPnts_QuasiUniformDeflection()
|
||||
else:
|
||||
discretizer = GCPnts_UniformDeflection()
|
||||
discretizer.Initialize(
|
||||
curve,
|
||||
deflection,
|
||||
curve.FirstParameter(),
|
||||
curve.LastParameter(),
|
||||
)
|
||||
|
||||
assert discretizer.IsDone()
|
||||
|
||||
return [
|
||||
Vector(v.X(), v.Y(), v.Z())
|
||||
for v in (
|
||||
curve.Value(discretizer.Parameter(i))
|
||||
for i in range(1, discretizer.NbPoints() + 1)
|
||||
)
|
||||
]
|
||||
def curvature_comb(
|
||||
self, count: int = 100, max_tooth_size: float | None = None
|
||||
) -> ShapeList[Edge]:
|
||||
|
|
@ -1207,22 +1185,52 @@ class Mixin1D(Shape[TOPODS]):
|
|||
|
||||
def positions(
|
||||
self,
|
||||
distances: Iterable[float],
|
||||
distances: Iterable[float] | None = None,
|
||||
position_mode: PositionMode = PositionMode.PARAMETER,
|
||||
deflection: float | None = None,
|
||||
) -> list[Vector]:
|
||||
"""Positions along curve
|
||||
|
||||
Generate positions along the underlying curve
|
||||
|
||||
Args:
|
||||
distances (Iterable[float]): distance or parameter values
|
||||
position_mode (PositionMode, optional): position calculation mode.
|
||||
Defaults to PositionMode.PARAMETER.
|
||||
distances (Iterable[float] | None, optional): distance or parameter values.
|
||||
Defaults to None.
|
||||
position_mode (PositionMode, optional): position calculation mode only applies
|
||||
when using distances. Defaults to PositionMode.PARAMETER.
|
||||
deflection (float | None, optional): maximum deflection between the curve and
|
||||
the polygon that results from the computed points. Defaults to None.
|
||||
|
||||
|
||||
Returns:
|
||||
list[Vector]: positions along curve
|
||||
"""
|
||||
if deflection is not None:
|
||||
curve: BRepAdaptor_Curve | BRepAdaptor_CompCurve = self.geom_adaptor()
|
||||
# GCPnts_UniformDeflection provides the best results but is limited
|
||||
if curve.Continuity() in (GeomAbs_C2, GeomAbs_C3, GeomAbs_CN):
|
||||
discretizer: (
|
||||
GCPnts_UniformDeflection | GCPnts_QuasiUniformDeflection
|
||||
) = GCPnts_UniformDeflection()
|
||||
else:
|
||||
discretizer = GCPnts_QuasiUniformDeflection()
|
||||
|
||||
discretizer.Initialize(
|
||||
curve,
|
||||
deflection,
|
||||
curve.FirstParameter(),
|
||||
curve.LastParameter(),
|
||||
)
|
||||
if not discretizer.IsDone() or discretizer.NbPoints() == 0:
|
||||
raise RuntimeError("Deflection calculation failed")
|
||||
return [
|
||||
Vector(curve.Value(discretizer.Parameter(i + 1)))
|
||||
for i in range(discretizer.NbPoints())
|
||||
]
|
||||
elif distances is not None:
|
||||
return [self.position_at(d, position_mode) for d in distances]
|
||||
else:
|
||||
raise ValueError("Either distances or deflection must be provided")
|
||||
|
||||
def project(
|
||||
self, face: Face, direction: VectorLike, closest: bool = True
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ license:
|
|||
|
||||
import math
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from build123d.build_enums import (
|
||||
CenterOf,
|
||||
|
|
@ -106,13 +107,73 @@ class TestMixin1D(unittest.TestCase):
|
|||
5,
|
||||
)
|
||||
|
||||
def test_positions(self):
|
||||
def test_positions_with_distances(self):
|
||||
e = Edge.make_line((0, 0, 0), (1, 1, 1))
|
||||
distances = [i / 4 for i in range(3)]
|
||||
pts = e.positions(distances)
|
||||
for i, position in enumerate(pts):
|
||||
self.assertAlmostEqual(position, (i / 4, i / 4, i / 4), 5)
|
||||
|
||||
def test_positions_deflection_line(self):
|
||||
"""Deflection sampling on a straight line should yield exactly 2 points."""
|
||||
e = Edge.make_line((0, 0, 0), (10, 0, 0))
|
||||
pts = e.positions(deflection=0.1)
|
||||
|
||||
self.assertEqual(len(pts), 2)
|
||||
self.assertAlmostEqual(pts[0], (0, 0, 0), 7)
|
||||
self.assertAlmostEqual(pts[1], (10, 0, 0), 7)
|
||||
|
||||
def test_positions_deflection_circle(self):
|
||||
"""Deflection on a C2 curve (circle) should produce multiple points."""
|
||||
radius = 5
|
||||
e = Edge.make_circle(radius)
|
||||
|
||||
pts = e.positions(deflection=0.1)
|
||||
|
||||
# Should produce more than just two points
|
||||
self.assertGreater(len(pts), 2)
|
||||
|
||||
# Endpoints should match curve endpoints
|
||||
first, last = pts[0], pts[-1]
|
||||
curve = e.geom_adaptor()
|
||||
p0 = Vector(curve.Value(curve.FirstParameter()))
|
||||
p1 = Vector(curve.Value(curve.LastParameter()))
|
||||
|
||||
self.assertAlmostEqual(first, p0, 7)
|
||||
self.assertAlmostEqual(last, p1, 7)
|
||||
|
||||
def test_positions_deflection_resolution(self):
|
||||
"""Smaller deflection tolerance should produce more points."""
|
||||
e = Edge.make_circle(10)
|
||||
|
||||
pts_coarse = e.positions(deflection=0.5)
|
||||
pts_fine = e.positions(deflection=0.05)
|
||||
|
||||
self.assertGreater(len(pts_fine), len(pts_coarse))
|
||||
|
||||
def test_positions_deflection_C0_curve(self):
|
||||
"""C0 spline should use QuasiUniformDeflection and still succeed."""
|
||||
e = Polyline((0, 0), (1, 2), (2, 0))._to_bspline() # C0
|
||||
pts = e.positions(deflection=0.1)
|
||||
|
||||
self.assertGreater(len(pts), 2)
|
||||
|
||||
def test_positions_missing_arguments(self):
|
||||
e = Edge.make_line((0, 0, 0), (1, 0, 0))
|
||||
with self.assertRaises(ValueError):
|
||||
e.positions()
|
||||
|
||||
def test_positions_deflection_failure(self):
|
||||
e = Edge.make_circle(1.0)
|
||||
|
||||
with patch("build123d.topology.one_d.GCPnts_UniformDeflection") as MockDefl:
|
||||
instance = MockDefl.return_value
|
||||
instance.IsDone.return_value = False
|
||||
instance.NbPoints.return_value = 0
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
e.positions(deflection=0.1)
|
||||
|
||||
def test_tangent_at(self):
|
||||
self.assertAlmostEqual(
|
||||
Edge.make_circle(1, start_angle=0, end_angle=90).tangent_at(1.0),
|
||||
|
|
@ -368,11 +429,6 @@ class TestMixin1D(unittest.TestCase):
|
|||
self.assertAlmostEqual(common.z_dir.Y, 0, 5)
|
||||
self.assertAlmostEqual(common.z_dir.Z, 0, 5)
|
||||
|
||||
def test_discretize(self):
|
||||
edge = Edge.make_circle(2, start_angle=0, end_angle=180)
|
||||
points = edge.discretize(0.1)
|
||||
self.assertEqual(len(points), 6)
|
||||
|
||||
def test_edge_volume(self):
|
||||
edge = Edge.make_line((0, 0), (1, 1))
|
||||
self.assertAlmostEqual(edge.volume, 0, 5)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue