mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-15 15:20:37 -08:00
Introduced with notation for workplanes and points
This commit is contained in:
parent
81a6a2bde3
commit
69abdd9c45
19 changed files with 660 additions and 481 deletions
9
examples/boxes_on_faces.py
Normal file
9
examples/boxes_on_faces.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import build123d as bd
|
||||
|
||||
with bd.BuildPart() as bp:
|
||||
bd.Box(3, 3, 3)
|
||||
with bd.Workplanes(*bp.faces()):
|
||||
bd.Box(1, 2, 0.1, rotation=(0, 0, 45))
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(bp.part, name="box on faces")
|
||||
|
|
@ -42,15 +42,15 @@ with BuildLine() as one:
|
|||
TangentArc(l1 @ 1, (0, font_height * 0.7), tangent=(l1 % 1) * -1)
|
||||
|
||||
with BuildSketch() as two:
|
||||
PushPoints((font_height * 0.35, 0))
|
||||
Text("2", fontsize=10, valign=Valign.BOTTOM)
|
||||
with Locations((font_height * 0.35, 0)):
|
||||
Text("2", fontsize=10, valign=Valign.BOTTOM)
|
||||
|
||||
with BuildPart() as three_d:
|
||||
PushPoints((font_height * 1.1, 0))
|
||||
with BuildSketch():
|
||||
Text("3d", fontsize=10, valign=Valign.BOTTOM)
|
||||
Extrude(font_height * 0.3)
|
||||
logo_width = three_d.vertices().sort_by(SortBy.X)[-1].x
|
||||
with Locations((font_height * 1.1, 0)):
|
||||
with BuildSketch():
|
||||
Text("3d", fontsize=10, valign=Valign.BOTTOM)
|
||||
Extrude(font_height * 0.3)
|
||||
logo_width = three_d.vertices().sort_by(SortBy.X)[-1].x
|
||||
|
||||
with BuildLine() as arrow_left:
|
||||
t1 = TangentArc((0, 0), (1, 0.75), tangent=(1, 0))
|
||||
|
|
@ -64,20 +64,20 @@ with BuildLine() as extension_lines:
|
|||
(logo_width, -font_height * 0.1),
|
||||
(logo_width, -ext_line_length - font_height * 0.1),
|
||||
)
|
||||
PushPoints(l1 @ 0.5)
|
||||
Add(*arrow_left.line)
|
||||
PushPoints(l2 @ 0.5)
|
||||
Add(*arrow_left.line, rotation=180.0)
|
||||
with Locations(l1 @ 0.5):
|
||||
Add(*arrow_left.line)
|
||||
with Locations(l2 @ 0.5):
|
||||
Add(*arrow_left.line, rotation=180.0)
|
||||
Line(l1 @ 0.5, l1 @ 0.5 + cq.Vector(dim_line_length, 0))
|
||||
Line(l2 @ 0.5, l2 @ 0.5 - cq.Vector(dim_line_length, 0))
|
||||
|
||||
# Precisely center the build Faces
|
||||
with BuildSketch() as build:
|
||||
PushPoints(
|
||||
with Locations(
|
||||
(l1 @ 0.5 + l2 @ 0.5) / 2
|
||||
- cq.Vector((build_vertices[-1].x + build_vertices[0].x) / 2, 0)
|
||||
)
|
||||
Add(build_text.sketch)
|
||||
):
|
||||
Add(build_text.sketch)
|
||||
|
||||
logo = cq.Assembly(None, name="logo")
|
||||
logo.add(one.line_as_wire, name="one")
|
||||
|
|
@ -86,23 +86,24 @@ logo.add(three_d.part, name="three_d")
|
|||
for line in extension_lines.line:
|
||||
logo.add(line)
|
||||
logo.add(build.sketch, name="build")
|
||||
logo.save("logo.step")
|
||||
cq.exporters.export(
|
||||
logo.toCompound(),
|
||||
"logo.svg",
|
||||
opt={
|
||||
# "width": 300,
|
||||
# "height": 300,
|
||||
# "marginLeft": 10,
|
||||
# "marginTop": 10,
|
||||
"showAxes": False,
|
||||
# "projectionDir": (0.5, 0.5, 0.5),
|
||||
"strokeWidth": 0.1,
|
||||
# "strokeColor": (255, 0, 0),
|
||||
# "hiddenColor": (0, 0, 255),
|
||||
"showHidden": False,
|
||||
},
|
||||
)
|
||||
if False:
|
||||
logo.save("logo.step")
|
||||
cq.exporters.export(
|
||||
logo.toCompound(),
|
||||
"logo.svg",
|
||||
opt={
|
||||
# "width": 300,
|
||||
# "height": 300,
|
||||
# "marginLeft": 10,
|
||||
# "marginTop": 10,
|
||||
"showAxes": False,
|
||||
# "projectionDir": (0.5, 0.5, 0.5),
|
||||
"strokeWidth": 0.1,
|
||||
# "strokeColor": (255, 0, 0),
|
||||
# "hiddenColor": (0, 0, 255),
|
||||
"showHidden": False,
|
||||
},
|
||||
)
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(one.line, name="one")
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ with BuildSketch() as leaf:
|
|||
BuildFace(*leaf.pending_edges)
|
||||
|
||||
with BuildSketch() as west_field:
|
||||
PushPoints((-1, 0))
|
||||
Rectangle(0.5, 1, centered=(False, False))
|
||||
with Locations((-1, 0)):
|
||||
Rectangle(0.5, 1, centered=(False, False))
|
||||
|
||||
with BuildSketch() as east_field:
|
||||
Mirror(west_field.sketch, axis=Axis.Y)
|
||||
|
|
|
|||
19
examples/circuit_board.py
Normal file
19
examples/circuit_board.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from build123d import *
|
||||
|
||||
with BuildPart() as pcb:
|
||||
with BuildSketch():
|
||||
Rectangle(70, 30)
|
||||
for i in range(65 // 5):
|
||||
x = i * 5 - 30
|
||||
with Locations((x, -15), (x, -10), (x, 10), (x, 15)):
|
||||
Circle(1, mode=Mode.SUBTRACT)
|
||||
for i in range(30 // 5 - 1):
|
||||
y = i * 5 - 10
|
||||
with Locations((30, y), (35, y)):
|
||||
Circle(1, mode=Mode.SUBTRACT)
|
||||
with GridLocations(60, 20, 2, 2):
|
||||
Circle(2, mode=Mode.SUBTRACT)
|
||||
Extrude(3)
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(pcb.part)
|
||||
|
|
@ -38,19 +38,19 @@ with BuildSketch() as minute_indicator:
|
|||
|
||||
with BuildSketch() as clock_face:
|
||||
Circle(clock_radius)
|
||||
PolarArray(0, 0, 360, 60)
|
||||
Add(minute_indicator.sketch, mode=Mode.SUBTRACT)
|
||||
PolarArray(clock_radius * 0.875, 0, 360, 12)
|
||||
SlotOverall(clock_radius * 0.05, clock_radius * 0.025, mode=Mode.SUBTRACT)
|
||||
with PolarLocations(0, 0, 360, 60):
|
||||
Add(minute_indicator.sketch, mode=Mode.SUBTRACT)
|
||||
with PolarLocations(clock_radius * 0.875, 0, 360, 12):
|
||||
SlotOverall(clock_radius * 0.05, clock_radius * 0.025, mode=Mode.SUBTRACT)
|
||||
for hour in range(1, 13):
|
||||
PolarArray(clock_radius * 0.75, -hour * 30 + 90, 360, 1, rotate=False)
|
||||
Text(
|
||||
str(hour),
|
||||
fontsize=clock_radius * 0.175,
|
||||
font_style=FontStyle.BOLD,
|
||||
halign=Halign.CENTER,
|
||||
mode=Mode.SUBTRACT,
|
||||
)
|
||||
with PolarLocations(clock_radius * 0.75, -hour * 30 + 90, 360, 1, rotate=False):
|
||||
Text(
|
||||
str(hour),
|
||||
fontsize=clock_radius * 0.175,
|
||||
font_style=FontStyle.BOLD,
|
||||
halign=Halign.CENTER,
|
||||
mode=Mode.SUBTRACT,
|
||||
)
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(clock_face.sketch, name="clock_face")
|
||||
|
|
|
|||
|
|
@ -62,20 +62,18 @@ with BuildPart(workplane="XZ") as rail:
|
|||
)
|
||||
)
|
||||
Fillet(*inside_vertices, radius=fillet)
|
||||
outside_vertices = list(
|
||||
filter(
|
||||
lambda v: (v.Y == 0.0 or v.Y == height)
|
||||
and -overall_width / 2 < v.X < overall_width / 2,
|
||||
din.vertices(),
|
||||
)
|
||||
outside_vertices = filter(
|
||||
lambda v: (v.Y == 0.0 or v.Y == height)
|
||||
and -overall_width / 2 < v.X < overall_width / 2,
|
||||
din.vertices(),
|
||||
)
|
||||
Fillet(*outside_vertices, radius=fillet + thickness)
|
||||
Extrude(rail_length)
|
||||
WorkplanesFromFaces(rail.faces().filter_by_axis(Axis.Z)[-1], replace=True)
|
||||
with BuildSketch() as slots:
|
||||
RectangularArray(0, slot_pitch, 1, rail_length // slot_pitch - 1)
|
||||
SlotOverall(slot_length, slot_width, rotation=90)
|
||||
slot_holes = Extrude(-height, mode=Mode.SUBTRACT)
|
||||
with Workplanes(rail.faces().filter_by_axis(Axis.Z)[-1]):
|
||||
with BuildSketch() as slots:
|
||||
with GridLocations(0, slot_pitch, 1, rail_length // slot_pitch - 1):
|
||||
SlotOverall(slot_length, slot_width, rotation=90)
|
||||
Extrude(-height, mode=Mode.SUBTRACT)
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(rail.part, name="rail")
|
||||
|
|
|
|||
|
|
@ -44,20 +44,20 @@ with BuildPart() as handle:
|
|||
|
||||
# Create the cross sections - added to pending_faces
|
||||
for i in range(segment_count + 1):
|
||||
Workplanes(
|
||||
with Workplanes(
|
||||
Plane(
|
||||
origin=handle_path @ (i / segment_count),
|
||||
normal=handle_path % (i / segment_count),
|
||||
)
|
||||
)
|
||||
with BuildSketch() as section:
|
||||
if i % segment_count == 0:
|
||||
Circle(1)
|
||||
else:
|
||||
Rectangle(1, 2)
|
||||
Fillet(*section.vertices(), radius=0.2)
|
||||
):
|
||||
with BuildSketch() as section:
|
||||
if i % segment_count == 0:
|
||||
Circle(1)
|
||||
else:
|
||||
Rectangle(1.25, 3)
|
||||
Fillet(*section.vertices(), radius=0.2)
|
||||
# Record the sections for display
|
||||
sections = list(*handle.pending_faces.values())
|
||||
sections = handle.pending_faces
|
||||
|
||||
# Create the handle by sweeping along the path
|
||||
Sweep(multisection=True)
|
||||
|
|
|
|||
19
examples/intersecting_chamfers.py
Normal file
19
examples/intersecting_chamfers.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from build123d import *
|
||||
|
||||
|
||||
with BuildPart() as blocks:
|
||||
with Locations((-1, -1, 0)):
|
||||
Box(1, 2, 1, centered=(True, False, False))
|
||||
Box(1, 1, 2, centered=(True, False, False))
|
||||
with Locations((1, -1, 0)):
|
||||
Box(1, 2, 1, centered=(True, False, False))
|
||||
bottom_edges = blocks.edges().filter_by_position(
|
||||
Axis.Z, 0, 1, inclusive=(True, False)
|
||||
)
|
||||
Chamfer(*bottom_edges, length=0.1)
|
||||
top_edges = blocks.edges().filter_by_position(Axis.Z, 1, 2, inclusive=(False, True))
|
||||
Chamfer(*top_edges, length=0.1)
|
||||
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(blocks.part)
|
||||
|
|
@ -29,15 +29,15 @@ from build123d import *
|
|||
|
||||
with BuildPart() as pipes:
|
||||
Box(10, 10, 10, rotation=(10, 20, 30))
|
||||
WorkplanesFromFaces(*pipes.faces(), replace=True)
|
||||
with BuildSketch() as pipe:
|
||||
Circle(4)
|
||||
Extrude(-5, mode=Mode.SUBTRACT)
|
||||
with BuildSketch() as pipe:
|
||||
Circle(4.5)
|
||||
Circle(4, mode=Mode.SUBTRACT)
|
||||
Extrude(10)
|
||||
Fillet(*pipes.edges(Select.LAST), radius=0.2)
|
||||
with Workplanes(*pipes.faces()):
|
||||
with BuildSketch() as pipe:
|
||||
Circle(4)
|
||||
Extrude(-5, mode=Mode.SUBTRACT)
|
||||
with BuildSketch() as pipe:
|
||||
Circle(4.5)
|
||||
Circle(4, mode=Mode.SUBTRACT)
|
||||
Extrude(10)
|
||||
Fillet(*pipes.edges(Select.LAST), radius=0.2)
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(pipes.part, name="intersecting pipes")
|
||||
|
|
|
|||
96
examples/lego.py
Normal file
96
examples/lego.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
"""
|
||||
|
||||
name: lego.py
|
||||
by: Gumyr
|
||||
date: September 12th 2022
|
||||
|
||||
desc:
|
||||
|
||||
This example creates a model of a double wide lego block with a
|
||||
parametric length (pip_count).
|
||||
|
||||
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 *
|
||||
from cadquery import Plane
|
||||
|
||||
pip_count = 6
|
||||
|
||||
lego_unit_size = 8
|
||||
pip_height = 1.8
|
||||
pip_diameter = 4.8
|
||||
block_length = lego_unit_size * pip_count
|
||||
block_width = 16
|
||||
base_height = 9.6
|
||||
block_height = base_height + pip_height
|
||||
support_outer_diameter = 6.5
|
||||
support_inner_diameter = 4.8
|
||||
ridge_width = 0.6
|
||||
ridge_depth = 0.3
|
||||
wall_thickness = 1.2
|
||||
|
||||
with BuildPart() as lego:
|
||||
# Draw the bottom of the block
|
||||
with BuildSketch():
|
||||
# Start with a Rectangle the size of the block
|
||||
perimeter = Rectangle(block_length, block_width)
|
||||
# Subtract an offset to create the block walls
|
||||
Offset(
|
||||
perimeter,
|
||||
amount=-wall_thickness,
|
||||
kind=Kind.INTERSECTION,
|
||||
mode=Mode.SUBTRACT,
|
||||
)
|
||||
# Add a grid of lengthwise and widthwise bars
|
||||
with GridLocations(0, lego_unit_size, 1, 2):
|
||||
Rectangle(block_length, ridge_width)
|
||||
with GridLocations(lego_unit_size, 0, pip_count, 1):
|
||||
Rectangle(ridge_width, block_width)
|
||||
# Substract a rectangle leaving ribs on the block walls
|
||||
Rectangle(
|
||||
block_length - 2 * (wall_thickness + ridge_depth),
|
||||
block_width - 2 * (wall_thickness + ridge_depth),
|
||||
mode=Mode.SUBTRACT,
|
||||
)
|
||||
# Add a row of hollow circles to the center
|
||||
with GridLocations(lego_unit_size, 0, pip_count - 1, 1):
|
||||
Circle(support_outer_diameter / 2)
|
||||
Circle(support_inner_diameter / 2, mode=Mode.SUBTRACT)
|
||||
# Extrude this base sketch to the height of the walls
|
||||
Extrude(base_height - wall_thickness)
|
||||
# Create a workplane on the top of the walls
|
||||
with Workplanes(
|
||||
Plane(origin=(0, 0, base_height - wall_thickness), normal=(0, 0, 1))
|
||||
):
|
||||
# Create the top of the block
|
||||
Box(
|
||||
block_length,
|
||||
block_width,
|
||||
wall_thickness,
|
||||
centered=(True, True, False),
|
||||
)
|
||||
# Create a workplane on the top of the block
|
||||
with Workplanes(lego.faces().sort_by(SortBy.Z)[-1]):
|
||||
# Create a grid of pips
|
||||
with GridLocations(lego_unit_size, lego_unit_size, pip_count, 2):
|
||||
Cylinder(
|
||||
radius=pip_diameter / 2, height=pip_height, centered=(True, True, False)
|
||||
)
|
||||
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(lego.part, name="lego")
|
||||
|
|
@ -32,9 +32,9 @@ from build123d import *
|
|||
with BuildPart() as art:
|
||||
slice_count = 10
|
||||
for i in range(slice_count + 1):
|
||||
Workplanes(Plane(origin=(0, 0, i * 3), normal=(0, 0, 1)))
|
||||
with BuildSketch() as slice:
|
||||
Circle(10 * sin(i * pi / slice_count) + 5)
|
||||
with Workplanes(Plane(origin=(0, 0, i * 3), normal=(0, 0, 1))):
|
||||
with BuildSketch() as slice:
|
||||
Circle(10 * sin(i * pi / slice_count) + 5)
|
||||
Loft()
|
||||
top_bottom = art.faces().filter_by_type(Type.PLANE)
|
||||
Shell(*top_bottom, thickness=0.5)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ from build123d import *
|
|||
|
||||
with BuildPart() as obj:
|
||||
Box(5, 5, 1)
|
||||
WorkplanesFromFaces(*obj.faces().filter_by_axis(Axis.Z))
|
||||
Sphere(1.8, mode=Mode.SUBTRACT)
|
||||
with Workplanes(*obj.faces().filter_by_axis(Axis.Z)):
|
||||
Sphere(1.8, mode=Mode.SUBTRACT)
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(obj.part)
|
||||
|
|
|
|||
|
|
@ -37,10 +37,10 @@ with BuildPart() as pillow_block:
|
|||
Rectangle(width, height)
|
||||
Fillet(*plan.vertices(), radius=5)
|
||||
Extrude(thickness)
|
||||
WorkplanesFromFaces(pillow_block.faces().filter_by_axis(Axis.Z)[-1])
|
||||
CounterBoreHole(bearing_axle_radius, bearing_radius, bearing_thickness)
|
||||
RectangularArray(width - 2 * padding, height - 2 * padding, 2, 2)
|
||||
CounterBoreHole(screw_shaft_radius, screw_head_radius, screw_head_height)
|
||||
with Workplanes(pillow_block.faces().filter_by_axis(Axis.Z)[-1]):
|
||||
CounterBoreHole(bearing_axle_radius, bearing_radius, bearing_thickness)
|
||||
with GridLocations(width - 2 * padding, height - 2 * padding, 2, 2):
|
||||
CounterBoreHole(screw_shaft_radius, screw_head_radius, screw_head_height)
|
||||
|
||||
# Render the part
|
||||
if "show_object" in locals():
|
||||
|
|
|
|||
16
examples/washer.py
Normal file
16
examples/washer.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from build123d import *
|
||||
|
||||
with BuildPart() as simple_washer:
|
||||
Cylinder(3, 2)
|
||||
Hole(1)
|
||||
|
||||
with BuildPart() as washer:
|
||||
with Locations((10, 0)):
|
||||
Cylinder(3, 2)
|
||||
with Workplanes(washer.faces().sort_by(SortBy.Z)[-1]):
|
||||
Hole(1)
|
||||
|
||||
|
||||
if "show_object" in locals():
|
||||
show_object(simple_washer.part, name="simple_washer")
|
||||
show_object(washer.part, name="washer")
|
||||
|
|
@ -24,11 +24,11 @@ __all__ = [
|
|||
"BoundingBox",
|
||||
"Chamfer",
|
||||
"Fillet",
|
||||
"HexArray",
|
||||
"HexLocations",
|
||||
"Mirror",
|
||||
"PolarArray",
|
||||
"PushPoints",
|
||||
"RectangularArray",
|
||||
"PolarLocations",
|
||||
"Locations",
|
||||
"GridLocations",
|
||||
"BuildLine",
|
||||
"CenterArc",
|
||||
"Helix",
|
||||
|
|
@ -52,7 +52,6 @@ __all__ = [
|
|||
"Split",
|
||||
"Sweep",
|
||||
"Workplanes",
|
||||
"WorkplanesFromFaces",
|
||||
"Box",
|
||||
"Cone",
|
||||
"Cylinder",
|
||||
|
|
@ -74,4 +73,6 @@ __all__ = [
|
|||
"SlotOverall",
|
||||
"Text",
|
||||
"Trapezoid",
|
||||
"LocationList",
|
||||
"WorkplaneList",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ license:
|
|||
|
||||
"""
|
||||
import contextvars
|
||||
from itertools import product
|
||||
from abc import ABC, abstractmethod
|
||||
from math import radians
|
||||
from typing import Iterable, Union
|
||||
|
|
@ -46,6 +47,7 @@ from cadquery import (
|
|||
Vertex,
|
||||
Plane,
|
||||
)
|
||||
from cadquery.occ_impl.shapes import VectorLike
|
||||
from OCP.gp import gp_Pnt, gp_Ax1, gp_Dir, gp_Trsf
|
||||
import cq_warehouse.extensions
|
||||
import logging
|
||||
|
|
@ -520,8 +522,9 @@ class Builder(ABC):
|
|||
"Builder._current"
|
||||
)
|
||||
|
||||
def __init__(self, mode: Mode = Mode.ADD):
|
||||
def __init__(self, mode: Mode = Mode.ADD, initial_plane: Plane = Plane.named("XY")):
|
||||
self.mode = mode
|
||||
self.initial_plane = initial_plane
|
||||
self._reset_tok = None
|
||||
self._parent = None
|
||||
self.last_vertices = []
|
||||
|
|
@ -532,6 +535,10 @@ class Builder(ABC):
|
|||
|
||||
self._parent = Builder._get_context()
|
||||
self._reset_tok = self._current.set(self)
|
||||
|
||||
# Push an initial plane and point
|
||||
self.workplane_generator = Workplanes(self.initial_plane).__enter__()
|
||||
|
||||
logger.info(f"Entering {type(self).__name__} with mode={self.mode}")
|
||||
return self
|
||||
|
||||
|
|
@ -539,6 +546,10 @@ class Builder(ABC):
|
|||
"""Upon exiting restore context and send object to parent"""
|
||||
self._current.reset(self._reset_tok)
|
||||
logger.info(f"Exiting {type(self).__name__}")
|
||||
|
||||
# Pop the initial plane and point
|
||||
self.workplane_generator.__exit__(None, None, None)
|
||||
|
||||
if self._parent is not None:
|
||||
logger.debug(
|
||||
f"Transferring {len([o for o in self._obj])} to {type(self._parent).__name__}"
|
||||
|
|
@ -571,3 +582,252 @@ class Builder(ABC):
|
|||
here to avoid having to recreate this method.
|
||||
"""
|
||||
return cls._current.get(None)
|
||||
|
||||
|
||||
class LocationList:
|
||||
|
||||
"""Context variable used to link to LocationList instance"""
|
||||
|
||||
_current: contextvars.ContextVar["LocationList"] = contextvars.ContextVar(
|
||||
"ContextList._current"
|
||||
)
|
||||
|
||||
def __init__(self, locations: list):
|
||||
self._reset_tok = None
|
||||
self.locations = locations
|
||||
|
||||
def __enter__(self):
|
||||
"""Upon entering create a token to restore contextvars"""
|
||||
self._reset_tok = self._current.set(self)
|
||||
logger.info(f"{type(self).__name__} is pushing {len(self.locations)} points")
|
||||
return self
|
||||
|
||||
def __exit__(self, exception_type, exception_value, traceback):
|
||||
"""Upon exiting restore context"""
|
||||
self._current.reset(self._reset_tok)
|
||||
logger.info(f"{type(self).__name__} is popping {len(self.locations)} points")
|
||||
|
||||
@classmethod
|
||||
def _get_context(cls):
|
||||
"""Return the instance of the current LocationList"""
|
||||
return cls._current.get(None)
|
||||
|
||||
|
||||
class HexLocations(LocationList):
|
||||
"""Location Generator: Hex Array
|
||||
|
||||
Creates a context of hexagon array of points.
|
||||
|
||||
Args:
|
||||
diagonal: tip to tip size of hexagon ( must be > 0)
|
||||
xCount: number of points ( > 0 )
|
||||
yCount: number of points ( > 0 )
|
||||
centered: specify centering along each axis.
|
||||
|
||||
Raises:
|
||||
ValueError: Spacing and count must be > 0
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
diagonal: float,
|
||||
xCount: int,
|
||||
yCount: int,
|
||||
centered: tuple[bool, bool] = (True, True),
|
||||
):
|
||||
xSpacing = 3 * diagonal / 4
|
||||
ySpacing = diagonal * sqrt(3) / 2
|
||||
if xSpacing <= 0 or ySpacing <= 0 or xCount < 1 or yCount < 1:
|
||||
raise ValueError("Spacing and count must be > 0 ")
|
||||
|
||||
lpoints = [] # coordinates relative to bottom left point
|
||||
for x in range(0, xCount, 2):
|
||||
for y in range(yCount):
|
||||
lpoints.append(Vector(xSpacing * x, ySpacing * y + ySpacing / 2))
|
||||
for x in range(1, xCount, 2):
|
||||
for y in range(yCount):
|
||||
lpoints.append(Vector(xSpacing * x, ySpacing * y + ySpacing))
|
||||
|
||||
# shift points down and left relative to origin if requested
|
||||
offset = Vector()
|
||||
if centered[0]:
|
||||
offset += Vector(-xSpacing * (xCount - 1) * 0.5, 0)
|
||||
if centered[1]:
|
||||
offset += Vector(0, -ySpacing * yCount * 0.5)
|
||||
lpoints = [x + offset for x in lpoints]
|
||||
|
||||
# convert to locations
|
||||
self.locations = [
|
||||
Location(plane) * Location(pt)
|
||||
for pt in lpoints
|
||||
for plane in WorkplaneList._get_context().workplanes
|
||||
]
|
||||
|
||||
super().__init__(self.locations)
|
||||
|
||||
|
||||
class PolarLocations(LocationList):
|
||||
"""Location Generator: Polar Array
|
||||
|
||||
Push a polar array of locations to Part or Sketch
|
||||
|
||||
Args:
|
||||
radius (float): array radius
|
||||
start_angle (float): angle to first point from +ve X axis
|
||||
stop_angle (float): angle to last point from +ve X axis
|
||||
count (int): Number of points to push
|
||||
rotate (bool, optional): Align locations with arc tangents. Defaults to True.
|
||||
|
||||
Raises:
|
||||
ValueError: Count must be greater than or equal to 1
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
radius: float,
|
||||
start_angle: float,
|
||||
stop_angle: float,
|
||||
count: int,
|
||||
rotate: bool = True,
|
||||
):
|
||||
if count < 1:
|
||||
raise ValueError(f"At least 1 elements required, requested {count}")
|
||||
|
||||
angle_step = (stop_angle - start_angle) / count
|
||||
|
||||
# Note: rotate==False==0 so the location orientation doesn't change
|
||||
self.locations = [
|
||||
Location(plane)
|
||||
* Location(
|
||||
Vector(radius, 0).rotateZ(start_angle + angle_step * i),
|
||||
Vector(0, 0, 1),
|
||||
rotate * angle_step * i,
|
||||
)
|
||||
for i in range(count)
|
||||
for plane in WorkplaneList._get_context().workplanes
|
||||
]
|
||||
|
||||
super().__init__(self.locations)
|
||||
|
||||
|
||||
class Locations(LocationList):
|
||||
"""Location Generator: Push Points
|
||||
|
||||
Push sequence of locations to Part or Sketch
|
||||
|
||||
Args:
|
||||
pts (Union[VectorLike, Vertex, Location]): sequence of points to push
|
||||
"""
|
||||
|
||||
def __init__(self, *pts: Union[VectorLike, Vertex, Location]):
|
||||
self.locations = []
|
||||
for plane in WorkplaneList._get_context().workplanes:
|
||||
for pt in pts:
|
||||
if isinstance(pt, Location):
|
||||
self.locations.append(Location(plane) * pt)
|
||||
elif isinstance(pt, Vector):
|
||||
self.locations.append(Location(plane) * Location(pt))
|
||||
elif isinstance(pt, Vertex):
|
||||
self.locations.append(
|
||||
Location(plane) * Location(Vector(pt.toTuple()))
|
||||
)
|
||||
elif isinstance(pt, tuple):
|
||||
self.locations.append(Location(plane) * Location(Vector(pt)))
|
||||
else:
|
||||
raise ValueError(f"Locations doesn't accept type {type(pt)}")
|
||||
super().__init__(self.locations)
|
||||
|
||||
|
||||
class GridLocations(LocationList):
|
||||
"""Location Generator: Rectangular Array
|
||||
|
||||
Push a rectangular array of locations to Part or Sketch
|
||||
|
||||
Args:
|
||||
x_spacing (float): horizontal spacing
|
||||
y_spacing (float): vertical spacing
|
||||
x_count (int): number of horizontal points
|
||||
y_count (int): number of vertical points
|
||||
|
||||
Raises:
|
||||
ValueError: Either x or y count must be greater than or equal to one.
|
||||
"""
|
||||
|
||||
def __init__(self, x_spacing: float, y_spacing: float, x_count: int, y_count: int):
|
||||
if x_count < 1 or y_count < 1:
|
||||
raise ValueError(
|
||||
f"At least 1 elements required, requested {x_count}, {y_count}"
|
||||
)
|
||||
|
||||
self.locations = []
|
||||
for plane in WorkplaneList._get_context().workplanes:
|
||||
offset = Vector((x_count - 1) * x_spacing, (y_count - 1) * y_spacing) * 0.5
|
||||
for i, j in product(range(x_count), range(y_count)):
|
||||
self.locations.append(
|
||||
Location(plane)
|
||||
* Location(Vector(i * x_spacing, j * y_spacing) - offset)
|
||||
)
|
||||
super().__init__(self.locations)
|
||||
|
||||
|
||||
class WorkplaneList:
|
||||
|
||||
"""Context variable used to link to WorkplaneList instance"""
|
||||
|
||||
_current: contextvars.ContextVar["WorkplaneList"] = contextvars.ContextVar(
|
||||
"WorkplaneList._current"
|
||||
)
|
||||
|
||||
def __init__(self, workplanes: list):
|
||||
self._reset_tok = None
|
||||
self.workplanes = workplanes
|
||||
|
||||
def __enter__(self):
|
||||
"""Upon entering create a token to restore contextvars"""
|
||||
self._reset_tok = self._current.set(self)
|
||||
self.point_generator = Locations((0, 0, 0)).__enter__()
|
||||
logger.info(
|
||||
f"{type(self).__name__} is pushing {len(self.workplanes)} workplanes"
|
||||
)
|
||||
return self
|
||||
|
||||
def __exit__(self, exception_type, exception_value, traceback):
|
||||
"""Upon exiting restore context"""
|
||||
self._current.reset(self._reset_tok)
|
||||
self.point_generator.__exit__(None, None, None)
|
||||
logger.info(
|
||||
f"{type(self).__name__} is popping {len(self.workplanes)} workplanes"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_context(cls):
|
||||
"""Return the instance of the current ContextList"""
|
||||
return cls._current.get(None)
|
||||
|
||||
|
||||
class Workplanes(WorkplaneList):
|
||||
"""Workplane Generator: Workplanes
|
||||
|
||||
Create workplanes from the given sequence of planes.
|
||||
|
||||
Args:
|
||||
objs (Union[Face,PlaneLike]): sequence of faces or planes to use
|
||||
as workplanes.
|
||||
Raises:
|
||||
ValueError: invalid input
|
||||
"""
|
||||
|
||||
def __init__(self, *objs: Union[Face, PlaneLike]):
|
||||
self.workplanes = []
|
||||
for obj in objs:
|
||||
if isinstance(obj, Plane):
|
||||
self.workplanes.append(obj)
|
||||
elif isinstance(obj, str):
|
||||
self.workplanes.append(Plane.named(obj))
|
||||
elif isinstance(obj, Face):
|
||||
self.workplanes.append(
|
||||
Plane(origin=obj.Center(), normal=obj.normalAt(obj.Center()))
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Workplanes does not accept {type(obj)}")
|
||||
super().__init__(self.workplanes)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,31 @@
|
|||
from itertools import product
|
||||
from math import sqrt
|
||||
"""
|
||||
BuildGeneric
|
||||
|
||||
name: build_generic.py
|
||||
by: Gumyr
|
||||
date: July 12th 2022
|
||||
|
||||
desc:
|
||||
This python module is a library of generic classes used by other
|
||||
build123d builders.
|
||||
|
||||
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 typing import Union
|
||||
from build123d import (
|
||||
BuildLine,
|
||||
|
|
@ -10,6 +36,7 @@ from build123d import (
|
|||
Rotation,
|
||||
Axis,
|
||||
Builder,
|
||||
LocationList,
|
||||
)
|
||||
from cadquery import (
|
||||
Shape,
|
||||
|
|
@ -24,6 +51,11 @@ from cadquery import (
|
|||
Vector,
|
||||
)
|
||||
from cadquery.occ_impl.shapes import VectorLike
|
||||
import logging
|
||||
|
||||
logging.getLogger("build123d").addHandler(logging.NullHandler())
|
||||
logger = logging.getLogger("build123d")
|
||||
|
||||
|
||||
#
|
||||
# Objects
|
||||
|
|
@ -82,10 +114,9 @@ class Add(Compound):
|
|||
# Can't use get_and_clear_locations because the solid needs to be
|
||||
# oriented to the workplane after being moved to a local location
|
||||
new_objects = [
|
||||
workplane.fromLocalCoords(solid.moved(location))
|
||||
solid.moved(location)
|
||||
for solid in new_solids
|
||||
for workplane in context.workplanes
|
||||
for location in context.locations
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context.locations = [Location(Vector())]
|
||||
context._add_to_context(*new_objects, mode=mode)
|
||||
|
|
@ -98,11 +129,10 @@ class Add(Compound):
|
|||
obj.rotate(
|
||||
Vector(0, 0, 0), Vector(0, 0, 1), rotation_angle
|
||||
).moved(location)
|
||||
for location in context.locations
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
)
|
||||
context._add_to_context(*new_objects, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
# elif isinstance(context, BuildLine):
|
||||
# new_objects = [obj for obj in objects if isinstance(obj, Edge)]
|
||||
# for new_wires in filter(lambda o: isinstance(o, Wire), objects):
|
||||
|
|
@ -248,63 +278,6 @@ class Fillet(Compound):
|
|||
)
|
||||
|
||||
|
||||
class HexArray:
|
||||
"""Generic Operation: Hex Array
|
||||
|
||||
Creates a hexagon array of points and pushes them to Part or Sketch locations.
|
||||
|
||||
Args:
|
||||
diagonal: tip to tip size of hexagon ( must be > 0)
|
||||
xCount: number of points ( > 0 )
|
||||
yCount: number of points ( > 0 )
|
||||
centered: specify centering along each axis.
|
||||
|
||||
Raises:
|
||||
ValueError: Spacing and count must be > 0
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
diagonal: float,
|
||||
xCount: int,
|
||||
yCount: int,
|
||||
centered: tuple[bool, bool] = (True, True),
|
||||
):
|
||||
context: Builder = Builder._get_context()
|
||||
xSpacing = 3 * diagonal / 4
|
||||
ySpacing = diagonal * sqrt(3) / 2
|
||||
if xSpacing <= 0 or ySpacing <= 0 or xCount < 1 or yCount < 1:
|
||||
raise ValueError("Spacing and count must be > 0 ")
|
||||
|
||||
lpoints = [] # coordinates relative to bottom left point
|
||||
for x in range(0, xCount, 2):
|
||||
for y in range(yCount):
|
||||
lpoints.append(Vector(xSpacing * x, ySpacing * y + ySpacing / 2))
|
||||
for x in range(1, xCount, 2):
|
||||
for y in range(yCount):
|
||||
lpoints.append(Vector(xSpacing * x, ySpacing * y + ySpacing))
|
||||
|
||||
# shift points down and left relative to origin if requested
|
||||
offset = Vector()
|
||||
if centered[0]:
|
||||
offset += Vector(-xSpacing * (xCount - 1) * 0.5, 0)
|
||||
if centered[1]:
|
||||
offset += Vector(0, -ySpacing * yCount * 0.5)
|
||||
lpoints = [x + offset for x in lpoints]
|
||||
|
||||
# convert to locations
|
||||
new_locations = [Location(pt) for pt in lpoints]
|
||||
|
||||
context.locations = new_locations
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate over the points typically in a for loop"""
|
||||
context: Builder = Builder._get_context()
|
||||
for location in context.locations:
|
||||
yield location.toTuple()[0]
|
||||
context.locations = [Location(Vector())]
|
||||
|
||||
|
||||
class Mirror(Compound):
|
||||
"""Generic Operation: Mirror
|
||||
|
||||
|
|
@ -357,118 +330,3 @@ class Mirror(Compound):
|
|||
mirrored_edges + mirrored_wires + mirrored_faces
|
||||
).wrapped
|
||||
)
|
||||
|
||||
|
||||
class PolarArray:
|
||||
"""Generic Operation: Polar Array
|
||||
|
||||
Push a polar array of locations to Part or Sketch
|
||||
|
||||
Args:
|
||||
radius (float): array radius
|
||||
start_angle (float): angle to first point from +ve X axis
|
||||
stop_angle (float): angle to last point from +ve X axis
|
||||
count (int): Number of points to push
|
||||
rotate (bool, optional): Align locations with arc tangents. Defaults to True.
|
||||
|
||||
Raises:
|
||||
ValueError: Count must be greater than or equal to 1
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
radius: float,
|
||||
start_angle: float,
|
||||
stop_angle: float,
|
||||
count: int,
|
||||
rotate: bool = True,
|
||||
):
|
||||
context: Builder = Builder._get_context()
|
||||
if count < 1:
|
||||
raise ValueError(f"At least 1 elements required, requested {count}")
|
||||
|
||||
angle_step = (stop_angle - start_angle) / count
|
||||
|
||||
# Note: rotate==False==0 so the location orientation doesn't change
|
||||
new_locations = [
|
||||
Location(
|
||||
Vector(radius, 0).rotateZ(start_angle + angle_step * i),
|
||||
Vector(0, 0, 1),
|
||||
rotate * angle_step * i,
|
||||
)
|
||||
for i in range(count)
|
||||
]
|
||||
|
||||
context.locations = new_locations
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate over the points typically in a for loop"""
|
||||
context: Builder = Builder._get_context()
|
||||
for location in context.locations:
|
||||
yield location.toTuple()[0]
|
||||
context.locations = [Location(Vector())]
|
||||
|
||||
|
||||
class PushPoints:
|
||||
"""Generic Operation: Push Points
|
||||
|
||||
Push sequence of locations to Part or Sketch
|
||||
|
||||
Args:
|
||||
pts (Union[VectorLike, Vertex, Location]): sequence of points to push
|
||||
"""
|
||||
|
||||
def __init__(self, *pts: Union[VectorLike, Vertex, Location]):
|
||||
context: Builder = Builder._get_context()
|
||||
new_locations = []
|
||||
for pt in pts:
|
||||
if isinstance(pt, Location):
|
||||
new_locations.append(pt)
|
||||
elif isinstance(pt, Vector):
|
||||
new_locations.append(Location(pt))
|
||||
elif isinstance(pt, Vertex):
|
||||
new_locations.append(Location(Vector(pt.toTuple())))
|
||||
elif isinstance(pt, tuple):
|
||||
new_locations.append(Location(Vector(pt)))
|
||||
else:
|
||||
raise ValueError(f"PushPoints doesn't accept type {type(pt)}")
|
||||
context.locations = new_locations
|
||||
|
||||
|
||||
class RectangularArray:
|
||||
"""Generic Operation: Rectangular Array
|
||||
|
||||
Push a rectangular array of locations to Part or Sketch
|
||||
|
||||
Args:
|
||||
x_spacing (float): horizontal spacing
|
||||
y_spacing (float): vertical spacing
|
||||
x_count (int): number of horizontal points
|
||||
y_count (int): number of vertical points
|
||||
|
||||
Raises:
|
||||
ValueError: Either x or y count must be greater than or equal to one.
|
||||
"""
|
||||
|
||||
def __init__(self, x_spacing: float, y_spacing: float, x_count: int, y_count: int):
|
||||
context: Builder = Builder._get_context()
|
||||
if x_count < 1 or y_count < 1:
|
||||
raise ValueError(
|
||||
f"At least 1 elements required, requested {x_count}, {y_count}"
|
||||
)
|
||||
|
||||
new_locations = []
|
||||
offset = Vector((x_count - 1) * x_spacing, (y_count - 1) * y_spacing) * 0.5
|
||||
for i, j in product(range(x_count), range(y_count)):
|
||||
new_locations.append(
|
||||
Location(Vector(i * x_spacing, j * y_spacing) - offset)
|
||||
)
|
||||
|
||||
context.locations = new_locations
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate over the points typically in a for loop"""
|
||||
context: Builder = Builder._get_context()
|
||||
for location in context.locations:
|
||||
yield location.toTuple()[0]
|
||||
context.locations = [Location(Vector())]
|
||||
|
|
|
|||
|
|
@ -62,11 +62,6 @@ class BuildPart(Builder):
|
|||
def _obj(self):
|
||||
return self.part
|
||||
|
||||
@property
|
||||
def workplane_count(self) -> int:
|
||||
"""Number of active workplanes"""
|
||||
return len(self.workplanes)
|
||||
|
||||
@property
|
||||
def pending_faces_count(self) -> int:
|
||||
"""Number of pending faces"""
|
||||
|
|
@ -86,12 +81,7 @@ class BuildPart(Builder):
|
|||
@property
|
||||
def pending_edges_as_wire(self) -> Wire:
|
||||
"""Return a wire representation of the pending edges"""
|
||||
return Wire.assembleEdges(list(*self.pending_edges.values()))
|
||||
|
||||
@property
|
||||
def pending_location_count(self) -> int:
|
||||
"""Number of current locations"""
|
||||
return len(self.locations)
|
||||
return Wire.assembleEdges(self.pending_edges)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -99,18 +89,16 @@ class BuildPart(Builder):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
self.part: Compound = None
|
||||
if isinstance(workplane, Plane):
|
||||
user_plane = workplane
|
||||
else:
|
||||
user_plane = Plane.named(workplane)
|
||||
|
||||
self.workplanes: list[Plane] = [user_plane]
|
||||
self.locations: list[Location] = [Location(user_plane.origin)]
|
||||
self.pending_faces: dict[int : list[Face]] = {0: []}
|
||||
self.pending_edges: dict[int : list[Edge]] = {0: []}
|
||||
initial_plane = (
|
||||
workplane if isinstance(workplane, Plane) else Plane.named(workplane)
|
||||
)
|
||||
# self.pending_faces: dict[int : list[Face]] = {0: []}
|
||||
# self.pending_edges: dict[int : list[Edge]] = {0: []}
|
||||
self.pending_faces: list[Face] = []
|
||||
self.pending_edges: list[Edge] = []
|
||||
self.last_faces = []
|
||||
self.last_solids = []
|
||||
super().__init__(mode)
|
||||
super().__init__(mode, initial_plane)
|
||||
|
||||
def vertices(self, select: Select = Select.ALL) -> ShapeList[Vertex]:
|
||||
"""Return Vertices from Part
|
||||
|
|
@ -203,25 +191,18 @@ class BuildPart(Builder):
|
|||
objects (Union[Edge, Face]): sequence of objects to add
|
||||
"""
|
||||
for obj in objects:
|
||||
for i, workplane in enumerate(self.workplanes):
|
||||
for loc in self.locations:
|
||||
localized_obj = workplane.fromLocalCoords(obj.moved(loc))
|
||||
if isinstance(obj, Face):
|
||||
logger.debug(
|
||||
f"Adding localized Face to pending_faces at {localized_obj.location()}"
|
||||
)
|
||||
if i in self.pending_faces:
|
||||
self.pending_faces[i].append(localized_obj)
|
||||
else:
|
||||
self.pending_faces[i] = [localized_obj]
|
||||
else:
|
||||
logger.debug(
|
||||
f"Adding localized Edge to pending_edges at {localized_obj.location()}"
|
||||
)
|
||||
if i in self.pending_edges:
|
||||
self.pending_edges[i].append(localized_obj)
|
||||
else:
|
||||
self.pending_edges[i] = [localized_obj]
|
||||
for loc in LocationList._get_context().locations:
|
||||
localized_obj = obj.moved(loc)
|
||||
if isinstance(obj, Face):
|
||||
logger.debug(
|
||||
f"Adding localized Face to pending_faces at {localized_obj.location()}"
|
||||
)
|
||||
self.pending_faces.append(localized_obj)
|
||||
else:
|
||||
logger.debug(
|
||||
f"Adding localized Edge to pending_edges at {localized_obj.location()}"
|
||||
)
|
||||
self.pending_edges.append(localized_obj)
|
||||
|
||||
def _get_and_clear_locations(self) -> list:
|
||||
"""Return location and planes from current points and workplanes and clear locations."""
|
||||
|
|
@ -363,19 +344,18 @@ class CounterBoreHole(Compound):
|
|||
hole_depth = (
|
||||
context.part.BoundingBox().DiagonalLength if depth is None else depth
|
||||
)
|
||||
location_planes = context._get_and_clear_locations()
|
||||
new_solids = [
|
||||
Solid.makeCylinder(
|
||||
radius, hole_depth, loc.position(), plane.zDir * -1.0
|
||||
).fuse(
|
||||
Solid.makeCylinder(radius, hole_depth, (0, 0, 0), (0, 0, -1))
|
||||
.fuse(
|
||||
Solid.makeCylinder(
|
||||
counter_bore_radius,
|
||||
counter_bore_depth,
|
||||
loc.position(),
|
||||
plane.zDir * -1.0,
|
||||
(0, 0, 0),
|
||||
(0, 0, -1),
|
||||
)
|
||||
)
|
||||
for loc, plane in location_planes
|
||||
.moved(location)
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
|
@ -408,20 +388,19 @@ class CounterSinkHole(Compound):
|
|||
context.part.BoundingBox().DiagonalLength if depth is None else depth
|
||||
)
|
||||
cone_height = counter_sink_radius / tan(radians(counter_sink_angle / 2.0))
|
||||
location_planes = context._get_and_clear_locations()
|
||||
new_solids = [
|
||||
Solid.makeCylinder(
|
||||
radius, hole_depth, loc.position(), plane.zDir * -1.0
|
||||
).fuse(
|
||||
Solid.makeCylinder(radius, hole_depth, (0, 0, 0), (0, 0, -1))
|
||||
.fuse(
|
||||
Solid.makeCone(
|
||||
counter_sink_radius,
|
||||
0.0,
|
||||
cone_height,
|
||||
loc.position(),
|
||||
plane.zDir * -1.0,
|
||||
(0, 0, 0),
|
||||
(0, 0, -1),
|
||||
)
|
||||
)
|
||||
for loc, plane in location_planes
|
||||
.moved(location)
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
|
@ -448,26 +427,24 @@ class Extrude(Compound):
|
|||
):
|
||||
new_solids: list[Solid] = []
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
for plane_index, faces in context.pending_faces.items():
|
||||
for face in faces:
|
||||
for face in context.pending_faces:
|
||||
new_solids.append(
|
||||
Solid.extrudeLinear(
|
||||
face,
|
||||
face.normalAt(face.Center()) * until,
|
||||
0,
|
||||
)
|
||||
)
|
||||
if both:
|
||||
new_solids.append(
|
||||
Solid.extrudeLinear(
|
||||
face,
|
||||
context.workplanes[plane_index].zDir * until,
|
||||
face.normalAt(face.Center()) * until * -1.0,
|
||||
0,
|
||||
)
|
||||
)
|
||||
if both:
|
||||
new_solids.append(
|
||||
Solid.extrudeLinear(
|
||||
face,
|
||||
context.workplanes[plane_index].zDir * until * -1.0,
|
||||
0,
|
||||
)
|
||||
)
|
||||
|
||||
context.pending_faces = {0: []}
|
||||
context.pending_faces = []
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
||||
|
|
@ -494,12 +471,11 @@ class Hole(Compound):
|
|||
hole_depth = (
|
||||
context.part.BoundingBox().DiagonalLength if depth is None else depth
|
||||
)
|
||||
location_planes = context._get_and_clear_locations()
|
||||
new_solids = [
|
||||
Solid.makeCylinder(
|
||||
radius, hole_depth, loc.position(), plane.zDir * -1.0, 360
|
||||
Solid.makeCylinder(radius, hole_depth, (0, 0, 0), (0, 0, -1), 360).moved(
|
||||
location
|
||||
)
|
||||
for loc, plane in location_planes
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
|
@ -522,15 +498,12 @@ class Loft(Solid):
|
|||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
if not sections:
|
||||
loft_wires = []
|
||||
for i in range(len(context.workplanes)):
|
||||
for face in context.pending_faces[i]:
|
||||
loft_wires.append(face.outerWire())
|
||||
loft_wires = [face.outerWire() for face in context.pending_faces]
|
||||
else:
|
||||
loft_wires = [section.outerWire() for section in sections]
|
||||
new_solid = Solid.makeLoft(loft_wires, ruled)
|
||||
|
||||
context.pending_faces = {0: []}
|
||||
context.pending_faces = []
|
||||
context._add_to_context(new_solid, mode=mode)
|
||||
super().__init__(new_solid.wrapped)
|
||||
|
||||
|
|
@ -563,7 +536,8 @@ class Revolve(Compound):
|
|||
angle = 360.0 if angle == 0 else angle
|
||||
|
||||
new_solids = []
|
||||
for i, workplane in enumerate(context.workplanes):
|
||||
# for i, workplane in enumerate(context.workplanes):
|
||||
for location in LocationList._get_context().locations:
|
||||
axis = []
|
||||
if axis_start is None:
|
||||
axis.append(workplane.fromLocalCoords(Vector(0, 0, 0)))
|
||||
|
|
@ -578,7 +552,7 @@ class Revolve(Compound):
|
|||
for face in context.pending_faces[i]:
|
||||
new_solids.append(Solid.revolve(face, angle, *axis))
|
||||
|
||||
context.pending_faces = {0: []}
|
||||
context.pending_faces = []
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
||||
|
|
@ -743,8 +717,8 @@ class Sweep(Compound):
|
|||
if sections:
|
||||
section_list = sections
|
||||
else:
|
||||
section_list = list(*context.pending_faces.values())
|
||||
context.pending_faces = {0: []}
|
||||
section_list = context.pending_faces
|
||||
context.pending_faces = []
|
||||
|
||||
if binormal is None and normal is not None:
|
||||
binormal_mode = Vector(normal)
|
||||
|
|
@ -754,12 +728,12 @@ class Sweep(Compound):
|
|||
binormal_mode = binormal
|
||||
|
||||
new_solids = []
|
||||
for workplane in context.workplanes:
|
||||
for location in LocationList._get_context().locations:
|
||||
if multisection:
|
||||
sections = [section.outerWire() for section in section_list]
|
||||
new_solid = Solid.sweep_multi(
|
||||
sections, path_wire, True, is_frenet, binormal_mode
|
||||
)
|
||||
).moved(location)
|
||||
else:
|
||||
for section in section_list:
|
||||
new_solid = Solid.sweep(
|
||||
|
|
@ -769,51 +743,13 @@ class Sweep(Compound):
|
|||
is_frenet,
|
||||
binormal_mode,
|
||||
transition.name.lower(),
|
||||
)
|
||||
new_solids.append(workplane.fromLocalCoords(new_solid))
|
||||
).moved(location)
|
||||
new_solids.append(new_solid)
|
||||
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
||||
|
||||
class Workplanes:
|
||||
"""Part Operation: Workplanes
|
||||
|
||||
Create workplanes from the given sequence of planes, optionally replacing existing
|
||||
workplanes.
|
||||
|
||||
Args:
|
||||
planes (PlaneLike): sequence of planes to use as workplanes.
|
||||
replace (bool, optional): replace existing workplanes. Defaults to True.
|
||||
"""
|
||||
|
||||
def __init__(self, *planes: PlaneLike, replace=True):
|
||||
user_planes = [
|
||||
user_plane if isinstance(user_plane, Plane) else Plane.named(user_plane)
|
||||
for user_plane in planes
|
||||
]
|
||||
BuildPart._get_context()._workplane(*user_planes, replace=replace)
|
||||
|
||||
|
||||
class WorkplanesFromFaces:
|
||||
"""Part Operation: Workplanes from Faces
|
||||
|
||||
Create workplanes from the given sequence of faces, optionally replacing existing
|
||||
workplanes. The workplane origin is aligned to the center of the face.
|
||||
|
||||
Args:
|
||||
faces (Face): sequence of faces to convert to workplanes.
|
||||
replace (bool, optional): replace existing workplanes. Defaults to True.
|
||||
"""
|
||||
|
||||
def __init__(self, *faces: Face, replace=True):
|
||||
new_planes = [
|
||||
Plane(origin=face.Center(), normal=face.normalAt(face.Center()))
|
||||
for face in faces
|
||||
]
|
||||
BuildPart._get_context()._workplane(*new_planes, replace=replace)
|
||||
|
||||
|
||||
#
|
||||
# Objects
|
||||
#
|
||||
|
|
@ -828,7 +764,6 @@ class Box(Compound):
|
|||
length (float): box size
|
||||
width (float): box size
|
||||
height (float): box size
|
||||
position (VectorLike, optional): initial position. Defaults to None.
|
||||
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes.
|
||||
Defaults to (True, True, True).
|
||||
|
|
@ -840,7 +775,6 @@ class Box(Compound):
|
|||
length: float,
|
||||
width: float,
|
||||
height: float,
|
||||
position: VectorLike = None,
|
||||
rotation: RotationLike = (0, 0, 0),
|
||||
centered: tuple[bool, bool, bool] = (True, True, True),
|
||||
mode: Mode = Mode.ADD,
|
||||
|
|
@ -848,14 +782,6 @@ class Box(Compound):
|
|||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
if position:
|
||||
location_planes = [
|
||||
(Location(plane.fromLocalCoords(Vector(position))), plane)
|
||||
for plane in context.workplanes
|
||||
]
|
||||
else:
|
||||
location_planes = context._get_and_clear_locations()
|
||||
|
||||
center_offset = Vector(
|
||||
-length / 2 if centered[0] else 0,
|
||||
-width / 2 if centered[1] else 0,
|
||||
|
|
@ -866,10 +792,10 @@ class Box(Compound):
|
|||
length,
|
||||
width,
|
||||
height,
|
||||
loc.position() + plane.fromLocalCoords(center_offset) - plane.origin,
|
||||
plane.zDir,
|
||||
).moved(rotate)
|
||||
for loc, plane in location_planes
|
||||
center_offset,
|
||||
Vector(0, 0, 1),
|
||||
).moved(location * rotate)
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
|
@ -885,7 +811,6 @@ class Cone(Compound):
|
|||
top_radius (float): top size, could be zero
|
||||
height (float): cone size
|
||||
arc_size (float, optional): angular size of cone. Defaults to 360.
|
||||
position (VectorLike, optional): initial position. Defaults to None.
|
||||
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes.
|
||||
Defaults to (True, True, True).
|
||||
|
|
@ -898,7 +823,6 @@ class Cone(Compound):
|
|||
top_radius: float,
|
||||
height: float,
|
||||
arc_size: float = 360,
|
||||
position: VectorLike = None,
|
||||
rotation: RotationLike = (0, 0, 0),
|
||||
centered: tuple[bool, bool, bool] = (True, True, True),
|
||||
mode: Mode = Mode.ADD,
|
||||
|
|
@ -906,13 +830,6 @@ class Cone(Compound):
|
|||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
if position:
|
||||
location_planes = [
|
||||
(Location(plane.fromLocalCoords(Vector(position))), plane)
|
||||
for plane in context.workplanes
|
||||
]
|
||||
else:
|
||||
location_planes = context._get_and_clear_locations()
|
||||
center_offset = Vector(
|
||||
0 if centered[0] else max(bottom_radius, top_radius),
|
||||
0 if centered[1] else max(bottom_radius, top_radius),
|
||||
|
|
@ -923,11 +840,11 @@ class Cone(Compound):
|
|||
bottom_radius,
|
||||
top_radius,
|
||||
height,
|
||||
loc.position() + plane.fromLocalCoords(center_offset) - plane.origin,
|
||||
plane.zDir,
|
||||
center_offset,
|
||||
Vector(0, 0, 1),
|
||||
arc_size,
|
||||
).moved(rotate)
|
||||
for loc, plane in location_planes
|
||||
).moved(location * rotate)
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
|
@ -942,7 +859,6 @@ class Cylinder(Compound):
|
|||
radius (float): cylinder size
|
||||
height (float): cylinder size
|
||||
arc_size (float, optional): angular size of cone. Defaults to 360.
|
||||
position (VectorLike, optional): initial position. Defaults to None.
|
||||
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes.
|
||||
Defaults to (True, True, True).
|
||||
|
|
@ -954,20 +870,12 @@ class Cylinder(Compound):
|
|||
radius: float,
|
||||
height: float,
|
||||
arc_size: float = 360,
|
||||
position: VectorLike = None,
|
||||
rotation: RotationLike = (0, 0, 0),
|
||||
centered: tuple[bool, bool, bool] = (True, True, True),
|
||||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
if position:
|
||||
location_planes = [
|
||||
(Location(plane.fromLocalCoords(Vector(position))), plane)
|
||||
for plane in context.workplanes
|
||||
]
|
||||
else:
|
||||
location_planes = context._get_and_clear_locations()
|
||||
center_offset = Vector(
|
||||
0 if centered[0] else radius,
|
||||
0 if centered[1] else radius,
|
||||
|
|
@ -977,11 +885,11 @@ class Cylinder(Compound):
|
|||
Solid.makeCylinder(
|
||||
radius,
|
||||
height,
|
||||
loc.position() + plane.fromLocalCoords(center_offset) - plane.origin,
|
||||
plane.zDir,
|
||||
center_offset,
|
||||
Vector(0, 0, 1),
|
||||
arc_size,
|
||||
).moved(rotate)
|
||||
for loc, plane in location_planes
|
||||
).moved(location * rotate)
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
|
@ -1018,13 +926,6 @@ class Sphere(Compound):
|
|||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
if position:
|
||||
location_planes = [
|
||||
(Location(plane.fromLocalCoords(Vector(position))), plane)
|
||||
for plane in context.workplanes
|
||||
]
|
||||
else:
|
||||
location_planes = context._get_and_clear_locations()
|
||||
center_offset = Vector(
|
||||
0 if centered[0] else radius,
|
||||
0 if centered[1] else radius,
|
||||
|
|
@ -1033,13 +934,13 @@ class Sphere(Compound):
|
|||
new_solids = [
|
||||
Solid.makeSphere(
|
||||
radius,
|
||||
loc.position() + plane.fromLocalCoords(center_offset) - plane.origin,
|
||||
plane.zDir,
|
||||
center_offset,
|
||||
(0, 0, 1),
|
||||
arc_size1,
|
||||
arc_size2,
|
||||
arc_size3,
|
||||
).moved(rotate)
|
||||
for loc, plane in location_planes
|
||||
).moved(location * rotate)
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
|
@ -1093,12 +994,12 @@ class Torus(Compound):
|
|||
Solid.makeTorus(
|
||||
major_radius,
|
||||
minor_radius,
|
||||
loc.position() + plane.fromLocalCoords(center_offset) - plane.origin,
|
||||
plane.zDir,
|
||||
center_offset,
|
||||
Vector(0, 0, 1),
|
||||
major_arc_size,
|
||||
minor_arc_size,
|
||||
).moved(rotate)
|
||||
for loc, plane in location_planes
|
||||
).moved(location * rotate)
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
|
@ -1117,7 +1018,6 @@ class Wedge(Compound):
|
|||
zmin (float): minimum Z location
|
||||
xmax (float): maximum X location
|
||||
zmax (float): maximum Z location
|
||||
position (VectorLike, optional): initial position. Defaults to None.
|
||||
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
|
||||
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
|
||||
"""
|
||||
|
|
@ -1131,25 +1031,15 @@ class Wedge(Compound):
|
|||
zmin: float,
|
||||
xmax: float,
|
||||
zmax: float,
|
||||
position: VectorLike = None,
|
||||
rotation: RotationLike = (0, 0, 0),
|
||||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
if position:
|
||||
location_planes = [
|
||||
(Location(plane.fromLocalCoords(Vector(position))), plane)
|
||||
for plane in context.workplanes
|
||||
]
|
||||
else:
|
||||
location_planes = context._get_and_clear_locations()
|
||||
new_solids = [
|
||||
Solid.makeWedge(
|
||||
dx, dy, dz, xmin, zmin, xmax, zmax, loc.position(), plane.zDir
|
||||
).moved(rotate)
|
||||
for loc, plane in location_planes
|
||||
Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax).moved(location * rotate)
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
super().__init__(Compound.makeCompound(new_solids).wrapped)
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class BuildSketch(Builder):
|
|||
def __init__(self, mode: Mode = Mode.ADD):
|
||||
self.sketch: Compound = None
|
||||
self.pending_edges: ShapeList[Edge] = ShapeList()
|
||||
self.locations: list[Location] = [Location(Vector())]
|
||||
# self.locations: list[Location] = [Location(Vector())]
|
||||
self.last_faces = []
|
||||
super().__init__(mode)
|
||||
|
||||
|
|
@ -335,10 +335,11 @@ class Circle(Compound):
|
|||
face = Face.makeFromWires(Wire.makeCircle(radius, *z_axis)).moved(
|
||||
Location(center_offset)
|
||||
)
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -381,10 +382,11 @@ class Ellipse(Compound):
|
|||
)
|
||||
face = face.moved(Location(center_offset))
|
||||
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -416,10 +418,11 @@ class Polygon(Compound):
|
|||
0 if centered[1] else bounding_box.ylen / 2,
|
||||
)
|
||||
face = face.moved(Location(center_offset))
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -453,10 +456,11 @@ class Rectangle(Compound):
|
|||
)
|
||||
face = face.moved(Location(center_offset))
|
||||
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -497,10 +501,11 @@ class RegularPolygon(Compound):
|
|||
)
|
||||
face = face.moved(Location(center_offset))
|
||||
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -531,10 +536,11 @@ class SlotArc(Compound):
|
|||
raise ValueError("Bug - Edges aren't supported by offset")
|
||||
# arc_wire = arc if isinstance(arc, Wire) else Wire.assembleEdges([arc])
|
||||
face = Face.makeFromWires(arc.offset2D(height / 2)[0]).rotate(*z_axis, rotation)
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -573,10 +579,11 @@ class SlotCenterPoint(Compound):
|
|||
]
|
||||
)[0].offset2D(height / 2)[0]
|
||||
).rotate(*z_axis, rotation)
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -609,10 +616,11 @@ class SlotCenterToCenter(Compound):
|
|||
]
|
||||
).offset2D(height / 2)[0]
|
||||
).rotate(*z_axis, rotation)
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -644,10 +652,11 @@ class SlotOverall(Compound):
|
|||
]
|
||||
).offset2D(height / 2)[0]
|
||||
).rotate(*z_axis, rotation)
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -697,11 +706,13 @@ class Text(Compound):
|
|||
position_on_path,
|
||||
path,
|
||||
).rotate(Vector(), Vector(0, 0, 1), rotation)
|
||||
new_compounds = [text_string.moved(location) for location in context.locations]
|
||||
new_compounds = [
|
||||
text_string.moved(location)
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
new_faces = [face for compound in new_compounds for face in compound]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
||||
|
||||
|
|
@ -758,8 +769,9 @@ class Trapezoid(Compound):
|
|||
0 if centered[1] else bounding_box.ylen / 2,
|
||||
)
|
||||
face = face.moved(Location(center_offset))
|
||||
new_faces = [face.moved(location) for location in context.locations]
|
||||
new_faces = [
|
||||
face.moved(location) for location in LocationList._get_context().locations
|
||||
]
|
||||
for face in new_faces:
|
||||
context._add_to_context(face, mode=mode)
|
||||
context.locations = [Location(Vector())]
|
||||
super().__init__(Compound.makeCompound(new_faces).wrapped)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue