Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plane instantiation from planar Geom_BoundedSurface faces Issue #756 #764

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions src/build123d/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@
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.GeomLib import GeomLib_IsPlanarSurface
from OCP.gp import (
gp_Ax1,
gp_Ax2,
Expand Down Expand Up @@ -2092,18 +2093,20 @@ 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):
is_surface_planar = GeomLib_IsPlanarSurface(surface, TOLERANCE).IsPlanar()
if not is_surface_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):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the special case of Geom_BoundedSurface handled here?

Copy link
Author

@dalibor-frivaldsky dalibor-frivaldsky Nov 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added test to this PR fails otherwise when trying to instantiate a Plane from the trapezoid faces created by the loft(), since the underlying kernel creates them as Geom_BSplineSurface and not Geom_Plane. Initially I thought of making the special case for Geom_BSPlineSurface specifically, but there are other surface types, like Geom_BezierSurface. These can be planar as well and it should be possible to create a Plane object from those too. The Geom_BoundedSurface is a base class of these and makes the implementation more general, that's why I opted for it.

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()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The important ability to force the x direction has been removed and needs to be returned.

Copy link
Author

@dalibor-frivaldsky dalibor-frivaldsky Nov 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that is the case, the line 2109 retains the ability to force the x direction from the argument to the Plane constructor. All the tests pass so I suppose such a regression would have been caught by them, unless I overlooked something.

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)
Expand Down
35 changes: 35 additions & 0 deletions tests/test_direct_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 (
Expand Down Expand Up @@ -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),
Expand Down