Eliminated named from Plane & Literal parameters

This commit is contained in:
Roger Maitland 2022-10-16 11:57:50 -04:00
parent e24a7c060d
commit c7ca0bdf34
24 changed files with 470 additions and 929 deletions

View file

@ -2,19 +2,34 @@ import build123d as b
class MyRectangle(b.BaseSketchOperation):
def __init__(self, width: float, height: float, rotation: float = 0, centered: tuple[bool, bool] = (True, True), mode: b.Mode = b.Mode.ADD):
face = b.Face.makePlane(height, width)
def __init__(
self,
width: float,
height: float,
rotation: float = 0,
centered: tuple[bool, bool] = (True, True),
mode: b.Mode = b.Mode.ADD,
):
face = b.Face.make_plane(height, width)
super().__init__(face, rotation, centered, mode)
class MyHole(b.BaseSketchOperation):
def __init__(self, radius: float, rotation: float = 0, centered: tuple[bool, bool] = (True, True), mode: b.Mode = b.Mode.SUBTRACT):
face = b.Face.makeFromWires(b.Wire.makeCircle(radius, (0, 0, 0), (0, 0, 1)))
def __init__(
self,
radius: float,
rotation: float = 0,
centered: tuple[bool, bool] = (True, True),
mode: b.Mode = b.Mode.SUBTRACT,
):
face = b.Face.make_from_wires(b.Wire.make_circle(radius, (0, 0, 0), (0, 0, 1)))
super().__init__(face, rotation, centered, mode)
with b.BuildSketch() as builder:
MyRectangle(width=20, height=15, centered=(True, True), rotation=45)
MyHole(radius=3)
if 'show_object' in locals():
show_object(builder.sketch)
if "show_object" in locals():
show_object(builder.sketch.wrapped)

View file

@ -29,12 +29,12 @@ import cadquery as cq
with BuildSketch() as logo_text:
Text("123d", fontsize=10, valign=Valign.BOTTOM)
font_height = (logo_text.vertices() >> Axis.Y).Y
font_height = logo_text.vertices().sort_by(Axis.Y)[-1].Y
with BuildSketch() as build_text:
Text("build", fontsize=5, halign=Halign.CENTER)
build_bb = BoundingBox(build_text.sketch, mode=Mode.PRIVATE)
build_vertices = build_bb.vertices() > Axis.X
build_vertices = build_bb.vertices().sort_by(Axis.X)
build_width = build_vertices[-1].X - build_vertices[0].X
with BuildLine() as one:
@ -50,11 +50,11 @@ with BuildPart() as three_d:
with BuildSketch():
Text("3d", fontsize=10, valign=Valign.BOTTOM)
Extrude(amount=font_height * 0.3)
logo_width = (three_d.vertices() >> Axis.X).X
logo_width = three_d.vertices().sort_by(Axis.X)[-1].X
with BuildLine() as arrow_left:
t1 = TangentArc((0, 0), (1, 0.75), tangent=(1, 0))
Mirror(t1, about="XZ")
Mirror(t1, about=Plane.XZ)
ext_line_length = font_height * 0.5
dim_line_length = (logo_width - build_width - 2 * font_height * 0.05) / 2

View file

@ -25,7 +25,6 @@ license:
See the License for the specific language governing permissions and
limitations under the License.
"""
from cadquery import Vector
from build123d import *
with BuildSketch() as leaf:
@ -43,7 +42,7 @@ with BuildSketch() as leaf:
Spline(l5 @ 1, l6 @ 0, tangents=(l5 % 1, l6 % 0), tangent_scalars=(2, 2))
l7 = Line((0.0692, 0.7808), (0.0000, 0.9167))
TangentArc(l6 @ 1, l7 @ 0, tangent=l6 % 1)
Mirror(*outline.edges(), about="YZ")
Mirror(*outline.edges(), about=Plane.YZ)
BuildFace(*leaf.pending_edges)
with BuildSketch() as west_field:
@ -51,7 +50,7 @@ with BuildSketch() as west_field:
Rectangle(0.5, 1, centered=(False, False))
with BuildSketch() as east_field:
Mirror(west_field.sketch, about="YZ")
Mirror(west_field.sketch, about=Plane.YZ)
with BuildSketch() as centre_field:
Rectangle(1, 1, centered=(True, False))

View file

@ -1,3 +1,29 @@
"""
name: circuit_board.py
by: Gumyr
date: September 1st 2022
desc:
This example demonstrates placing holes around a part.
license:
Copyright 2022 Gumyr
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from build123d import *
with BuildPart() as pcb:

View file

@ -41,7 +41,7 @@ overall_width, top_width, height, thickness, fillet = 35, 27, 7.5, 1, 0.8
rail_length = 1000
slot_width, slot_length, slot_pitch = 6.2, 15, 25
with BuildPart("XZ") as rail:
with BuildPart(Plane.XZ) as rail:
with BuildSketch() as din:
Rectangle(overall_width, thickness, centered=(True, False))
Rectangle(top_width, height, centered=(True, False))

View file

@ -60,7 +60,7 @@ with BuildPart() as single_multiple:
with BuildPart() as non_planar:
Cylinder(10, 20, rotation=(90, 0, 0), centered=(True, False, True))
Box(10, 10, 10, centered=(True, True, False), mode=Mode.INTERSECT)
Extrude(non_planar.part.faces() << Axis.Z, amount=2, mode=Mode.REPLACE)
Extrude(non_planar.part.faces().sort_by(Axis.Z)[0], amount=2, mode=Mode.REPLACE)
# Taper Extrude and Extrude to "next" while creating a Cherry MX key cap
# See: https://www.cherrymx.de/en/dev.html

View file

@ -39,7 +39,7 @@ with BuildPart() as handle:
tangent_scalars=(1.5, 1.5),
)
# Record the center line for display and workplane creation
handle_path = handle_center_line.wires()[0]
handle_path: Wire = handle_center_line.wires()[0]
# Create the cross sections - added to pending_faces
for i in range(segment_count + 1):

View file

