Skip to content

Commit

Permalink
Added Face.is_planar property Issue #756
Browse files Browse the repository at this point in the history
  • Loading branch information
gumyr committed Nov 4, 2024
1 parent d8d841e commit c7a8529
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 9 deletions.
24 changes: 15 additions & 9 deletions src/build123d/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
GeomFill_Frenet,
GeomFill_TrihedronLaw,
)
from OCP.GeomLib import GeomLib_IsPlanarSurface
from OCP.gp import (
gp_Ax1,
gp_Ax2,
Expand Down Expand Up @@ -3447,7 +3448,7 @@ def filter_by(
# could be moved out maybe?
def axis_parallel_predicate(axis: Axis, tolerance: float):
def pred(shape: Shape):
if isinstance(shape, Face) and shape.geom_type == GeomType.PLANE:
if isinstance(shape, Face) and shape.is_planar:
shape_axis = Axis(shape.center(), shape.normal_at(None))
elif isinstance(shape, Edge) and shape.geom_type == GeomType.LINE:
shape_axis = Axis(shape.position_at(0), shape.tangent_at(0))
Expand All @@ -3462,7 +3463,7 @@ def plane_parallel_predicate(plane: Plane, tolerance: float):
plane_xyz = plane.z_dir.wrapped.XYZ()

def pred(shape: Shape):
if isinstance(shape, Face) and shape.geom_type == GeomType.PLANE:
if isinstance(shape, Face) and shape.is_planar:
shape_axis = Axis(shape.center(), shape.normal_at(None))
return plane_axis.is_parallel(shape_axis, tolerance)
if isinstance(shape, Wire):
Expand Down Expand Up @@ -5601,7 +5602,7 @@ def __init__(self, *args, **kwargs):
def length(self) -> float:
"""length of planar face"""
result = None
if self.geom_type == GeomType.PLANE:
if self.is_planar:
# Reposition on Plane.XY
flat_face = Plane(self).to_local_coords(self)
face_vertices = flat_face.vertices().sort_by(Axis.X)
Expand All @@ -5617,7 +5618,7 @@ def volume(self) -> float:
def width(self) -> float:
"""width of planar face"""
result = None
if self.geom_type == GeomType.PLANE:
if self.is_planar:
# Reposition on Plane.XY
flat_face = Plane(self).to_local_coords(self)
face_vertices = flat_face.vertices().sort_by(Axis.Y)
Expand All @@ -5628,7 +5629,7 @@ def width(self) -> float:
def geometry(self) -> str:
"""geometry of planar face"""
result = None
if self.geom_type == GeomType.PLANE:
if self.is_planar:
flat_face = Plane(self).to_local_coords(self)
flat_face_edges = flat_face.edges()
if all([e.geom_type == GeomType.LINE for e in flat_face_edges]):
Expand Down Expand Up @@ -5661,6 +5662,13 @@ def center_location(self) -> Location:
origin = self.position_at(0.5, 0.5)
return Plane(origin, z_dir=self.normal_at(origin)).location

@property
def is_planar(face: Face) -> bool:
"""Is the face planar even though its geom_type may not be PLANE"""
surface = BRep_Tool.Surface_s(face.wrapped)
is_face_planar = GeomLib_IsPlanarSurface(surface, TOLERANCE)
return is_face_planar.IsPlanar()

def _geom_adaptor(self) -> Geom_Surface:
""" """
return BRep_Tool.Surface_s(self.wrapped)
Expand Down Expand Up @@ -5810,7 +5818,7 @@ def center(self, center_of=CenterOf.GEOMETRY) -> Vector:
Vector: center
"""
if (center_of == CenterOf.MASS) or (
center_of == CenterOf.GEOMETRY and self.geom_type == GeomType.PLANE
center_of == CenterOf.GEOMETRY and self.is_planar
):
properties = GProp_GProps()
BRepGProp.SurfaceProperties_s(self.wrapped, properties)
Expand Down Expand Up @@ -7447,9 +7455,7 @@ def extrude_until(
clip_faces = [
f
for f in faces
if not (
f.geom_type == GeomType.PLANE and f.normal_at().dot(direction) == 0.0
)
if not (f.is_planar and f.normal_at().dot(direction) == 0.0)
]
if not clip_faces:
raise ValueError("provided face does not intersect target_object")
Expand Down
15 changes: 15 additions & 0 deletions tests/test_direct_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,21 @@ def test_geometry(self):
extrude(amount=1)
self.assertEqual(test.faces().sort_by(Axis.Z).last.geometry, "POLYGON")

def test_is_planar(self):
self.assertTrue(Face.make_rect(1, 1).is_planar)
self.assertFalse(
Solid.make_cylinder(1, 1).faces().filter_by(GeomType.CYLINDER)[0].is_planar
)
# Some of these faces have geom_type BSPLINE but are planar
mount = Solid.make_loft(
[
Rectangle((1 + 16 + 4), 20, align=(Align.MIN, Align.CENTER)).wire(),
Pos(1, 0, 4)
* Rectangle(16, 20, align=(Align.MIN, Align.CENTER)).wire(),
],
)
self.assertTrue(all(f.is_planar for f in mount.faces()))

def test_negate(self):
square = Face.make_rect(1, 1)
self.assertVectorAlmostEquals(square.normal_at(), (0, 0, 1), 5)
Expand Down

0 comments on commit c7a8529

Please sign in to comment.