Skip to content

Commit

Permalink
Added ability to accept iterables to: Builders, Locations, Bezier,
Browse files Browse the repository at this point in the history
FilletPolyline, Line, Polyline, Spline, TangentArc, ThreePointArc,
Polygon, Issue #269
  • Loading branch information
gumyr committed Nov 29, 2023
1 parent b18af27 commit 34db0aa
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 66 deletions.
44 changes: 40 additions & 4 deletions src/build123d/build_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,30 @@
KG = 1000 * G
LB = 453.59237 * G

T = TypeVar("T")


def flatten_sequence(*obj: T) -> list[T]:
"""Convert a sequence of object potentially containing iterables into a flat list"""

def is_point(obj):
"""Identify points as tuples of numbers"""
return isinstance(obj, tuple) and all(
isinstance(item, (int, float)) for item in obj
)

flat_list = []
for item in obj:
# Note: an Iterable can't be used here as it will match with Vector & Vertex
# and break them into a list of floats.
if isinstance(item, (list, tuple, filter, set)) and not is_point(item):
flat_list.extend(item)
else:
flat_list.append(item)

return flat_list


operations_apply_to = {
"add": ["BuildPart", "BuildSketch", "BuildLine"],
"bounding_box": ["BuildPart", "BuildSketch", "BuildLine"],
Expand Down Expand Up @@ -942,16 +966,28 @@ class Locations(LocationList):
Creates a context of locations for Part or Sketch
Args:
pts (Union[VectorLike, Vertex, Location]): sequence of points to push
pts (Union[VectorLike, Vertex, Location, Face, Plane, Axis] or iterable of same):
sequence of points to push
Attributes:
local_locations (list{Location}): locations relative to workplane
"""

def __init__(self, *pts: Union[VectorLike, Vertex, Location, Face, Plane, Axis]):
def __init__(
self,
*pts: Union[
VectorLike,
Vertex,
Location,
Face,
Plane,
Axis,
Iterable[VectorLike, Vertex, Location, Face, Plane, Axis],
],
):
local_locations = []
for point in pts:
for point in flatten_sequence(*pts):
if isinstance(point, Location):
local_locations.append(point)
elif isinstance(point, Vector):
Expand Down Expand Up @@ -1108,6 +1144,7 @@ def __init__(self, *workplanes: Union[Face, Plane, Location]):
@staticmethod
def _convert_to_planes(objs: Iterable[Union[Face, Plane, Location]]) -> list[Plane]:
"""Translate objects to planes"""
objs = flatten_sequence(*objs)
planes = []
for obj in objs:
if isinstance(obj, Plane):
Expand Down Expand Up @@ -1186,7 +1223,6 @@ def localize(cls, *points: VectorLike) -> Union[list[Vector], Vector]:
return result


T = TypeVar("T")
P = ParamSpec("P")


Expand Down
2 changes: 2 additions & 0 deletions src/build123d/build_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ def __init__(
):
self.line: Curve = None
super().__init__(workplane, mode=mode)
if len(self.workplanes) > 1:
raise ValueError("BuildLine only accepts one workplane")

def __exit__(self, exception_type, exception_value, traceback):
"""Upon exiting restore context and send object to parent"""
Expand Down
43 changes: 30 additions & 13 deletions src/build123d/objects_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from math import copysign, cos, radians, sin, sqrt
from typing import Iterable, Union

from build123d.build_common import WorkplaneList, validate_inputs
from build123d.build_common import WorkplaneList, flatten_sequence, validate_inputs
from build123d.build_enums import AngularDirection, GeomType, LengthMode, Mode
from build123d.build_line import BuildLine
from build123d.geometry import Axis, Plane, Vector, VectorLike
Expand Down Expand Up @@ -90,6 +90,7 @@ def __init__(
context: BuildLine = BuildLine._get_context(self)
validate_inputs(context, self)

cntl_pnts = flatten_sequence(*cntl_pnts)
polls = WorkplaneList.localize(*cntl_pnts)
curve = Edge.make_bezier(*polls, weights=weights)

Expand Down Expand Up @@ -353,7 +354,7 @@ class FilletPolyline(BaseLineObject):
are filleted to a given radius.
Args:
pts (VectorLike): sequence of three or more points
pts (Union[VectorLike, Iterable[VectorLike]]): sequence of three or more points
radius (float): radius of filleted corners
close (bool, optional): close by generating an extra Edge. Defaults to False.
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
Expand All @@ -367,14 +368,16 @@ class FilletPolyline(BaseLineObject):

def __init__(
self,
*pts: VectorLike,
*pts: Union[VectorLike, Iterable[VectorLike]],
radius: float,
close: bool = False,
mode: Mode = Mode.ADD,
):
context: BuildLine = BuildLine._get_context(self)
validate_inputs(context, self)

pts = flatten_sequence(*pts)

if len(pts) < 3:
raise ValueError("filletpolyline requires three or more pts")
if radius <= 0:
Expand Down Expand Up @@ -506,7 +509,7 @@ class Line(BaseLineObject):
Add a straight line defined by two end points.
Args:
pts (VectorLike): sequence of two points
pts (Union[VectorLike, Iterable[VectorLike]]): sequence of two points
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
Raises:
Expand All @@ -515,7 +518,10 @@ class Line(BaseLineObject):

_applies_to = [BuildLine._tag]

def __init__(self, *pts: VectorLike, mode: Mode = Mode.ADD):
def __init__(
self, *pts: Union[VectorLike, Iterable[VectorLike]], mode: Mode = Mode.ADD
):
pts = flatten_sequence(*pts)
if len(pts) != 2:
raise ValueError("Line requires two pts")

Expand Down Expand Up @@ -637,7 +643,7 @@ class Polyline(BaseLineObject):
Add a sequence of straight lines defined by successive point pairs.
Args:
pts (VectorLike): sequence of three or more points
pts (Union[VectorLike, Iterable[VectorLike]]): sequence of three or more points
close (bool, optional): close by generating an extra Edge. Defaults to False.
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
Expand All @@ -647,10 +653,16 @@ class Polyline(BaseLineObject):

_applies_to = [BuildLine._tag]

def __init__(self, *pts: VectorLike, close: bool = False, mode: Mode = Mode.ADD):
def __init__(
self,
*pts: Union[VectorLike, Iterable[VectorLike]],
close: bool = False,
mode: Mode = Mode.ADD,
):
context: BuildLine = BuildLine._get_context(self)
validate_inputs(context, self)

pts = flatten_sequence(*pts)
if len(pts) < 3:
raise ValueError("polyline requires three or more pts")

Expand Down Expand Up @@ -766,7 +778,7 @@ class Spline(BaseLineObject):
Add a spline through the provided points optionally constrained by tangents.
Args:
pts (VectorLike): sequence of two or more points
pts (Union[VectorLike, Iterable[VectorLike]]): sequence of two or more points
tangents (Iterable[VectorLike], optional): tangents at end points. Defaults to None.
tangent_scalars (Iterable[float], optional): change shape by amplifying tangent.
Defaults to None.
Expand All @@ -778,12 +790,13 @@ class Spline(BaseLineObject):

def __init__(
self,
*pts: VectorLike,
*pts: Union[VectorLike, Iterable[VectorLike]],
tangents: Iterable[VectorLike] = None,
tangent_scalars: Iterable[float] = None,
periodic: bool = False,
mode: Mode = Mode.ADD,
):
pts = flatten_sequence(*pts)
context: BuildLine = BuildLine._get_context(self)
validate_inputs(context, self)

Expand Down Expand Up @@ -821,7 +834,7 @@ class TangentArc(BaseLineObject):
Add an arc defined by two points and a tangent.
Args:
pts (VectorLike): sequence of two points
pts (Union[VectorLike, Iterable[VectorLike]]): sequence of two points
tangent (VectorLike): tangent to constrain arc
tangent_from_first (bool, optional): apply tangent to first point. Note, applying
tangent to end point will flip the orientation of the arc. Defaults to True.
Expand All @@ -835,11 +848,12 @@ class TangentArc(BaseLineObject):

def __init__(
self,
*pts: VectorLike,
*pts: Union[VectorLike, Iterable[VectorLike]],
tangent: VectorLike,
tangent_from_first: bool = True,
mode: Mode = Mode.ADD,
):
pts = flatten_sequence(*pts)
context: BuildLine = BuildLine._get_context(self)
validate_inputs(context, self)

Expand All @@ -862,7 +876,7 @@ class ThreePointArc(BaseLineObject):
Add an arc generated by three points.
Args:
pts (VectorLike): sequence of three points
pts (Union[VectorLike, Iterable[VectorLike]]): sequence of three points
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
Raises:
Expand All @@ -871,10 +885,13 @@ class ThreePointArc(BaseLineObject):

_applies_to = [BuildLine._tag]

def __init__(self, *pts: VectorLike, mode: Mode = Mode.ADD):
def __init__(
self, *pts: Union[VectorLike, Iterable[VectorLike]], mode: Mode = Mode.ADD
):
context: BuildLine = BuildLine._get_context(self)
validate_inputs(context, self)

pts = flatten_sequence(*pts)
if len(pts) != 3:
raise ValueError("ThreePointArc requires three points")
points = WorkplaneList.localize(*pts)
Expand Down
13 changes: 8 additions & 5 deletions src/build123d/objects_sketch.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
from __future__ import annotations

from math import cos, pi, radians, sin, tan
from typing import Union
from typing import Iterable, Union

from build123d.build_common import LocationList, validate_inputs
from build123d.build_common import LocationList, flatten_sequence, validate_inputs
from build123d.build_enums import Align, FontStyle, Mode
from build123d.build_sketch import BuildSketch
from build123d.geometry import Axis, Location, Rotation, Vector, VectorLike
Expand Down Expand Up @@ -155,7 +155,8 @@ class Polygon(BaseSketchObject):
Add polygon(s) defined by given sequence of points to sketch.
Args:
pts (VectorLike): sequence of points defining the vertices of polygon
pts (Union[VectorLike, Iterable[VectorLike]]): sequence of points defining the
vertices of polygon
rotation (float, optional): angles to rotate objects. Defaults to 0.
align (Union[Align, tuple[Align, Align]], optional): align min, center, or max of object.
Defaults to (Align.CENTER, Align.CENTER).
Expand All @@ -166,14 +167,15 @@ class Polygon(BaseSketchObject):

def __init__(
self,
*pts: VectorLike,
*pts: Union[VectorLike, Iterable[VectorLike]],
rotation: float = 0,
align: Union[Align, tuple[Align, Align]] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
context = BuildSketch._get_context(self)
validate_inputs(context, self)

pts = flatten_sequence(*pts)
self.pts = pts
self.align = tuplify(align, 2)

Expand Down Expand Up @@ -495,7 +497,7 @@ def __init__(
).offset_2d(height / 2)
)
else:
face = Circle(width/2, mode=mode).face()
face = Circle(width / 2, mode=mode).face()
super().__init__(face, rotation, align, mode)


Expand All @@ -518,6 +520,7 @@ class Text(BaseSketchObject):
rotation (float, optional): angles to rotate objects. Defaults to 0.
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""

# pylint: disable=too-many-instance-attributes
_applies_to = [BuildSketch._tag]

Expand Down
Loading

0 comments on commit 34db0aa

Please sign in to comment.