mirror of
https://github.com/gumyr/build123d.git
synced 2026-03-09 08:11:37 -07:00
feat: allow a single point to be used as either a profile or a guide
modified: pyproject.toml modified: src/build123d/topology/two_d.py modified: tests/test_direct_api/test_face.py
This commit is contained in:
parent
198dab0ab4
commit
02d7be83b1
3 changed files with 87 additions and 22 deletions
|
|
@ -44,7 +44,7 @@ dependencies = [
|
|||
"ipython >= 8.0.0, < 10",
|
||||
"lib3mf >= 2.4.1",
|
||||
"ocpsvg >= 0.5, < 0.6",
|
||||
"ocp_gordon >= 0.1.14",
|
||||
"ocp_gordon >= 0.1.15",
|
||||
"trianglesolver",
|
||||
"sympy",
|
||||
"scipy",
|
||||
|
|
|
|||
|
|
@ -83,11 +83,12 @@ from OCP.BRepTools import BRepTools, BRepTools_ReShape
|
|||
from OCP.gce import gce_MakeLin
|
||||
from OCP.Geom import (
|
||||
Geom_BezierSurface,
|
||||
Geom_BSplineCurve,
|
||||
Geom_RectangularTrimmedSurface,
|
||||
Geom_Surface,
|
||||
Geom_TrimmedCurve,
|
||||
)
|
||||
from OCP.GeomAbs import GeomAbs_C0, GeomAbs_G1, GeomAbs_G2, GeomAbs_CurveType
|
||||
from OCP.GeomAbs import GeomAbs_C0, GeomAbs_CurveType, GeomAbs_G1, GeomAbs_G2
|
||||
from OCP.GeomAPI import (
|
||||
GeomAPI_ExtremaCurveCurve,
|
||||
GeomAPI_PointsToBSplineSurface,
|
||||
|
|
@ -104,13 +105,17 @@ from OCP.Standard import (
|
|||
Standard_NoSuchObject,
|
||||
)
|
||||
from OCP.StdFail import StdFail_NotDone
|
||||
from OCP.TColgp import TColgp_HArray2OfPnt
|
||||
from OCP.TColStd import TColStd_HArray2OfReal
|
||||
from OCP.TColgp import TColgp_Array1OfPnt, TColgp_HArray2OfPnt
|
||||
from OCP.TColStd import (
|
||||
TColStd_Array1OfInteger,
|
||||
TColStd_Array1OfReal,
|
||||
TColStd_HArray2OfReal,
|
||||
)
|
||||
from OCP.TopExp import TopExp
|
||||
from OCP.TopoDS import TopoDS, TopoDS_Face, TopoDS_Shape, TopoDS_Shell, TopoDS_Solid
|
||||
from OCP.TopTools import TopTools_IndexedDataMapOfShapeListOfShape, TopTools_ListOfShape
|
||||
from typing_extensions import Self
|
||||
from ocp_gordon import interpolate_curve_network
|
||||
from typing_extensions import Self
|
||||
|
||||
from build123d.build_enums import (
|
||||
CenterOf,
|
||||
|
|
@ -922,32 +927,66 @@ class Face(Mixin2D, Shape[TopoDS_Face]):
|
|||
@classmethod
|
||||
def make_gordon_surface(
|
||||
cls,
|
||||
profiles: Iterable[Edge],
|
||||
guides: Iterable[Edge],
|
||||
profiles: Iterable[VectorLike | Edge],
|
||||
guides: Iterable[VectorLike | Edge],
|
||||
tolerance: float = 3e-4,
|
||||
) -> Face:
|
||||
"""
|
||||
Creates a Gordon surface from a network of profile and guide curves.
|
||||
Constructs a Gordon surface from a network of profile and guide curves.
|
||||
|
||||
Profiles and guides may consist of points or curves, but at least one
|
||||
profile and one guide must be a non-point curve.
|
||||
|
||||
Args:
|
||||
profiles (Iterable[Edge]): Edges representing profile curves.
|
||||
guides (Iterable[Edge]): Edges representing guide curves.
|
||||
tolerance (float, optional): Tolerance for surface creation and
|
||||
profiles (Iterable[VectorLike | Edge]): Profiles defined as points or edges.
|
||||
guides (Iterable[VectorLike | Edge]): Guides defined as points or edges.
|
||||
tolerance (float, optional): Tolerance used for surface construction and
|
||||
intersection calculations.
|
||||
|
||||
Raises:
|
||||
ValueError: Input edge cannot be empty
|
||||
ValueError: If the input profiles or guides are empty.
|
||||
|
||||
Returns:
|
||||
Face: the interpolated Gordon surface
|
||||
"""
|
||||
|
||||
def to_geom_curve(edge: Edge):
|
||||
if edge.wrapped is None:
|
||||
raise ValueError("input edge cannot be empty")
|
||||
def create_zero_length_bspline_curve(
|
||||
point: gp_Pnt, degree: int = 1
|
||||
) -> Geom_BSplineCurve:
|
||||
"""
|
||||
Helper to create a simple linear B-spline curve.
|
||||
"""
|
||||
control_points = TColgp_Array1OfPnt(1, 2)
|
||||
control_points.SetValue(1, point)
|
||||
control_points.SetValue(2, point)
|
||||
|
||||
adaptor = BRepAdaptor_Curve(edge.wrapped)
|
||||
curve = BRep_Tool.Curve_s(edge.wrapped, 0, 1)
|
||||
knots = TColStd_Array1OfReal(1, 2)
|
||||
knots.SetValue(1, 0.0)
|
||||
knots.SetValue(2, 1.0)
|
||||
|
||||
multiplicities = TColStd_Array1OfInteger(1, 2)
|
||||
multiplicities.SetValue(1, degree + 1)
|
||||
multiplicities.SetValue(2, degree + 1)
|
||||
|
||||
curve = Geom_BSplineCurve(control_points, knots, multiplicities, degree)
|
||||
return curve
|
||||
|
||||
def to_geom_curve(shape: VectorLike | Edge):
|
||||
if isinstance(shape, (Vector, tuple, Sequence)):
|
||||
_shape = Vector(shape)
|
||||
if _shape.wrapped is None:
|
||||
raise ValueError("input VectorLike cannot be empty")
|
||||
|
||||
single_point_curve = create_zero_length_bspline_curve(
|
||||
gp_Pnt(_shape.wrapped.XYZ())
|
||||
)
|
||||
return single_point_curve
|
||||
|
||||
if shape.wrapped is None:
|
||||
raise ValueError("input Edge cannot be empty")
|
||||
|
||||
adaptor = BRepAdaptor_Curve(shape.wrapped)
|
||||
curve = BRep_Tool.Curve_s(shape.wrapped, 0, 1)
|
||||
if not (
|
||||
(adaptor.IsPeriodic() and adaptor.IsClosed())
|
||||
or adaptor.GetType() == GeomAbs_CurveType.GeomAbs_BSplineCurve
|
||||
|
|
@ -958,8 +997,8 @@ class Face(Mixin2D, Shape[TopoDS_Face]):
|
|||
)
|
||||
return curve
|
||||
|
||||
ocp_profiles = [to_geom_curve(edge) for edge in profiles]
|
||||
ocp_guides = [to_geom_curve(edge) for edge in guides]
|
||||
ocp_profiles = [to_geom_curve(shape) for shape in profiles]
|
||||
ocp_guides = [to_geom_curve(shape) for shape in guides]
|
||||
|
||||
gordon_bspline_surface = interpolate_curve_network(
|
||||
ocp_profiles, ocp_guides, tolerance=tolerance
|
||||
|
|
|
|||
|
|
@ -31,9 +31,11 @@ import os
|
|||
import platform
|
||||
import random
|
||||
import unittest
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
from unittest.mock import patch, PropertyMock
|
||||
from OCP.Geom import Geom_RectangularTrimmedSurface
|
||||
from OCP.GeomAPI import GeomAPI_ExtremaCurveCurve
|
||||
|
||||
from build123d.build_common import Locations, PolarLocations
|
||||
from build123d.build_enums import Align, CenterOf, ContinuityLevel, GeomType
|
||||
from build123d.build_line import BuildLine
|
||||
|
|
@ -57,7 +59,6 @@ from build123d.operations_generic import fillet, offset
|
|||
from build123d.operations_part import extrude
|
||||
from build123d.operations_sketch import make_face
|
||||
from build123d.topology import Edge, Face, Shell, Solid, Wire
|
||||
from OCP.GeomAPI import GeomAPI_ExtremaCurveCurve
|
||||
|
||||
|
||||
class TestFace(unittest.TestCase):
|
||||
|
|
@ -448,7 +449,7 @@ class TestFace(unittest.TestCase):
|
|||
profiles, guides, tolerance=tolerance
|
||||
)
|
||||
|
||||
def test_make_gordon_surface_edge_types(self):
|
||||
def test_make_gordon_surface_input_types(self):
|
||||
tolerance = 3e-4
|
||||
|
||||
def point_at_uv_against_expected(u: float, v: float, expected_point: Vector):
|
||||
|
|
@ -538,6 +539,30 @@ class TestFace(unittest.TestCase):
|
|||
point_at_uv_against_expected(u=0.0, v=0.5, expected_point=guides[0] @ 0.5)
|
||||
point_at_uv_against_expected(u=1.0, v=0.5, expected_point=guides[0] @ 0.5)
|
||||
|
||||
profiles = [
|
||||
points[0],
|
||||
ThreePointArc(
|
||||
points[1], (points[1] + points[3]) / 2 + Vector(0, 0, 2), points[3]
|
||||
),
|
||||
points[2],
|
||||
]
|
||||
guides = [
|
||||
Spline(
|
||||
points[0],
|
||||
profiles[1] @ 0,
|
||||
points[2],
|
||||
),
|
||||
Spline(
|
||||
points[0],
|
||||
profiles[1] @ 1,
|
||||
points[2],
|
||||
),
|
||||
]
|
||||
gordon_surface = Face.make_gordon_surface(profiles, guides, tolerance=tolerance)
|
||||
point_at_uv_against_expected(u=0.0, v=1.0, expected_point=guides[0] @ 1)
|
||||
point_at_uv_against_expected(u=1.0, v=1.0, expected_point=guides[1] @ 1)
|
||||
point_at_uv_against_expected(u=1.0, v=0.0, expected_point=points[0])
|
||||
|
||||
def test_make_surface(self):
|
||||
corners = [Vector(x, y) for x in [-50.5, 50.5] for y in [-24.5, 24.5]]
|
||||
net_exterior = Wire(
|
||||
|
|
@ -1232,3 +1257,4 @@ class TestAxesOfSymmetrySplitNone(unittest.TestCase):
|
|||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue