mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 02:30:55 -08:00
Improving test coverage
Some checks failed
benchmarks / benchmarks (macos-13, 3.12) (push) Has been cancelled
benchmarks / benchmarks (macos-14, 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-13, 3.10) (push) Has been cancelled
tests / tests (macos-13, 3.13) (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 (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-13, 3.12) (push) Has been cancelled
benchmarks / benchmarks (macos-14, 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-13, 3.10) (push) Has been cancelled
tests / tests (macos-13, 3.13) (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 (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:
parent
6755a721d8
commit
a52f112375
3 changed files with 138 additions and 90 deletions
|
|
@ -152,6 +152,7 @@ from OCP.TopoDS import (
|
||||||
TopoDS_Face,
|
TopoDS_Face,
|
||||||
TopoDS_Shape,
|
TopoDS_Shape,
|
||||||
TopoDS_Shell,
|
TopoDS_Shell,
|
||||||
|
TopoDS_Vertex,
|
||||||
TopoDS_Wire,
|
TopoDS_Wire,
|
||||||
)
|
)
|
||||||
from OCP.gp import (
|
from OCP.gp import (
|
||||||
|
|
@ -803,19 +804,6 @@ class Mixin1D(Shape):
|
||||||
|
|
||||||
if side != Side.BOTH:
|
if side != Side.BOTH:
|
||||||
# Find and remove the end arcs
|
# Find and remove the end arcs
|
||||||
# offset_edges = offset_wire.edges()
|
|
||||||
# edges_to_keep: list[list[Edge]] = [[], [], []]
|
|
||||||
# i = 0
|
|
||||||
# for edge in offset_edges:
|
|
||||||
# if edge.geom_type == GeomType.CIRCLE and (
|
|
||||||
# edge.arc_center == line.position_at(0)
|
|
||||||
# or edge.arc_center == line.position_at(1)
|
|
||||||
# ):
|
|
||||||
# i += 1
|
|
||||||
# else:
|
|
||||||
# edges_to_keep[i].append(edge)
|
|
||||||
# edges_to_keep[0] += edges_to_keep[2]
|
|
||||||
# wires = [Wire(edges) for edges in edges_to_keep[0:2]]
|
|
||||||
endpoints = (line.position_at(0), line.position_at(1))
|
endpoints = (line.position_at(0), line.position_at(1))
|
||||||
offset_edges = offset_wire.edges().filter_by(
|
offset_edges = offset_wire.edges().filter_by(
|
||||||
lambda e: (
|
lambda e: (
|
||||||
|
|
@ -826,8 +814,6 @@ class Mixin1D(Shape):
|
||||||
)
|
)
|
||||||
wires = edges_to_wires(offset_edges)
|
wires = edges_to_wires(offset_edges)
|
||||||
centers = [w.position_at(0.5) for w in wires]
|
centers = [w.position_at(0.5) for w in wires]
|
||||||
tangent = line.tangent_at(0)
|
|
||||||
start = line.position_at(0)
|
|
||||||
angles = [
|
angles = [
|
||||||
line.tangent_at(0).get_signed_angle(c - line.position_at(0))
|
line.tangent_at(0).get_signed_angle(c - line.position_at(0))
|
||||||
for c in centers
|
for c in centers
|
||||||
|
|
@ -2619,7 +2605,6 @@ class Wire(Mixin1D, Shape[TopoDS_Wire]):
|
||||||
edge, label, color, parent = args[:4] + (None,) * (4 - l_a)
|
edge, label, color, parent = args[:4] + (None,) * (4 - l_a)
|
||||||
elif isinstance(args[0], Wire):
|
elif isinstance(args[0], Wire):
|
||||||
wire, label, color, parent = args[:4] + (None,) * (4 - l_a)
|
wire, label, color, parent = args[:4] + (None,) * (4 - l_a)
|
||||||
# elif isinstance(args[0], Curve):
|
|
||||||
elif (
|
elif (
|
||||||
hasattr(args[0], "wrapped")
|
hasattr(args[0], "wrapped")
|
||||||
and isinstance(args[0].wrapped, TopoDS_Compound)
|
and isinstance(args[0].wrapped, TopoDS_Compound)
|
||||||
|
|
@ -3202,14 +3187,14 @@ class Wire(Mixin1D, Shape[TopoDS_Wire]):
|
||||||
raise ValueError("Can't find point on empty wire")
|
raise ValueError("Can't find point on empty wire")
|
||||||
|
|
||||||
point_on_curve = Vector(point)
|
point_on_curve = Vector(point)
|
||||||
|
vertex_on_curve = Vertex(point_on_curve)
|
||||||
|
assert vertex_on_curve.wrapped is not None
|
||||||
|
|
||||||
separation = self.distance_to(point)
|
separation = self.distance_to(point)
|
||||||
if not isclose_b(separation, 0, abs_tol=TOLERANCE):
|
if not isclose_b(separation, 0, abs_tol=TOLERANCE):
|
||||||
raise ValueError(f"point ({point}) is {separation} from wire")
|
raise ValueError(f"point ({point}) is {separation} from wire")
|
||||||
|
|
||||||
extrema = BRepExtrema_DistShapeShape(
|
extrema = BRepExtrema_DistShapeShape(vertex_on_curve.wrapped, self.wrapped)
|
||||||
Vertex(point_on_curve).wrapped, self.wrapped
|
|
||||||
)
|
|
||||||
extrema.Perform()
|
extrema.Perform()
|
||||||
if not extrema.IsDone() or extrema.NbSolution() == 0:
|
if not extrema.IsDone() or extrema.NbSolution() == 0:
|
||||||
raise ValueError("point is not on Wire")
|
raise ValueError("point is not on Wire")
|
||||||
|
|
@ -3217,15 +3202,19 @@ class Wire(Mixin1D, Shape[TopoDS_Wire]):
|
||||||
supp_type = extrema.SupportTypeShape2(1)
|
supp_type = extrema.SupportTypeShape2(1)
|
||||||
|
|
||||||
if supp_type == BRepExtrema_SupportType.BRepExtrema_IsOnEdge:
|
if supp_type == BRepExtrema_SupportType.BRepExtrema_IsOnEdge:
|
||||||
closest_topods_edge = downcast(extrema.SupportOnShape2(1))
|
closest_topods_edge = tcast(
|
||||||
|
TopoDS_Edge, downcast(extrema.SupportOnShape2(1))
|
||||||
|
)
|
||||||
closest_topods_edge_param = extrema.ParOnEdgeS2(1)[0]
|
closest_topods_edge_param = extrema.ParOnEdgeS2(1)[0]
|
||||||
elif supp_type == BRepExtrema_SupportType.BRepExtrema_IsVertex:
|
elif supp_type == BRepExtrema_SupportType.BRepExtrema_IsVertex:
|
||||||
v_hit = downcast(extrema.SupportOnShape2(1))
|
v_hit = tcast(TopoDS_Vertex, downcast(extrema.SupportOnShape2(1)))
|
||||||
vertex_edge_map = TopTools_IndexedDataMapOfShapeListOfShape()
|
vertex_edge_map = TopTools_IndexedDataMapOfShapeListOfShape()
|
||||||
TopExp.MapShapesAndAncestors_s(
|
TopExp.MapShapesAndAncestors_s(
|
||||||
self.wrapped, ta.TopAbs_VERTEX, ta.TopAbs_EDGE, vertex_edge_map
|
self.wrapped, ta.TopAbs_VERTEX, ta.TopAbs_EDGE, vertex_edge_map
|
||||||
)
|
)
|
||||||
closest_topods_edge = downcast(vertex_edge_map.FindFromKey(v_hit).First())
|
closest_topods_edge = tcast(
|
||||||
|
TopoDS_Edge, downcast(vertex_edge_map.FindFromKey(v_hit).First())
|
||||||
|
)
|
||||||
closest_topods_edge_param = BRep_Tool.Parameter_s(
|
closest_topods_edge_param = BRep_Tool.Parameter_s(
|
||||||
v_hit, closest_topods_edge
|
v_hit, closest_topods_edge
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import unittest
|
||||||
|
|
||||||
from unittest.mock import patch, PropertyMock
|
from unittest.mock import patch, PropertyMock
|
||||||
|
|
||||||
from build123d.topology.shape_core import TOLERANCE
|
|
||||||
from build123d.build_enums import AngularDirection, GeomType, PositionMode, Transition
|
from build123d.build_enums import AngularDirection, GeomType, PositionMode, Transition
|
||||||
from build123d.geometry import Axis, Plane, Vector
|
from build123d.geometry import Axis, Plane, Vector
|
||||||
from build123d.objects_curve import CenterArc, EllipticalCenterArc
|
from build123d.objects_curve import CenterArc, EllipticalCenterArc
|
||||||
|
|
@ -187,6 +186,10 @@ class TestEdge(unittest.TestCase):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
line.trim(0.75, 0.25)
|
line.trim(0.75, 0.25)
|
||||||
|
|
||||||
|
line.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
line.trim(0.1, 0.9)
|
||||||
|
|
||||||
def test_trim_to_length(self):
|
def test_trim_to_length(self):
|
||||||
|
|
||||||
e1 = Edge.make_line((0, 0), (10, 10))
|
e1 = Edge.make_line((0, 0), (10, 10))
|
||||||
|
|
@ -210,6 +213,10 @@ class TestEdge(unittest.TestCase):
|
||||||
e4_trim = Edge(a4).trim_to_length(0.5, 2)
|
e4_trim = Edge(a4).trim_to_length(0.5, 2)
|
||||||
self.assertAlmostEqual(e4_trim.length, 2, 5)
|
self.assertAlmostEqual(e4_trim.length, 2, 5)
|
||||||
|
|
||||||
|
e1.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
e1.trim_to_length(0.1, 2)
|
||||||
|
|
||||||
def test_bezier(self):
|
def test_bezier(self):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
Edge.make_bezier((1, 1))
|
Edge.make_bezier((1, 1))
|
||||||
|
|
@ -278,6 +285,10 @@ class TestEdge(unittest.TestCase):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
edge.param_at_point((-1, 1))
|
edge.param_at_point((-1, 1))
|
||||||
|
|
||||||
|
ea.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
ea.param_at_point((15, 5))
|
||||||
|
|
||||||
def test_param_at_point_bspline(self):
|
def test_param_at_point_bspline(self):
|
||||||
# Define a complex spline with inflections and non-monotonic behavior
|
# Define a complex spline with inflections and non-monotonic behavior
|
||||||
curve = Edge.make_spline(
|
curve = Edge.make_spline(
|
||||||
|
|
@ -315,6 +326,10 @@ class TestEdge(unittest.TestCase):
|
||||||
e2r = e2.reversed(reconstruct=True)
|
e2r = e2.reversed(reconstruct=True)
|
||||||
self.assertAlmostEqual((e2 @ 0.1).X, -(e2r @ 0.1).X, 5)
|
self.assertAlmostEqual((e2 @ 0.1).X, -(e2r @ 0.1).X, 5)
|
||||||
|
|
||||||
|
e2.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
e2.reversed()
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
Edge(direction=(1, 0, 0))
|
Edge(direction=(1, 0, 0))
|
||||||
|
|
@ -410,69 +425,5 @@ class TestEdge(unittest.TestCase):
|
||||||
line.geom_adaptor()
|
line.geom_adaptor()
|
||||||
|
|
||||||
|
|
||||||
class TestWireToBSpline(unittest.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
# A simple rectilinear, multi-segment wire:
|
|
||||||
# p0 ── p1
|
|
||||||
# │
|
|
||||||
# p2 ── p3
|
|
||||||
self.p0 = Vector(0, 0, 0)
|
|
||||||
self.p1 = Vector(20, 0, 0)
|
|
||||||
self.p2 = Vector(20, 10, 0)
|
|
||||||
self.p3 = Vector(35, 10, 0)
|
|
||||||
|
|
||||||
e01 = Edge.make_line(self.p0, self.p1)
|
|
||||||
e12 = Edge.make_line(self.p1, self.p2)
|
|
||||||
e23 = Edge.make_line(self.p2, self.p3)
|
|
||||||
|
|
||||||
self.wire = Wire([e01, e12, e23])
|
|
||||||
|
|
||||||
def test_to_bspline_basic_properties(self):
|
|
||||||
bs = self.wire._to_bspline()
|
|
||||||
|
|
||||||
# 1) Type/geom check
|
|
||||||
self.assertIsInstance(bs, Edge)
|
|
||||||
self.assertEqual(bs.geom_type, GeomType.BSPLINE)
|
|
||||||
|
|
||||||
# 2) Endpoint preservation
|
|
||||||
self.assertLess((Vector(bs.vertices()[0]) - self.p0).length, TOLERANCE)
|
|
||||||
self.assertLess((Vector(bs.vertices()[-1]) - self.p3).length, TOLERANCE)
|
|
||||||
|
|
||||||
# 3) Length preservation (within numerical tolerance)
|
|
||||||
self.assertAlmostEqual(bs.length, self.wire.length, delta=1e-6)
|
|
||||||
|
|
||||||
# 4) Topology collapse: single edge has only 2 vertices (start/end)
|
|
||||||
self.assertEqual(len(bs.vertices()), 2)
|
|
||||||
|
|
||||||
# 5) The composite BSpline should pass through former junctions
|
|
||||||
for junction in (self.p1, self.p2):
|
|
||||||
self.assertLess(bs.distance_to(junction), 1e-6)
|
|
||||||
|
|
||||||
# 6) Normalized parameter increases along former junctions
|
|
||||||
u_p1 = bs.param_at_point(self.p1)
|
|
||||||
u_p2 = bs.param_at_point(self.p2)
|
|
||||||
self.assertGreater(u_p1, 0.0)
|
|
||||||
self.assertLess(u_p2, 1.0)
|
|
||||||
self.assertLess(u_p1, u_p2)
|
|
||||||
|
|
||||||
# 7) Re-evaluating at those parameters should be close to the junctions
|
|
||||||
self.assertLess((bs.position_at(u_p1) - self.p1).length, 1e-6)
|
|
||||||
self.assertLess((bs.position_at(u_p2) - self.p2).length, 1e-6)
|
|
||||||
|
|
||||||
def test_to_bspline_orientation(self):
|
|
||||||
# Ensure the BSpline follows the wire's topological order
|
|
||||||
bs = self.wire._to_bspline()
|
|
||||||
|
|
||||||
# Start ~ p0, end ~ p3
|
|
||||||
self.assertLess((bs.position_at(0.0) - self.p0).length, 1e-6)
|
|
||||||
self.assertLess((bs.position_at(1.0) - self.p3).length, 1e-6)
|
|
||||||
|
|
||||||
# Parameters at interior points should sit between 0 and 1
|
|
||||||
u0 = bs.param_at_point(self.p1)
|
|
||||||
u1 = bs.param_at_point(self.p2)
|
|
||||||
self.assertTrue(0.0 < u0 < 1.0)
|
|
||||||
self.assertTrue(0.0 < u1 < 1.0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,16 @@ import random
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from build123d.build_enums import Side
|
from build123d.topology.shape_core import TOLERANCE
|
||||||
|
|
||||||
|
from build123d.build_enums import GeomType, Side
|
||||||
from build123d.build_line import BuildLine
|
from build123d.build_line import BuildLine
|
||||||
from build123d.geometry import Axis, Color, Location, Plane, Vector
|
from build123d.geometry import Axis, Color, Location, Plane, Vector
|
||||||
from build123d.objects_curve import Line, PolarLine, Polyline, Spline
|
from build123d.objects_curve import Curve, Line, PolarLine, Polyline, Spline
|
||||||
from build123d.objects_sketch import Circle, Rectangle, RegularPolygon
|
from build123d.objects_sketch import Circle, Rectangle, RegularPolygon
|
||||||
from build123d.operations_generic import fillet
|
from build123d.operations_generic import fillet
|
||||||
from build123d.topology import Edge, Face, Wire
|
from build123d.topology import Edge, Face, Wire
|
||||||
|
from OCP.BRepAdaptor import BRepAdaptor_CompCurve
|
||||||
|
|
||||||
|
|
||||||
class TestWire(unittest.TestCase):
|
class TestWire(unittest.TestCase):
|
||||||
|
|
@ -64,6 +67,9 @@ class TestWire(unittest.TestCase):
|
||||||
self.assertAlmostEqual(
|
self.assertAlmostEqual(
|
||||||
squaroid.length, 4 * (1 - 2 * 0.1) + 2 * math.pi * 0.1, 5
|
squaroid.length, 4 * (1 - 2 * 0.1) + 2 * math.pi * 0.1, 5
|
||||||
)
|
)
|
||||||
|
square.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
square.fillet_2d(0.1, square.vertices())
|
||||||
|
|
||||||
def test_chamfer_2d(self):
|
def test_chamfer_2d(self):
|
||||||
square = Wire.make_rect(1, 1)
|
square = Wire.make_rect(1, 1)
|
||||||
|
|
@ -71,6 +77,18 @@ class TestWire(unittest.TestCase):
|
||||||
self.assertAlmostEqual(
|
self.assertAlmostEqual(
|
||||||
squaroid.length, 4 * (1 - 2 * 0.1 + 0.1 * math.sqrt(2)), 5
|
squaroid.length, 4 * (1 - 2 * 0.1 + 0.1 * math.sqrt(2)), 5
|
||||||
)
|
)
|
||||||
|
verts = square.vertices()
|
||||||
|
verts[0].wrapped = None
|
||||||
|
three_corners = square.chamfer_2d(0.1, 0.1, verts)
|
||||||
|
self.assertEqual(len(three_corners.edges()), 7)
|
||||||
|
|
||||||
|
square.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
square.chamfer_2d(0.1, 0.1, square.vertices())
|
||||||
|
|
||||||
|
def test_close(self):
|
||||||
|
t = Polyline((0, 0), (1, 0), (0, 1), close=True)
|
||||||
|
self.assertIs(t, t.close())
|
||||||
|
|
||||||
def test_chamfer_2d_edge(self):
|
def test_chamfer_2d_edge(self):
|
||||||
square = Wire.make_rect(1, 1)
|
square = Wire.make_rect(1, 1)
|
||||||
|
|
@ -98,7 +116,15 @@ class TestWire(unittest.TestCase):
|
||||||
hull_wire = Wire.make_convex_hull(adjoining_edges)
|
hull_wire = Wire.make_convex_hull(adjoining_edges)
|
||||||
self.assertAlmostEqual(Face(hull_wire).area, 319.9612, 4)
|
self.assertAlmostEqual(Face(hull_wire).area, 319.9612, 4)
|
||||||
|
|
||||||
# def test_fix_degenerate_edges(self):
|
def test_fix_degenerate_edges(self):
|
||||||
|
e0 = Edge.make_line((0, 0), (1, 0))
|
||||||
|
e1 = Edge.make_line((2, 0), (1, 0))
|
||||||
|
|
||||||
|
w = Wire([e0, e1])
|
||||||
|
w.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
w.fix_degenerate_edges(0.1)
|
||||||
|
|
||||||
# # Can't find a way to create one
|
# # Can't find a way to create one
|
||||||
# edge0 = Edge.make_line((0, 0, 0), (1, 0, 0))
|
# edge0 = Edge.make_line((0, 0, 0), (1, 0, 0))
|
||||||
# edge1 = Edge.make_line(edge0 @ 0, edge0 @ 0 + Vector(0, 1, 0))
|
# edge1 = Edge.make_line(edge0 @ 0, edge0 @ 0 + Vector(0, 1, 0))
|
||||||
|
|
@ -175,6 +201,10 @@ class TestWire(unittest.TestCase):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
w1.param_at_point((20, 20, 20))
|
w1.param_at_point((20, 20, 20))
|
||||||
|
|
||||||
|
w1.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
w1.param_at_point((0, 0))
|
||||||
|
|
||||||
def test_param_at_point_reversed_edges(self):
|
def test_param_at_point_reversed_edges(self):
|
||||||
with BuildLine(Plane.YZ) as wing_line:
|
with BuildLine(Plane.YZ) as wing_line:
|
||||||
l1 = Line((0, 65), (80 / 2 + 1.526 * 4, 65))
|
l1 = Line((0, 65), (80 / 2 + 1.526 * 4, 65))
|
||||||
|
|
@ -216,6 +246,13 @@ class TestWire(unittest.TestCase):
|
||||||
self.assertAlmostEqual(ordered_edges[1] @ 0, (1, 0, 0), 5)
|
self.assertAlmostEqual(ordered_edges[1] @ 0, (1, 0, 0), 5)
|
||||||
self.assertAlmostEqual(ordered_edges[2] @ 0, (1, 1, 0), 5)
|
self.assertAlmostEqual(ordered_edges[2] @ 0, (1, 1, 0), 5)
|
||||||
|
|
||||||
|
def test_geom_adaptor(self):
|
||||||
|
w = Polyline((0, 0), (1, 0), (1, 1))
|
||||||
|
self.assertTrue(isinstance(w.geom_adaptor(), BRepAdaptor_CompCurve))
|
||||||
|
w.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
w.geom_adaptor()
|
||||||
|
|
||||||
def test_constructor(self):
|
def test_constructor(self):
|
||||||
e0 = Edge.make_line((0, 0), (1, 0))
|
e0 = Edge.make_line((0, 0), (1, 0))
|
||||||
e1 = Edge.make_line((1, 0), (1, 1))
|
e1 = Edge.make_line((1, 0), (1, 1))
|
||||||
|
|
@ -241,9 +278,80 @@ class TestWire(unittest.TestCase):
|
||||||
c0 = Polyline((0, 0), (1, 0), (1, 1))
|
c0 = Polyline((0, 0), (1, 0), (1, 1))
|
||||||
w8 = Wire(c0)
|
w8 = Wire(c0)
|
||||||
self.assertTrue(w8.is_valid)
|
self.assertTrue(w8.is_valid)
|
||||||
|
w9 = Wire(Curve([e0, e1]))
|
||||||
|
self.assertTrue(w9.is_valid)
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
Wire(bob="fred")
|
Wire(bob="fred")
|
||||||
|
|
||||||
|
|
||||||
|
class TestWireToBSpline(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
# A simple rectilinear, multi-segment wire:
|
||||||
|
# p0 ── p1
|
||||||
|
# │
|
||||||
|
# p2 ── p3
|
||||||
|
self.p0 = Vector(0, 0, 0)
|
||||||
|
self.p1 = Vector(20, 0, 0)
|
||||||
|
self.p2 = Vector(20, 10, 0)
|
||||||
|
self.p3 = Vector(35, 10, 0)
|
||||||
|
|
||||||
|
e01 = Edge.make_line(self.p0, self.p1)
|
||||||
|
e12 = Edge.make_line(self.p1, self.p2)
|
||||||
|
e23 = Edge.make_line(self.p2, self.p3)
|
||||||
|
|
||||||
|
self.wire = Wire([e01, e12, e23])
|
||||||
|
|
||||||
|
def test_to_bspline_basic_properties(self):
|
||||||
|
bs = self.wire._to_bspline()
|
||||||
|
|
||||||
|
# 1) Type/geom check
|
||||||
|
self.assertIsInstance(bs, Edge)
|
||||||
|
self.assertEqual(bs.geom_type, GeomType.BSPLINE)
|
||||||
|
|
||||||
|
# 2) Endpoint preservation
|
||||||
|
self.assertLess((Vector(bs.vertices()[0]) - self.p0).length, TOLERANCE)
|
||||||
|
self.assertLess((Vector(bs.vertices()[-1]) - self.p3).length, TOLERANCE)
|
||||||
|
|
||||||
|
# 3) Length preservation (within numerical tolerance)
|
||||||
|
self.assertAlmostEqual(bs.length, self.wire.length, delta=1e-6)
|
||||||
|
|
||||||
|
# 4) Topology collapse: single edge has only 2 vertices (start/end)
|
||||||
|
self.assertEqual(len(bs.vertices()), 2)
|
||||||
|
|
||||||
|
# 5) The composite BSpline should pass through former junctions
|
||||||
|
for junction in (self.p1, self.p2):
|
||||||
|
self.assertLess(bs.distance_to(junction), 1e-6)
|
||||||
|
|
||||||
|
# 6) Normalized parameter increases along former junctions
|
||||||
|
u_p1 = bs.param_at_point(self.p1)
|
||||||
|
u_p2 = bs.param_at_point(self.p2)
|
||||||
|
self.assertGreater(u_p1, 0.0)
|
||||||
|
self.assertLess(u_p2, 1.0)
|
||||||
|
self.assertLess(u_p1, u_p2)
|
||||||
|
|
||||||
|
# 7) Re-evaluating at those parameters should be close to the junctions
|
||||||
|
self.assertLess((bs.position_at(u_p1) - self.p1).length, 1e-6)
|
||||||
|
self.assertLess((bs.position_at(u_p2) - self.p2).length, 1e-6)
|
||||||
|
|
||||||
|
w = self.wire
|
||||||
|
w.wrapped = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
w._to_bspline()
|
||||||
|
|
||||||
|
def test_to_bspline_orientation(self):
|
||||||
|
# Ensure the BSpline follows the wire's topological order
|
||||||
|
bs = self.wire._to_bspline()
|
||||||
|
|
||||||
|
# Start ~ p0, end ~ p3
|
||||||
|
self.assertLess((bs.position_at(0.0) - self.p0).length, 1e-6)
|
||||||
|
self.assertLess((bs.position_at(1.0) - self.p3).length, 1e-6)
|
||||||
|
|
||||||
|
# Parameters at interior points should sit between 0 and 1
|
||||||
|
u0 = bs.param_at_point(self.p1)
|
||||||
|
u1 = bs.param_at_point(self.p2)
|
||||||
|
self.assertTrue(0.0 < u0 < 1.0)
|
||||||
|
self.assertTrue(0.0 < u1 < 1.0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue