diff --git a/src/build123d/geometry.py b/src/build123d/geometry.py index d8d0360f..1f46c7f9 100644 --- a/src/build123d/geometry.py +++ b/src/build123d/geometry.py @@ -60,7 +60,7 @@ from OCP.BRepGProp import BRepGProp, BRepGProp_Face # used for mass calculation from OCP.BRepMesh import BRepMesh_IncrementalMesh from OCP.BRepTools import BRepTools -from OCP.Geom import Geom_Line, Geom_Plane +from OCP.Geom import Geom_BoundedSurface, Geom_Line, Geom_Plane from OCP.GeomAPI import GeomAPI_ProjectPointOnSurf, GeomAPI_IntCS, GeomAPI_IntSS from OCP.gp import ( gp_Ax1, @@ -2092,18 +2092,19 @@ def optarg(kwargs, name, args, index, default): elif arg_face: # Determine if face is planar surface = BRep_Tool.Surface_s(arg_face.wrapped) - if not isinstance(surface, Geom_Plane): + if not arg_face.is_planar: raise ValueError("Planes can only be created from planar faces") properties = GProp_GProps() BRepGProp.SurfaceProperties_s(arg_face.wrapped, properties) self._origin = Vector(properties.CentreOfMass()) - self.x_dir = ( - Vector(arg_x_dir) - if arg_x_dir - else Vector( - BRep_Tool.Surface_s(arg_face.wrapped).Position().XDirection() - ) - ) + if isinstance(surface, Geom_BoundedSurface): + point = gp_Pnt() + face_x_dir = gp_Vec() + tangent_v = gp_Vec() + surface.D1(0.5, 0.5, point, face_x_dir, tangent_v) + else: + face_x_dir = surface.Position().XDirection() + self.x_dir = Vector(arg_x_dir) if arg_x_dir else Vector(face_x_dir) self.x_dir = Vector(round(i, 14) for i in self.x_dir) self.z_dir = Plane.get_topods_face_normal(arg_face.wrapped) self.z_dir = Vector(round(i, 14) for i in self.z_dir) diff --git a/tests/test_direct_api.py b/tests/test_direct_api.py index 6702b0f9..0c36a475 100644 --- a/tests/test_direct_api.py +++ b/tests/test_direct_api.py @@ -14,6 +14,7 @@ from IPython.lib import pretty from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeEdge +from OCP.BRepGProp import BRepGProp from OCP.gp import ( gp, gp_Ax1, @@ -28,6 +29,7 @@ gp_Vec, gp_XYZ, ) +from OCP.GProp import GProp_GProps from build123d.build_common import GridLocations, Locations, PolarLocations from build123d.build_enums import ( @@ -2556,6 +2558,39 @@ def test_plane_init(self): with self.assertRaises(TypeError): Plane(Edge.make_line((0, 0), (0, 1))) + # can be instantiated from planar faces of surface types other than Geom_Plane + # this loft creates the trapezoid faces of type Geom_BSplineSurface + lofted_solid = Solid.make_loft( + [ + Rectangle(3, 1).wire(), + Pos(0, 0, 1) * Rectangle(1, 1).wire(), + ] + ) + + expected = [ + # Trapezoid face, negative y coordinate + ( + Axis.X.direction, # plane x_dir + Axis.Z.direction, # plane y_dir + -Axis.Y.direction, # plane z_dir + ), + # Trapezoid face, positive y coordinate + ( + -Axis.X.direction, + Axis.Z.direction, + Axis.Y.direction, + ), + ] + # assert properties of the trapezoid faces + for i, f in enumerate(lofted_solid.faces() | Plane.XZ > Axis.Y): + p = Plane(f) + f_props = GProp_GProps() + BRepGProp.SurfaceProperties_s(f.wrapped, f_props) + self.assertVectorAlmostEquals(p.origin, f_props.CentreOfMass(), 6) + self.assertVectorAlmostEquals(p.x_dir, expected[i][0], 6) + self.assertVectorAlmostEquals(p.y_dir, expected[i][1], 6) + self.assertVectorAlmostEquals(p.z_dir, expected[i][2], 6) + def test_plane_neg(self): p = Plane( origin=(1, 2, 3),