@ -39,7 +39,7 @@ fillet_radius = tube_spacing / 3
assert tube_extension > fillet_radius
# Generate list of tube locations
with Workplanes("XY"):
with Workplanes(Plane.XY):
tube_locations = [
l
for l in HexLocations(
@ -81,7 +81,7 @@ with BuildPart() as heat_exchanger:
radius=fillet_radius,
)
half_volume_after_fillet = heat_exchanger.part.volume()
Mirror(about="XY")
Mirror(about=Plane.XY)
fillet_volume = 2 * (half_volume_after_fillet - half_volume_before_fillet)
print(f"{fillet_volume=}")

View file

@ -47,7 +47,7 @@ with BuildPart() as recessed_counter_sink:
with BuildPart() as flush_counter_sink:
with Locations((10, 10)):
Cylinder(radius=3, height=2)
with Workplanes(flush_counter_sink.part.faces() >> Axis.Z):
with Workplanes(flush_counter_sink.part.faces().sort_by(Axis.Z)[-1]):
CounterSinkHole(radius=1, counter_sink_radius=1.5)
if "show_object" in locals():

View file

@ -16,4 +16,4 @@ with BuildPart() as blocks:
if "show_object" in locals():
show_object(blocks.part)
show_object(blocks.part.wrapped)

View file

@ -66,7 +66,7 @@ with BuildPart() as lego:
Circle(support_inner_diameter / 2, mode=Mode.SUBTRACT)
Extrude(amount=base_height - wall_thickness)
with Workplanes(
Plane(origin=(0, 0, (lego.vertices() >> Axis.Z).Z), normal=(0, 0, 1))
Plane(origin=(0, 0, lego.vertices().sort_by(Axis.Z)[-1].Z), normal=(0, 0, 1))
):
Box(
block_length,
@ -74,7 +74,7 @@ with BuildPart() as lego:
wall_thickness,
centered=(True, True, False),
)
with Workplanes(lego.faces() >> Axis.Z):
with Workplanes(lego.faces().sort_by(Axis.Z)[-1]):
with GridLocations(lego_unit_size, lego_unit_size, pip_count, 2):
Cylinder(
radius=pip_diameter / 2, height=pip_height, centered=(True, True, False)

View file

@ -53,7 +53,7 @@ with BuildPart() as vase:
vase.edges().filter_by_position(Axis.Y, 60, 62).filter_by_type(GeomType.CIRCLE)
)
Fillet(*top_edges, radius=0.25)
Fillet(vase.edges() << Axis.Y, radius=0.5)
Fillet(vase.edges().sort_by(Axis.Y)[0], radius=0.5)
if "show_object" in locals():

View file

@ -1,12 +1,5 @@
from build123d import (
Face,
Mode,
BuildSketch,
Location,
LocationList,
Compound,
Vector
)
from build123d import Face, Mode, BuildSketch, Location, LocationList, Compound, Vector
class BaseSketchOperation(Compound):
def __init__(
@ -14,11 +7,11 @@ class BaseSketchOperation(Compound):
face: Face,
rotation: float = 0,
centered: tuple[bool, bool] = (True, True),
mode: Mode = Mode.ADD
mode: Mode = Mode.ADD,
):
context: BuildSketch = BuildSketch._get_context()
bounding_box = face.BoundingBox()
bounding_box = face.bounding_box()
center_offset = Vector(
0 if centered[0] else bounding_box.xlen / 2,
0 if centered[1] else bounding_box.ylen / 2,
@ -34,4 +27,4 @@ class BaseSketchOperation(Compound):
for face in new_faces:
context._add_to_context(face, mode=mode)
Compound.__init__(self, Compound.makeCompound(new_faces).wrapped)
Compound.__init__(self, Compound.make_compound(new_faces).wrapped)

View file

@ -77,7 +77,6 @@ from .direct_api import (
Shape,
Vertex,
Plane,
PlaneLike,
Shell,
ShapeList,
)
@ -282,9 +281,6 @@ def validate_inputs(validating_class, builder_context, objects: Shape = None):
# #:TypeVar("RotationLike"): Three tuple of angles about x, y, z or Rotation
# RotationLike = Union[tuple[float, float, float], Rotation]
# #:TypeVar("PlaneLike"): Named Plane (e.g. "XY") or Plane
# PlaneLike = Union[str, Plane]
# class Axis:
# """Axis
@ -714,7 +710,7 @@ class Builder(ABC):
def __init__(
self,
*workplanes: Union[Face, PlaneLike, Location],
*workplanes: Union[Face, Plane, Location],
mode: Mode = Mode.ADD,
):
self.mode = mode
@ -732,7 +728,7 @@ class Builder(ABC):
self._reset_tok = self._current.set(self)
# If there are no workplanes, create a default XY plane
if not self.workplanes and not WorkplaneList._get_context():
self.workplanes_context = Workplanes(Plane.named("XY")).__enter__()
self.workplanes_context = Workplanes(Plane.XY).__enter__()
elif self.workplanes:
self.workplanes_context = Workplanes(*self.workplanes).__enter__()
@ -1171,13 +1167,13 @@ class Workplanes(WorkplaneList):
Create workplanes from the given sequence of planes.
Args:
objs (Union[Face, PlaneLike, Location]): sequence of faces, planes, or
objs (Union[Face, Plane, Location]): sequence of faces, planes, or
locations to use to define workplanes.
Raises:
ValueError: invalid input
"""
def __init__(self, *objs: Union[Face, PlaneLike, Location]):
def __init__(self, *objs: Union[Face, Plane, Location]):
self.workplanes = []
for obj in objs:
if isinstance(obj, Plane):
@ -1190,8 +1186,6 @@ class Workplanes(WorkplaneList):
normal=plane_face.normal_at(plane_face.center()),
)
)
elif isinstance(obj, str):
self.workplanes.append(Plane.named(obj))
elif isinstance(obj, Face):
self.workplanes.append(
Plane(origin=obj.center(), normal=obj.normal_at(obj.center()))

View file

@ -163,3 +163,43 @@ class GeomType(Enum):
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class AngularDirection(Enum):
"""Angular rotation direction"""
CLOCKWISE = auto()
COUNTER_CLOCKWISE = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class PositionMode(Enum):
"""Position along curve mode"""
LENGTH = auto()
PARAMETER = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class FrameMethod(Enum):
"""Moving frame calculation method"""
FRENET = auto()
CORRECTED = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class Direction(Enum):
"""Face direction"""
ALONG_AXIS = auto()
OPPOSITE = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"

View file

@ -39,7 +39,6 @@ from .direct_api import (
ShapeList,
Face,
Plane,
PlaneLike,
Matrix,
Rotation,
RotationLike,
@ -304,14 +303,14 @@ class Mirror(Compound):
Args:
objects (Union[Edge, Face,Compound]): sequence of edges or faces to mirror
about (PlaneLike, optional): reference plane. Defaults to "XZ".
about (Plane, optional): reference plane. Defaults to "XZ".
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""
def __init__(
self,
*objects: Union[Edge, Wire, Face, Compound],
about: PlaneLike = "XZ",
about: Plane = Plane.XZ,
mode: Mode = Mode.ADD,
):
context: Builder = Builder._get_context()
@ -325,7 +324,6 @@ class Mirror(Compound):
self.about = about
self.mode = mode
mirror_plane = about if isinstance(about, Plane) else Plane.named(about)
scale_matrix = Matrix(
[
[1.0, 0.0, 00.0, 0.0],
@ -334,9 +332,9 @@ class Mirror(Compound):
[0.0, 0.0, 00.0, 1.0],
]
)
localized = [mirror_plane.to_local_coords(o) for o in objects]
localized = [about.to_local_coords(o) for o in objects]
local_mirrored = [o.transform_geometry(scale_matrix) for o in localized]
mirrored = [mirror_plane.from_local_coords(o) for o in local_mirrored]
mirrored = [about.from_local_coords(o) for o in local_mirrored]
context._add_to_context(*mirrored, mode=mode)
super().__init__(Compound.make_compound(mirrored).wrapped)
@ -404,16 +402,12 @@ class Offset(Compound):
new_faces = []
for face in faces:
new_faces.append(
Face.make_from_wires(
face.outer_wire().offset_2d(amount, kind=kind.name.lower())[0]
)
Face.make_from_wires(face.outer_wire().offset_2d(amount, kind=kind)[0])
)
if edges:
if len(edges) == 1:
raise ValueError("At least two edges are required")
new_wires = Wire.assemble_edges(edges).offset_2d(
amount, kind=kind.name.lower()
)
new_wires = Wire.assemble_edges(edges).offset_2d(amount, kind=kind)
else:
new_wires = []
@ -427,9 +421,7 @@ class Offset(Compound):
else:
openings_in_this_solid = []
new_solids.append(
solid.shell(
openings_in_this_solid, amount, kind=kind.name.lower()
).fix()
solid.shell(openings_in_this_solid, amount, kind=kind).fix()
)
new_objects = new_wires + new_faces + new_solids
@ -508,7 +500,7 @@ class Split(Compound):
Args:
objects (Union[Edge, Wire, Face, Solid]), objects to split
bisect_by (PlaneLike, optional): plane to segment part. Defaults to Plane.named("XZ").
bisect_by (Plane, optional): plane to segment part. Defaults to Plane.XZ.
keep (Keep, optional): selector for which segment to keep. Defaults to Keep.TOP.
mode (Mode, optional): combination mode. Defaults to Mode.INTERSECT.
"""
@ -516,7 +508,7 @@ class Split(Compound):
def __init__(
self,
*objects: Union[Edge, Wire, Face, Solid],
bisect_by: PlaneLike = Plane.named("XZ"),
bisect_by: Plane = Plane.XZ,
keep: Keep = Keep.TOP,
mode: Mode = Mode.REPLACE,
):
@ -526,7 +518,7 @@ class Split(Compound):
if keep == Keep.TOP
else Vector(-max_size, -max_size, -2 * max_size)
)
return bisect_plane.from_local_coords(
return bisect_by.from_local_coords(
Solid.make_box(2 * max_size, 2 * max_size, 2 * max_size).moved(
Location(cutter_center)
)
@ -539,10 +531,6 @@ class Split(Compound):
if not objects:
objects = [context._obj]
bisect_plane = (
bisect_by if isinstance(bisect_by, Plane) else Plane.named(bisect_by)
)
self.objects = objects
self.bisect_by = bisect_by
self.keep = keep

View file

@ -39,7 +39,6 @@ from .direct_api import (
ShapeList,
Face,
Plane,
PlaneLike,
)
from .build_common import Builder, logger, validate_inputs
@ -63,13 +62,12 @@ class BuildLine(Builder):
def __init__(
self,
*workplanes: Union[Face, PlaneLike, Location],
*workplanes: Union[Face, Plane, Location],
mode: Mode = Mode.ADD,
):
self.initial_planes = workplanes
self.mode = mode
self.line: Compound = None
# self.locations: list[Location] = [Location(Vector())]
super().__init__(*workplanes, mode=mode)
def faces(self):

View file

@ -47,7 +47,6 @@ from .direct_api import (
Location,
Face,
Plane,
PlaneLike,
Axis,
Rotation,
RotationLike,
@ -69,7 +68,7 @@ class BuildPart(Builder):
Create 3D parts (objects with the property of volume) from sketches or 3D objects.
Args:
workplane (Plane, optional): initial plane to work on. Defaults to Plane.named("XY").
workplane (Plane, optional): initial plane to work on. Defaults to Plane.XY.
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""
@ -89,7 +88,7 @@ class BuildPart(Builder):
def __init__(
self,
*workplanes: Union[Face, PlaneLike, Location],
*workplanes: Union[Face, Plane, Location],
mode: Mode = Mode.ADD,
):
self.part: Compound = None
@ -701,7 +700,7 @@ class Section(Compound):
def __init__(
self,
*section_by: PlaneLike,
*section_by: Plane,
height: float = 0.0,
mode: Mode = Mode.INTERSECT,
):
@ -720,13 +719,6 @@ class Section(Compound):
section_planes = (
section_planes if isinstance(section_planes, Iterable) else [section_planes]
)
# If the user provided named planes, convert
section_planes = [
section_plane
if isinstance(section_plane, Plane)
else Plane.named(section_plane)
for section_plane in section_planes
]
planes = [
Face.make_plane(
2 * max_size,
@ -815,7 +807,7 @@ class Sweep(Compound):
True, # make solid
is_frenet,
binormal_mode,
transition.name.lower(),
transition,
).moved(location)
new_solids.append(new_solid)

View file

@ -53,7 +53,6 @@ from .direct_api import (
ShapeList,
Face,
Plane,
PlaneLike,
)
from .build_common import (
Builder,
@ -83,7 +82,7 @@ class BuildSketch(Builder):
def __init__(
self,
*workplanes: Union[Face, PlaneLike, Location],
*workplanes: Union[Face, Plane, Location],
mode: Mode = Mode.ADD,
):
self.workplanes = workplanes
@ -726,9 +725,9 @@ class Text(Compound):
fontsize=fontsize,
font=font,
font_path=font_path,
font_style=font_style.name.lower(),
halign=halign.name.lower(),
valign=valign.name.lower(),
font_style=font_style,
halign=halign,
valign=valign,
position_on_path=position_on_path,
text_path=path,
).rotate(Vector(), Vector(0, 0, 1), rotation)

File diff suppressed because it is too large Load diff

View file

@ -284,7 +284,7 @@ class TestBuilder(unittest.TestCase):
class TestWorkplanes(unittest.TestCase):
def test_named(self):
with Workplanes("XY") as test:
with Workplanes(Plane.XY) as test:
self.assertTupleAlmostEquals(
test.workplanes[0].origin.to_tuple(), (0, 0, 0), 5
)
@ -293,7 +293,7 @@ class TestWorkplanes(unittest.TestCase):
)
def test_locations(self):
with Workplanes("XY"):
with Workplanes(Plane.XY):
with Locations((0, 0, 1), (0, 0, 2)) as l:
with Workplanes(*l.locations) as w:
origins = [p.origin.to_tuple() for p in w.workplanes]

View file

@ -277,7 +277,7 @@ class MirrorTests(unittest.TestCase):
edge = Edge.make_line((1, 0, 0), (2, 0, 0))
wire = Wire.make_circle(1, center=(5, 0, 0), normal=(0, 0, 1))
with BuildLine() as test:
Mirror(edge, wire, about="YZ")
Mirror(edge, wire, about=Plane.YZ)
self.assertEqual(
len(test.edges().filter_by_position(Axis.X, minimum=0, maximum=10)), 0
)
@ -286,7 +286,7 @@ class MirrorTests(unittest.TestCase):
)
with BuildLine() as test:
Line((1, 0), (2, 0))
Mirror(about="YZ")
Mirror(about=Plane.YZ)
self.assertEqual(len(test.edges()), 2)
def test_mirror_sketch(self):
@ -300,7 +300,7 @@ class MirrorTests(unittest.TestCase):
]
)
with BuildSketch() as test:
Mirror(edge, wire, face, compound, about="YZ")
Mirror(edge, wire, face, compound, about=Plane.YZ)
self.assertEqual(
len(test.pending_edges.filter_by_position(Axis.X, minimum=0, maximum=10)), 0
)
@ -318,7 +318,7 @@ class MirrorTests(unittest.TestCase):
def test_mirror_part(self):
cone = Solid.make_cone(2, 1, 2, pnt=(5, 4, 0))
with BuildPart() as test:
Mirror(cone, about="YZ")
Mirror(cone, about=Plane.YZ)
self.assertEqual(
len(test.solids().filter_by_position(Axis.X, minimum=-10, maximum=0)), 1
)

View file

@ -75,7 +75,7 @@ class BuildLineTests(unittest.TestCase):
)
l7 = Line((0.0692, 0.7808), (0.0000, 0.9167))
TangentArc(l6 @ 1, l7 @ 0, tangent=l6 % 1)
Mirror(*outline.edges(), about="YZ")
Mirror(*outline.edges(), about=Plane.YZ)
BuildFace(*leaf.pending_edges)
self.assertAlmostEqual(leaf.sketch.area(), 0.2741600685288115, 5)

View file

@ -142,7 +142,7 @@ class BuildPartTests(unittest.TestCase):
self.assertEqual(len(LocationList._get_context().locations), 5)
def test_named_plane(self):
with BuildPart("YZ") as test:
with BuildPart(Plane.YZ) as test:
self.assertTupleAlmostEquals(
WorkplaneList._get_context().workplanes[0].z_dir.to_tuple(),
(1, 0, 0),
@ -340,7 +340,7 @@ class TestSection(unittest.TestCase):
def test_custom_plane(self):
with BuildPart() as test:
Sphere(10)
Section("XZ")
Section(Plane.XZ)
self.assertAlmostEqual(
test.faces().filter_by_axis(Axis.Y)[-1].area(), 100 * pi, 5
)
@ -362,7 +362,7 @@ class TestSplit(unittest.TestCase):
def test_custom_plane(self):
with BuildPart() as test:
Sphere(10)
Split(bisect_by="YZ", keep=Keep.TOP)
Split(bisect_by=Plane.YZ, keep=Keep.TOP)
self.assertAlmostEqual(test.part.volume(), (2 / 3) * 1000 * pi, 5)