mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-15 15:20:37 -08:00
Added ability to accept iterables to: Builders, Locations, Bezier,
FilletPolyline, Line, Polyline, Spline, TangentArc, ThreePointArc, Polygon, Issue #269
This commit is contained in:
parent
b18af27e13
commit
34db0aae78
9 changed files with 194 additions and 66 deletions
|
|
@ -103,6 +103,30 @@ G = 1
|
|||
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"],
|
||||
|
|
@ -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):
|
||||
|
|
@ -1108,6 +1144,7 @@ class WorkplaneList:
|
|||
@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):
|
||||
|
|
@ -1186,7 +1223,6 @@ class WorkplaneList:
|
|||
return result
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
P = ParamSpec("P")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ class BuildLine(Builder):
|
|||
):
|
||||
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"""
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import copy
|
|||
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
|
||||
|
|
@ -90,6 +90,7 @@ class Bezier(BaseLineObject):
|
|||
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)
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
@ -367,7 +368,7 @@ class FilletPolyline(BaseLineObject):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
*pts: VectorLike,
|
||||
*pts: Union[VectorLike, Iterable[VectorLike]],
|
||||
radius: float,
|
||||
close: bool = False,
|
||||
mode: Mode = Mode.ADD,
|
||||
|
|
@ -375,6 +376,8 @@ class FilletPolyline(BaseLineObject):
|
|||
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:
|
||||
|
|
@ -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:
|
||||
|
|
@ -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")
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
@ -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")
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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:
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ license:
|
|||
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
|
||||
|
|
@ -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).
|
||||
|
|
@ -166,7 +167,7 @@ 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,
|
||||
|
|
@ -174,6 +175,7 @@ class Polygon(BaseSketchObject):
|
|||
context = BuildSketch._get_context(self)
|
||||
validate_inputs(context, self)
|
||||
|
||||
pts = flatten_sequence(*pts)
|
||||
self.pts = pts
|
||||
self.align = tuplify(align, 2)
|
||||
|
||||
|
|
@ -495,7 +497,7 @@ class SlotOverall(BaseSketchObject):
|
|||
).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)
|
||||
|
||||
|
||||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,13 @@ import logging
|
|||
from math import radians, tan
|
||||
from typing import Union, Iterable
|
||||
|
||||
from build123d.build_common import Builder, LocationList, WorkplaneList, validate_inputs
|
||||
from build123d.build_common import (
|
||||
Builder,
|
||||
LocationList,
|
||||
WorkplaneList,
|
||||
flatten_sequence,
|
||||
validate_inputs,
|
||||
)
|
||||
from build123d.build_enums import Keep, Kind, Mode, Side, Transition
|
||||
from build123d.build_line import BuildLine
|
||||
from build123d.build_part import BuildPart
|
||||
|
|
@ -206,9 +212,8 @@ def bounding_box(
|
|||
raise ValueError("objects must be provided")
|
||||
object_list = [context._obj]
|
||||
else:
|
||||
object_list = (
|
||||
[*objects] if isinstance(objects, (list, tuple, filter)) else [objects]
|
||||
)
|
||||
object_list = flatten_sequence(objects)
|
||||
|
||||
validate_inputs(context, "bounding_box", object_list)
|
||||
|
||||
if all([obj._dim == 2 for obj in object_list]):
|
||||
|
|
@ -300,9 +305,7 @@ def chamfer(
|
|||
):
|
||||
raise ValueError("No objects provided")
|
||||
|
||||
object_list = (
|
||||
[*objects] if isinstance(objects, (list, tuple, filter)) else [objects]
|
||||
)
|
||||
object_list = flatten_sequence(objects)
|
||||
|
||||
validate_inputs(context, "chamfer", object_list)
|
||||
|
||||
|
|
@ -397,9 +400,8 @@ def fillet(
|
|||
):
|
||||
raise ValueError("No objects provided")
|
||||
|
||||
object_list = (
|
||||
[*objects] if isinstance(objects, (list, tuple, filter)) else [objects]
|
||||
)
|
||||
object_list = flatten_sequence(objects)
|
||||
|
||||
validate_inputs(context, "fillet", object_list)
|
||||
if context is not None:
|
||||
target = context._obj
|
||||
|
|
@ -495,9 +497,8 @@ def mirror(
|
|||
raise ValueError("objects must be provided")
|
||||
object_list = [context._obj]
|
||||
else:
|
||||
object_list = (
|
||||
[*objects] if isinstance(objects, (list, tuple, filter)) else [objects]
|
||||
)
|
||||
object_list = flatten_sequence(objects)
|
||||
|
||||
validate_inputs(context, "mirror", object_list)
|
||||
|
||||
mirrored = [copy.deepcopy(o).mirror(about) for o in object_list]
|
||||
|
|
@ -562,9 +563,8 @@ def offset(
|
|||
raise ValueError("objects must be provided")
|
||||
object_list = [context._obj]
|
||||
else:
|
||||
object_list = (
|
||||
[*objects] if isinstance(objects, (list, tuple, filter)) else [objects]
|
||||
)
|
||||
object_list = flatten_sequence(objects)
|
||||
|
||||
validate_inputs(context, "offset", object_list)
|
||||
|
||||
edges: list[Edge] = []
|
||||
|
|
@ -701,9 +701,7 @@ def project(
|
|||
else:
|
||||
workplane = context.exit_workplanes[0]
|
||||
else:
|
||||
object_list = (
|
||||
[*objects] if isinstance(objects, (list, tuple, filter)) else [objects]
|
||||
)
|
||||
object_list = flatten_sequence(objects)
|
||||
|
||||
# The size of the object determines the size of the target projection screen
|
||||
# as the screen is normal to the direction of parallel projection
|
||||
|
|
@ -826,9 +824,8 @@ def scale(
|
|||
raise ValueError("objects must be provided")
|
||||
object_list = [context._obj]
|
||||
else:
|
||||
object_list = (
|
||||
[*objects] if isinstance(objects, (list, tuple, filter)) else [objects]
|
||||
)
|
||||
object_list = flatten_sequence(objects)
|
||||
|
||||
validate_inputs(context, "scale", object_list)
|
||||
|
||||
if isinstance(by, (int, float)):
|
||||
|
|
@ -907,9 +904,8 @@ def split(
|
|||
raise ValueError("objects must be provided")
|
||||
object_list = [context._obj]
|
||||
else:
|
||||
object_list = (
|
||||
[*objects] if isinstance(objects, (list, tuple, filter)) else [objects]
|
||||
)
|
||||
object_list = flatten_sequence(objects)
|
||||
|
||||
validate_inputs(context, "split", object_list)
|
||||
|
||||
new_objects = []
|
||||
|
|
|
|||
|
|
@ -44,13 +44,18 @@ from build123d.topology import (
|
|||
Vertex,
|
||||
)
|
||||
|
||||
from build123d.build_common import logger, WorkplaneList, validate_inputs
|
||||
from build123d.build_common import (
|
||||
logger,
|
||||
WorkplaneList,
|
||||
flatten_sequence,
|
||||
validate_inputs,
|
||||
)
|
||||
|
||||
|
||||
def extrude(
|
||||
to_extrude: Union[Face, Sketch] = None,
|
||||
amount: float = None,
|
||||
dir: VectorLike = None, # pylint: disable=redefined-builtin
|
||||
dir: VectorLike = None, # pylint: disable=redefined-builtin
|
||||
until: Until = None,
|
||||
target: Union[Compound, Solid] = None,
|
||||
both: bool = False,
|
||||
|
|
@ -193,9 +198,7 @@ def loft(
|
|||
"""
|
||||
context: BuildPart = BuildPart._get_context("loft")
|
||||
|
||||
section_list = (
|
||||
[*sections] if isinstance(sections, (list, tuple, filter)) else [sections]
|
||||
)
|
||||
section_list = flatten_sequence(sections)
|
||||
validate_inputs(context, "loft", section_list)
|
||||
|
||||
if all([s is None for s in section_list]):
|
||||
|
|
@ -411,9 +414,8 @@ def revolve(
|
|||
"""
|
||||
context: BuildPart = BuildPart._get_context("revolve")
|
||||
|
||||
profile_list = (
|
||||
[*profiles] if isinstance(profiles, (list, tuple, filter)) else [profiles]
|
||||
)
|
||||
profile_list = flatten_sequence(profiles)
|
||||
|
||||
validate_inputs(context, "revolve", profile_list)
|
||||
|
||||
# Make sure we account for users specifying angles larger than 360 degrees, and
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ from __future__ import annotations
|
|||
from typing import Iterable, Union
|
||||
from build123d.build_enums import Mode
|
||||
from build123d.topology import Compound, Curve, Edge, Face, ShapeList, Wire, Sketch
|
||||
from build123d.build_common import validate_inputs
|
||||
from build123d.build_common import flatten_sequence, validate_inputs
|
||||
from build123d.build_sketch import BuildSketch
|
||||
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ def make_face(
|
|||
context: BuildSketch = BuildSketch._get_context("make_face")
|
||||
|
||||
if edges is not None:
|
||||
outer_edges = [*edges] if isinstance(edges, (list, tuple, filter)) else [edges]
|
||||
outer_edges = flatten_sequence(edges)
|
||||
elif context is not None:
|
||||
outer_edges = context.pending_edges
|
||||
else:
|
||||
|
|
@ -84,7 +84,7 @@ def make_hull(
|
|||
context: BuildSketch = BuildSketch._get_context("make_hull")
|
||||
|
||||
if edges is not None:
|
||||
hull_edges = [*edges] if isinstance(edges, (list, tuple, filter)) else [edges]
|
||||
hull_edges = flatten_sequence(edges)
|
||||
elif context is not None:
|
||||
hull_edges = context.pending_edges
|
||||
if context.sketch_local is not None:
|
||||
|
|
@ -131,7 +131,7 @@ def trace(
|
|||
context: BuildSketch = BuildSketch._get_context("trace")
|
||||
|
||||
if lines is not None:
|
||||
trace_lines = [*lines] if isinstance(lines, (list, tuple, filter)) else [lines]
|
||||
trace_lines = flatten_sequence(lines)
|
||||
trace_edges = [e for l in trace_lines for e in l.edges()]
|
||||
elif context is not None:
|
||||
trace_edges = context.pending_edges
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ license:
|
|||
import unittest
|
||||
from math import pi
|
||||
from build123d import *
|
||||
from build123d import Builder, WorkplaneList, LocationList
|
||||
from build123d import WorkplaneList, flatten_sequence
|
||||
|
||||
|
||||
def _assertTupleAlmostEquals(self, expected, actual, places, msg=None):
|
||||
|
|
@ -40,6 +40,38 @@ def _assertTupleAlmostEquals(self, expected, actual, places, msg=None):
|
|||
unittest.TestCase.assertTupleAlmostEquals = _assertTupleAlmostEquals
|
||||
|
||||
|
||||
class TestFlattenSequence(unittest.TestCase):
|
||||
"""Test the flatten_sequence helper function"""
|
||||
|
||||
def test_single_object(self):
|
||||
self.assertListEqual(flatten_sequence("a"), ["a"])
|
||||
|
||||
def test_sequence(self):
|
||||
self.assertListEqual(flatten_sequence("a", "b", "c"), ["a", "b", "c"])
|
||||
|
||||
def test_list(self):
|
||||
self.assertListEqual(flatten_sequence(["a", "b", "c"]), ["a", "b", "c"])
|
||||
|
||||
def test_list_sequence(self):
|
||||
self.assertListEqual(
|
||||
flatten_sequence(["a", "b", "c"], "d"), ["a", "b", "c", "d"]
|
||||
)
|
||||
|
||||
def test_sequence_tuple(self):
|
||||
self.assertListEqual(
|
||||
flatten_sequence("a", ("b", "c", "d"), "e"), ["a", "b", "c", "d", "e"]
|
||||
)
|
||||
|
||||
def test_points(self):
|
||||
self.assertListEqual(
|
||||
flatten_sequence("a", (1, 2, 3), "e"), ["a", (1, 2, 3), "e"]
|
||||
)
|
||||
|
||||
self.assertListEqual(
|
||||
flatten_sequence("a", (1.0, 2.0, 3.0), "e"), ["a", (1.0, 2.0, 3.0), "e"]
|
||||
)
|
||||
|
||||
|
||||
class TestBuilder(unittest.TestCase):
|
||||
"""Test the Builder base class"""
|
||||
|
||||
|
|
@ -123,6 +155,18 @@ class TestBuilder(unittest.TestCase):
|
|||
with self.assertWarns(UserWarning):
|
||||
p.solid()
|
||||
|
||||
def test_workplanes_as_list(self):
|
||||
with BuildPart() as p:
|
||||
Box(1, 1, 1)
|
||||
with BuildSketch(p.faces() >> Axis.Z):
|
||||
Rectangle(0.25, 0.25)
|
||||
extrude(amount=0.25)
|
||||
self.assertAlmostEqual(p.part.volume, 1**3 + 0.25**3, 5)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
with BuildLine([Plane.XY, Plane.XZ]):
|
||||
Line((0, 0), (1, 1))
|
||||
|
||||
|
||||
class TestBuilderExit(unittest.TestCase):
|
||||
def test_multiple(self):
|
||||
|
|
@ -306,6 +350,22 @@ class TestLocations(unittest.TestCase):
|
|||
self.assertTupleAlmostEquals(grid.min.to_tuple(), (-5, -15, 0), 5)
|
||||
self.assertTupleAlmostEquals(grid.max.to_tuple(), (5, 15, 0), 5)
|
||||
|
||||
def test_mixed_sequence_list(self):
|
||||
locs = Locations((0, 1), [(2, 3), (4, 5)], (6, 7))
|
||||
self.assertEqual(len(locs.locations), 4)
|
||||
self.assertTupleAlmostEquals(
|
||||
locs.locations[0].position.to_tuple(), (0, 1, 0), 5
|
||||
)
|
||||
self.assertTupleAlmostEquals(
|
||||
locs.locations[1].position.to_tuple(), (2, 3, 0), 5
|
||||
)
|
||||
self.assertTupleAlmostEquals(
|
||||
locs.locations[2].position.to_tuple(), (4, 5, 0), 5
|
||||
)
|
||||
self.assertTupleAlmostEquals(
|
||||
locs.locations[3].position.to_tuple(), (6, 7, 0), 5
|
||||
)
|
||||
|
||||
|
||||
class TestProperties(unittest.TestCase):
|
||||
def test_vector_properties(self):
|
||||
|
|
@ -609,9 +669,6 @@ class TestValidateInputs(unittest.TestCase):
|
|||
with BuildPart() as p:
|
||||
Box(1, 1, 1)
|
||||
fillet(4, radius=1)
|
||||
self.assertEqual(
|
||||
"fillet doesn't accept int, did you intend <keyword>=4?", str(rte.exception)
|
||||
)
|
||||
|
||||
|
||||
class TestVectorExtensions(unittest.TestCase):
|
||||
|
|
@ -695,20 +752,20 @@ class TestWorkplaneStorage(unittest.TestCase):
|
|||
class TestContextAwareSelectors(unittest.TestCase):
|
||||
def test_context_aware_selectors(self):
|
||||
with BuildPart() as p:
|
||||
Box(1,1,1)
|
||||
Box(1, 1, 1)
|
||||
self.assertEqual(solids(), p.solids())
|
||||
self.assertEqual(faces(), p.faces())
|
||||
self.assertEqual(wires(), p.wires())
|
||||
self.assertEqual(edges(), p.edges())
|
||||
self.assertEqual(vertices(), p.vertices())
|
||||
with BuildSketch() as p:
|
||||
Rectangle(1,1)
|
||||
Rectangle(1, 1)
|
||||
self.assertEqual(faces(), p.faces())
|
||||
self.assertEqual(wires(), p.wires())
|
||||
self.assertEqual(edges(), p.edges())
|
||||
self.assertEqual(vertices(), p.vertices())
|
||||
with BuildLine() as p:
|
||||
Line((0,0), (1,0))
|
||||
Line((0, 0), (1, 0))
|
||||
self.assertEqual(edges(), p.edges())
|
||||
self.assertEqual(vertices(), p.vertices())
|
||||
with BuildSketch() as p:
|
||||
|
|
|
|||
|
|
@ -280,6 +280,21 @@ class BuildLineTests(unittest.TestCase):
|
|||
self.assertEqual(len(test.edges()), 4)
|
||||
self.assertAlmostEqual(test.wires()[0].length, 4)
|
||||
|
||||
def test_polyline_with_list(self):
|
||||
"""Test edge generation and close"""
|
||||
with BuildLine() as test:
|
||||
Polyline((0, 0), [(1, 0), (1, 1)], (0, 1), close=True)
|
||||
self.assertAlmostEqual(
|
||||
(test.edges()[0] @ 0 - test.edges()[-1] @ 1).length, 0, 5
|
||||
)
|
||||
self.assertEqual(len(test.edges()), 4)
|
||||
self.assertAlmostEqual(test.wires()[0].length, 4)
|
||||
|
||||
def test_line_with_list(self):
|
||||
"""Test line with a list of points"""
|
||||
l = Line([(0, 0), (10, 0)])
|
||||
self.assertAlmostEqual(l.length, 10, 5)
|
||||
|
||||
def test_wires_select_last(self):
|
||||
with BuildLine() as test:
|
||||
Line((0, 0), (0, 1))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue