mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-05 18:20:46 -08:00
Enhanced make_face so faces can have holes. Added BoundBox.measure
This commit is contained in:
parent
e6d272b2fa
commit
a5e95fe72f
4 changed files with 42 additions and 10 deletions
|
|
@ -41,7 +41,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
from collections.abc import Callable, Iterable, Sequence
|
from collections.abc import Callable, Iterable, Sequence
|
||||||
from math import degrees, isclose, log10, pi, radians
|
from math import degrees, isclose, log10, pi, radians, prod
|
||||||
from typing import TYPE_CHECKING, Any, TypeAlias, overload
|
from typing import TYPE_CHECKING, Any, TypeAlias, overload
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
@ -1001,6 +1001,16 @@ class BoundBox:
|
||||||
self.max = Vector(x_max, y_max, z_max) #: location of maximum corner
|
self.max = Vector(x_max, y_max, z_max) #: location of maximum corner
|
||||||
self.size = Vector(x_max - x_min, y_max - y_min, z_max - z_min) #: overall size
|
self.size = Vector(x_max - x_min, y_max - y_min, z_max - z_min) #: overall size
|
||||||
|
|
||||||
|
@property
|
||||||
|
def measure(self) -> float:
|
||||||
|
"""Return the overall Lebesgue measure of the bounding box.
|
||||||
|
|
||||||
|
- For 1D objects: length
|
||||||
|
- For 2D objects: area
|
||||||
|
- For 3D objects: volume
|
||||||
|
"""
|
||||||
|
return prod([x for x in self.size if x > TOLERANCE])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def diagonal(self) -> float:
|
def diagonal(self) -> float:
|
||||||
"""body diagonal length (i.e. object maximum size)"""
|
"""body diagonal length (i.e. object maximum size)"""
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ from build123d.topology import (
|
||||||
Sketch,
|
Sketch,
|
||||||
topo_explore_connected_edges,
|
topo_explore_connected_edges,
|
||||||
topo_explore_common_vertex,
|
topo_explore_common_vertex,
|
||||||
|
edges_to_wires,
|
||||||
)
|
)
|
||||||
from build123d.geometry import Plane, Vector, TOLERANCE
|
from build123d.geometry import Plane, Vector, TOLERANCE
|
||||||
from build123d.build_common import flatten_sequence, validate_inputs
|
from build123d.build_common import flatten_sequence, validate_inputs
|
||||||
|
|
@ -200,26 +201,33 @@ def make_face(
|
||||||
) -> Sketch:
|
) -> Sketch:
|
||||||
"""Sketch Operation: make_face
|
"""Sketch Operation: make_face
|
||||||
|
|
||||||
Create a face from the given perimeter edges.
|
Create a face from the given edges.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
edges (Edge): sequence of perimeter edges. Defaults to all
|
edges (Edge): sequence of edges. Defaults to all sketch pending edges.
|
||||||
sketch pending edges.
|
|
||||||
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
|
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
|
||||||
"""
|
"""
|
||||||
context: BuildSketch | None = BuildSketch._get_context("make_face")
|
context: BuildSketch | None = BuildSketch._get_context("make_face")
|
||||||
|
|
||||||
if edges is not None:
|
if edges is not None:
|
||||||
outer_edges = flatten_sequence(edges)
|
raw_edges = flatten_sequence(edges)
|
||||||
elif context is not None:
|
elif context is not None:
|
||||||
outer_edges = context.pending_edges
|
raw_edges = context.pending_edges
|
||||||
else:
|
else:
|
||||||
raise ValueError("No objects to create a face")
|
raise ValueError("No objects to create a face")
|
||||||
if not outer_edges:
|
if not raw_edges:
|
||||||
raise ValueError("No objects to create a hull")
|
raise ValueError("No objects to create a face")
|
||||||
validate_inputs(context, "make_face", outer_edges)
|
validate_inputs(context, "make_face", raw_edges)
|
||||||
|
|
||||||
pending_face = Face(Wire.combine(outer_edges)[0])
|
wires = list(
|
||||||
|
edges_to_wires(raw_edges).sort_by(
|
||||||
|
lambda w: w.bounding_box().measure, reverse=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if len(wires) > 1:
|
||||||
|
pending_face = Face(wires[0], wires[1:])
|
||||||
|
else:
|
||||||
|
pending_face = Face(wires[0])
|
||||||
if pending_face.normal_at().Z < 0: # flip up-side-down faces
|
if pending_face.normal_at().Z < 0: # flip up-side-down faces
|
||||||
pending_face = -pending_face
|
pending_face = -pending_face
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,17 @@ class TestUpSideDown(unittest.TestCase):
|
||||||
sketch = make_face(wire.edges())
|
sketch = make_face(wire.edges())
|
||||||
self.assertTrue(sketch.faces()[0].normal_at().Z > 0)
|
self.assertTrue(sketch.faces()[0].normal_at().Z > 0)
|
||||||
|
|
||||||
|
def test_make_face_with_holes(self):
|
||||||
|
with BuildSketch() as skt:
|
||||||
|
with BuildLine() as perimeter:
|
||||||
|
CenterArc((0, 0), 3, 0, 360)
|
||||||
|
with BuildLine() as hole1:
|
||||||
|
Polyline((-1, 1), (1, 1), (1, 2), (-1, 2), (-1, 1))
|
||||||
|
with BuildLine() as hole2:
|
||||||
|
Airfoil("4020")
|
||||||
|
make_face()
|
||||||
|
self.assertEqual(len(skt.face().inner_wires()), 2)
|
||||||
|
|
||||||
|
|
||||||
class TestBuildSketchExceptions(unittest.TestCase):
|
class TestBuildSketchExceptions(unittest.TestCase):
|
||||||
"""Test exception handling"""
|
"""Test exception handling"""
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ class TestBoundBox(unittest.TestCase):
|
||||||
|
|
||||||
# OCC uses some approximations
|
# OCC uses some approximations
|
||||||
self.assertAlmostEqual(bb1.size.X, 1.0, 1)
|
self.assertAlmostEqual(bb1.size.X, 1.0, 1)
|
||||||
|
self.assertAlmostEqual(bb1.measure, 1.0, 5)
|
||||||
|
|
||||||
# Test adding to an existing bounding box
|
# Test adding to an existing bounding box
|
||||||
v0 = Vertex(0, 0, 0)
|
v0 = Vertex(0, 0, 0)
|
||||||
|
|
@ -50,6 +51,7 @@ class TestBoundBox(unittest.TestCase):
|
||||||
|
|
||||||
bb3 = bb1.add(bb2)
|
bb3 = bb1.add(bb2)
|
||||||
self.assertAlmostEqual(bb3.size, (2, 2, 2), 7)
|
self.assertAlmostEqual(bb3.size, (2, 2, 2), 7)
|
||||||
|
self.assertAlmostEqual(bb3.measure, 8, 5)
|
||||||
|
|
||||||
bb3 = bb2.add((3, 3, 3))
|
bb3 = bb2.add((3, 3, 3))
|
||||||
self.assertAlmostEqual(bb3.size, (3, 3, 3), 7)
|
self.assertAlmostEqual(bb3.size, (3, 3, 3), 7)
|
||||||
|
|
@ -61,6 +63,7 @@ class TestBoundBox(unittest.TestCase):
|
||||||
bb1 = Vertex(1, 1, 0).bounding_box().add(Vertex(2, 2, 0).bounding_box())
|
bb1 = Vertex(1, 1, 0).bounding_box().add(Vertex(2, 2, 0).bounding_box())
|
||||||
bb2 = Vertex(0, 0, 0).bounding_box().add(Vertex(3, 3, 0).bounding_box())
|
bb2 = Vertex(0, 0, 0).bounding_box().add(Vertex(3, 3, 0).bounding_box())
|
||||||
bb3 = Vertex(0, 0, 0).bounding_box().add(Vertex(1.5, 1.5, 0).bounding_box())
|
bb3 = Vertex(0, 0, 0).bounding_box().add(Vertex(1.5, 1.5, 0).bounding_box())
|
||||||
|
self.assertAlmostEqual(bb2.measure, 9, 5)
|
||||||
# Test that bb2 contains bb1
|
# Test that bb2 contains bb1
|
||||||
self.assertEqual(bb2, BoundBox.find_outside_box_2d(bb1, bb2))
|
self.assertEqual(bb2, BoundBox.find_outside_box_2d(bb1, bb2))
|
||||||
self.assertEqual(bb2, BoundBox.find_outside_box_2d(bb2, bb1))
|
self.assertEqual(bb2, BoundBox.find_outside_box_2d(bb2, bb1))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue