mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 02:30:55 -08:00
Adding method Face.wrap_faces
This commit is contained in:
parent
2efd21ff58
commit
ccdfda88e9
2 changed files with 82 additions and 0 deletions
|
|
@ -1754,6 +1754,71 @@ class Face(Mixin2D, Shape[TopoDS_Face]):
|
|||
f"{type(planar_shape)}"
|
||||
)
|
||||
|
||||
def wrap_faces(
|
||||
self,
|
||||
faces: Iterable[Face],
|
||||
path: Wire | Edge,
|
||||
start: float = 0.0,
|
||||
) -> ShapeList[Face]:
|
||||
"""wrap_faces
|
||||
|
||||
Wrap a sequence of 2D faces onto a 3D surface, aligned along a guiding path.
|
||||
|
||||
This method places multiple planar `Face` objects (defined in the XY plane) onto a
|
||||
curved 3D surface (`self`), following a given path (Wire or Edge) that lies on or
|
||||
closely follows the surface. Each face is spaced along the path according to its
|
||||
original horizontal (X-axis) position, preserving the relative layout of the input
|
||||
faces.
|
||||
|
||||
The wrapping process attempts to maintain the shape and size of each face while
|
||||
minimizing distortion. Each face is repositioned to the origin, then individually
|
||||
wrapped onto the surface starting at a specific point along the path. The face's
|
||||
new orientation is defined using the path's tangent direction and the surface normal
|
||||
at that point.
|
||||
|
||||
This is particularly useful for placing a series of features—such as embossed logos,
|
||||
engraved labels, or patterned tiles—onto a freeform or cylindrical surface, aligned
|
||||
along a reference edge or curve.
|
||||
|
||||
Args:
|
||||
faces (Iterable[Face]): An iterable of 2D planar faces to be wrapped.
|
||||
path (Wire | Edge): A curve on the target surface that defines the alignment
|
||||
direction. The X-position of each face is mapped to a relative position
|
||||
along this path.
|
||||
start (float, optional): The relative starting point on the path (between 0.0
|
||||
and 1.0) where the first face should be placed. Defaults to 0.0.
|
||||
|
||||
Returns:
|
||||
ShapeList[Face]: A list of wrapped face objects, aligned and conformed to the
|
||||
surface.
|
||||
"""
|
||||
path_length = path.length
|
||||
|
||||
face_list = list(faces)
|
||||
first_face_min_x = face_list[0].bounding_box().min.X
|
||||
|
||||
# Position each face at the origin and wrap onto surface
|
||||
wrapped_faces: ShapeList[Face] = ShapeList()
|
||||
for face in face_list:
|
||||
bbox = face.bounding_box()
|
||||
face_center_x = (bbox.min.X + bbox.max.X) / 2
|
||||
delta_x = face_center_x - first_face_min_x
|
||||
relative_position_on_wire = start + delta_x / path_length
|
||||
path_position = path.position_at(relative_position_on_wire)
|
||||
surface_location = Location(
|
||||
Plane(
|
||||
path_position,
|
||||
x_dir=path.tangent_at(relative_position_on_wire),
|
||||
z_dir=self.normal_at(path_position),
|
||||
)
|
||||
)
|
||||
assert isinstance(face.position, Vector)
|
||||
face.position -= (delta_x, 0, 0) # Shift back to origin
|
||||
wrapped_face = Face.wrap(self, face, surface_location)
|
||||
wrapped_faces.append(wrapped_face)
|
||||
|
||||
return wrapped_faces
|
||||
|
||||
def _uv_bounds(self) -> tuple[float, float, float, float]:
|
||||
"""Return the u min, u max, v min, v max values"""
|
||||
return BRepTools.UVBounds_s(self.wrapped)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ from build123d.objects_sketch import (
|
|||
Polygon,
|
||||
Rectangle,
|
||||
RegularPolygon,
|
||||
Text,
|
||||
Triangle,
|
||||
)
|
||||
from build123d.operations_generic import fillet, offset
|
||||
|
|
@ -888,6 +889,22 @@ class TestFace(unittest.TestCase):
|
|||
with self.assertRaises(RuntimeError):
|
||||
surface.wrap(star, target)
|
||||
|
||||
def test_wrap_faces(self):
|
||||
sphere = Solid.make_sphere(50, angle1=-90).face()
|
||||
surface = sphere.face()
|
||||
path: Edge = (
|
||||
sphere.cut(
|
||||
Solid.make_cylinder(80, 100, Plane.YZ).locate(Location((-50, 0, -70)))
|
||||
)
|
||||
.edges()
|
||||
.sort_by(Axis.Z)[0]
|
||||
.reversed()
|
||||
)
|
||||
text = Text(txt="ei", font_size=15, align=(Align.MIN, Align.CENTER))
|
||||
wrapped_faces = surface.wrap_faces(text.faces(), path, 0.2)
|
||||
self.assertEqual(len(wrapped_faces), 3)
|
||||
self.assertTrue(all(not f.is_planar_face for f in wrapped_faces))
|
||||
|
||||
def test_revolve(self):
|
||||
l1 = Edge.make_line((3, 0), (3, 2))
|
||||
revolved = Face.revolve(l1, 360, Axis.Y)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue