Skip to content

Commit

Permalink
Add support for lofting faces with inner wires
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthiasJ committed Jan 2, 2024
1 parent cb5625c commit d93b42e
Showing 1 changed file with 49 additions and 43 deletions.
92 changes: 49 additions & 43 deletions src/build123d/operations_part.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"""
from __future__ import annotations
from typing import Union, Iterable
from itertools import cycle, starmap, permutations
from build123d.build_enums import Mode, Until, Kind, Side
from build123d.build_part import BuildPart
from build123d.geometry import Axis, Plane, Vector, VectorLike
Expand Down Expand Up @@ -203,54 +204,59 @@ def loft(
validate_inputs(context, "loft", section_list)

if all([s is None for s in section_list]):
if context is None or (context is not None and not context.pending_faces):
if context is None or not context.pending_faces:
raise ValueError("No sections provided")
loft_wires = [face.outer_wire() for face in context.pending_faces]
section_list = context.pending_faces
context.pending_faces = []
context.pending_face_planes = []
else:
if not any(isinstance(s, Vertex) for s in section_list):
loft_wires = [
face.outer_wire()
for section in section_list
for face in section.faces()
]
elif any(isinstance(s, Vertex) for s in section_list) and any(
isinstance(s, (Face, Sketch)) for s in section_list
):
if any(isinstance(s, Vertex) for s in section_list[1:-1]):
raise ValueError(
"Vertices must be the first, last, or first and last elements"
)
loft_wires = []
for s in section_list:
if isinstance(s, Vertex):
loft_wires.append(s)
elif isinstance(s, Face):
loft_wires.append(s.outer_wire())
elif isinstance(s, Sketch):
loft_wires.append(s.face().outer_wire())
elif all(isinstance(s, Vertex) for s in section_list):
raise ValueError(
"At least one face/sketch is required if vertices are the first, last, or first and last elements"
)

new_solid = Solid.make_loft(loft_wires, ruled)

# Try to recover an invalid loft
if not new_solid.is_valid():
new_solid = Solid.make_solid(Shell.make_shell(new_solid.faces() + section_list))
if clean:
new_solid = new_solid.clean()

section_list = [s.face() if isinstance(s, Sketch) else s for s in section_list]

is_vertex = [isinstance(s, Vertex) for s in section_list]
if any(is_vertex[1:-1]) or all(is_vertex):
raise ValueError("Vertices must be the first, last, or first and last elements and at least one face/sketch is required")

faces = [x for x, is_vertex in zip(section_list, is_vertex) if not is_vertex]
has_inner_wires = [bool(f.inner_wires()) for f in faces]

outer_wires = [s if is_vertex else s.outer_wire() for s, is_vertex in zip(section_list, is_vertex)]

if not any(has_inner_wires):
new_solid = Solid.make_loft(outer_wires, ruled)

# Try to recover an invalid loft
if not new_solid.is_valid():
raise RuntimeError("Failed to create valid loft")

if context is not None:
context._add_to_context(new_solid, clean=clean, mode=mode)
elif clean:
new_solid = new_solid.clean()
new_solid = Solid.make_solid(Shell.make_shell(new_solid.faces() + section_list))
if clean:
new_solid = new_solid.clean()
if not new_solid.is_valid():
raise RuntimeError("Failed to create valid loft")

Check warning on line 233 in src/build123d/operations_part.py

View check run for this annotation

Codecov / codecov/patch

src/build123d/operations_part.py#L229-L233

Added lines #L229 - L233 were not covered by tests

if context is not None:
context._add_to_context(new_solid, clean=clean, mode=mode)
elif clean:
new_solid = new_solid.clean()

return Part(Compound.make_compound([new_solid]).wrapped)
return Part(Compound.make_compound([new_solid]).wrapped)
else:
n_inner = len(faces[0].inner_wires())
if not all(len(f.inner_wires()) == n_inner for f in faces):
raise ValueError("All sections must have the same number of inner wires")

Check warning on line 244 in src/build123d/operations_part.py

View check run for this annotation

Codecov / codecov/patch

src/build123d/operations_part.py#L242-L244

Added lines #L242 - L244 were not covered by tests

inner_sections = [[w] for w in faces[0].inner_wires()]
for f in faces[1:]:
dist = lambda j, k: (inner_sections[j][-1].center() - f.inner_wires()[k].center()).length
groupings = starmap(zip, zip(cycle((range(n_inner), )), permutations(range(n_inner))))
groupings = [list(g) for g in groupings]
quality = [sum(starmap(dist, g)) for g in groupings]
_, groupings = zip(*sorted(zip(quality, groupings)))
for j, k in groupings[0]:
inner_sections[j].append(f.inner_wires()[k])

Check warning on line 254 in src/build123d/operations_part.py

View check run for this annotation

Codecov / codecov/patch

src/build123d/operations_part.py#L246-L254

Added lines #L246 - L254 were not covered by tests

outer_sections = [Face.make_from_wires(w) for w, is_vertex in zip(outer_wires, is_vertex)]
inner_sections = [[Face.make_from_wires(w) for w in s] for s in inner_sections]

Check warning on line 257 in src/build123d/operations_part.py

View check run for this annotation

Codecov / codecov/patch

src/build123d/operations_part.py#L256-L257

Added lines #L256 - L257 were not covered by tests

return loft(outer_sections, ruled, clean, Mode.PRIVATE) - sum((loft(s, ruled, clean, Mode.PRIVATE) for s in inner_sections), Solid())

Check warning on line 259 in src/build123d/operations_part.py

View check run for this annotation

Codecov / codecov/patch

src/build123d/operations_part.py#L259

Added line #L259 was not covered by tests


def make_brake_formed(
Expand Down

0 comments on commit d93b42e

Please sign in to comment.