mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 02:30:55 -08:00
Added instance variables to BuildPart, updated tutorial
This commit is contained in:
parent
91aa315bae
commit
e991ba05a0
4 changed files with 117 additions and 23 deletions
|
|
@ -12,8 +12,7 @@ Step 1: Setup
|
|||
Before getting to the CAD operations, this Lego script needs to import the build123d
|
||||
environment. There are over 100 python classes in build123d so we'll just import them
|
||||
all with a ``from build123d import *`` but there are other options that we won't explore
|
||||
here. In addition, the ``Plane`` object from ``cadquery`` will be used so we'll import
|
||||
that class as well.
|
||||
here.
|
||||
|
||||
The dimensions of the Lego block follow. A key parameter is ``pip_count``, the length
|
||||
of the Lego blocks in pips. This parameter must be at least 2.
|
||||
|
|
@ -184,14 +183,18 @@ could be done with another sketch, we'll add a box to the top of the walls.
|
|||
:lines: 46-76
|
||||
:emphasize-lines: 23-31
|
||||
|
||||
To position the top, we'll create a new location context ``Locations`` at the center
|
||||
and at the height of the walls. To determine the height we'll extract that from the
|
||||
To position the top, we'll create a new location context ``Workplanes`` at the center
|
||||
and at the height of the walls. Much like the location contexts, the ``Workplanes`` context
|
||||
creates one or more planes that
|
||||
can be used to position further features. To determine the height we'll extract that from the
|
||||
``lego.part`` by using the ``vertices()`` method which returns a list of the positions
|
||||
of all of the vertices of the Lego block so far. Since we're interested in the top,
|
||||
we'll sort by the vertical (Z) axis and take the top of the list (index -1). Finally,
|
||||
the ``z`` property of this vertex will return just the height of the top.
|
||||
we'll sort by the vertical (Z) axis and take the top of the list ``>> Axis.Z``. Finally,
|
||||
the ``Z`` property of this vertex will return just the height of the top. Note that
|
||||
the ``X`` and ``Y`` values are not used from the selected vertex as there are no
|
||||
vertices in the center of the block.
|
||||
|
||||
Within the scope of this ``Locations`` context, a ``Box`` is created, centered at
|
||||
Within the scope of this ``Workplanes`` context, a ``Box`` is created, centered at
|
||||
the intersection of the x and y axis but not in the z thus aligning with the top of the walls.
|
||||
|
||||
The base is closed now as shown here:
|
||||
|
|
@ -210,9 +213,8 @@ a new workplane on top of the block where we can position the pips.
|
|||
:lines: 46-81
|
||||
:emphasize-lines: 32-36
|
||||
|
||||
Much like the location contexts, the ``Workplanes`` context creates one or more planes that
|
||||
can be used to position further features. In this case, the workplane is created from the
|
||||
top Face of the Lego block by using the ``faces`` method and then sorted vertically.
|
||||
In this case, the workplane is created from the top Face of the Lego block by using the
|
||||
``faces`` method and then sorted vertically and taking the top one ``>> Axis.Z``.
|
||||
|
||||
On the new workplane, a grid of locations is created and a number of ``Cylinder``'s are positioned
|
||||
at each location.
|
||||
|
|
@ -220,7 +222,6 @@ at each location.
|
|||
.. image:: tutorial_step11.svg
|
||||
:align: center
|
||||
|
||||
|
||||
This completes the Lego block. To access the finished product, refer to the builder's internal
|
||||
object as shown here:
|
||||
|
||||
|
|
|
|||
|
|
@ -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, base_height - wall_thickness), normal=(0, 0, 1))
|
||||
Plane(origin=(0, 0, (lego.vertices() >> Axis.Z).Z), normal=(0, 0, 1))
|
||||
):
|
||||
Box(
|
||||
block_length,
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ class BuildLine(Builder):
|
|||
self.locations: list[Location] = [Location(Vector())]
|
||||
super().__init__(mode)
|
||||
|
||||
def faces(self):
|
||||
"""Override the base Builder class definition of faces()"""
|
||||
return NotImplementedError("faces() doesn't apply to BuildLine")
|
||||
|
||||
def wires(self, select: Select = Select.ALL) -> ShapeList[Wire]:
|
||||
"""Return Wires from Line
|
||||
|
||||
|
|
|
|||
|
|
@ -279,9 +279,14 @@ class CounterBoreHole(Compound):
|
|||
mode: Mode = Mode.SUBTRACT,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context)
|
||||
|
||||
self.radius = radius
|
||||
self.counter_bore_radius = counter_bore_radius
|
||||
self.counter_bore_depth = counter_bore_depth
|
||||
self.depth = depth
|
||||
self.mode = mode
|
||||
|
||||
new_solids = []
|
||||
for location in LocationList._get_context().locations:
|
||||
hole_depth = (
|
||||
|
|
@ -329,9 +334,14 @@ class CounterSinkHole(Compound):
|
|||
mode: Mode = Mode.SUBTRACT,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context)
|
||||
|
||||
self.radius = radius
|
||||
self.counter_sink_radius = counter_sink_radius
|
||||
self.depth = depth
|
||||
self.counter_sink_angle = counter_sink_angle
|
||||
self.mode = mode
|
||||
|
||||
for location in LocationList._get_context().locations:
|
||||
hole_depth = (
|
||||
context.part.fuse(Solid.makeBox(1, 1, 1).locate(location))
|
||||
|
|
@ -388,11 +398,17 @@ class Extrude(Compound):
|
|||
taper: float = 0.0,
|
||||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
new_solids: list[Solid] = []
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context, [to_extrude])
|
||||
|
||||
self.to_extrude = to_extrude
|
||||
self.amount = amount
|
||||
self.until = until
|
||||
self.both = both
|
||||
self.taper = taper
|
||||
self.mode = mode
|
||||
|
||||
new_solids: list[Solid] = []
|
||||
if not to_extrude and not context.pending_faces:
|
||||
raise ValueError("Either a face or a pending face must be provided")
|
||||
|
||||
|
|
@ -518,9 +534,12 @@ class Hole(Compound):
|
|||
mode: Mode = Mode.SUBTRACT,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context)
|
||||
|
||||
self.radius = radius
|
||||
self.depth = depth
|
||||
self.mode = mode
|
||||
|
||||
# To ensure the hole will go all the way through the part when
|
||||
# no depth is specified, calculate depth based on the part and
|
||||
# hole location. In this case start the hole above the part
|
||||
|
|
@ -560,9 +579,12 @@ class Loft(Solid):
|
|||
def __init__(self, *sections: Face, ruled: bool = False, mode: Mode = Mode.ADD):
|
||||
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context, sections)
|
||||
|
||||
self.sections = sections
|
||||
self.ruled = ruled
|
||||
self.mode = mode
|
||||
|
||||
if not sections:
|
||||
loft_wires = [face.outerWire() for face in context.pending_faces]
|
||||
context.pending_faces = []
|
||||
|
|
@ -605,9 +627,13 @@ class Revolve(Compound):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context, profiles)
|
||||
|
||||
self.profiles = profiles
|
||||
self.axis = axis
|
||||
self.revolution_arc = revolution_arc
|
||||
self.mode = mode
|
||||
|
||||
# Make sure we account for users specifying angles larger than 360 degrees, and
|
||||
# for OCCT not assuming that a 0 degree revolve means a 360 degree revolve
|
||||
angle = revolution_arc % 360.0
|
||||
|
|
@ -675,9 +701,12 @@ class Section(Compound):
|
|||
mode: Mode = Mode.INTERSECT,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context)
|
||||
|
||||
self.section_by = section_by
|
||||
self.height = height
|
||||
self.mode = mode
|
||||
|
||||
max_size = context.part.BoundingBox().DiagonalLength
|
||||
|
||||
section_planes = (
|
||||
|
|
@ -737,9 +766,17 @@ class Sweep(Compound):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context, sections)
|
||||
|
||||
self.sections = sections
|
||||
self.path = path
|
||||
self.multisection = multisection
|
||||
self.is_frenet = is_frenet
|
||||
self.transition = transition
|
||||
self.normal = normal
|
||||
self.binormal = binormal
|
||||
self.mode = mode
|
||||
|
||||
if path is None:
|
||||
path_wire = context.pending_edges_as_wire
|
||||
else:
|
||||
|
|
@ -811,10 +848,17 @@ class Box(Compound):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context)
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
|
||||
self.length = length
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.rotation = rotate
|
||||
self.centered = centered
|
||||
self.mode = mode
|
||||
|
||||
center_offset = Vector(
|
||||
-length / 2 if centered[0] else 0,
|
||||
-width / 2 if centered[1] else 0,
|
||||
|
|
@ -861,10 +905,18 @@ class Cone(Compound):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildPart = BuildPart._get_context()
|
||||
|
||||
validate_inputs(self, context)
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
|
||||
self.bottom_radius = bottom_radius
|
||||
self.top_radius = top_radius
|
||||
self.height = height
|
||||
self.arc_size = arc_size
|
||||
self.rotation = rotate
|
||||
self.centered = centered
|
||||
self.mode = mode
|
||||
|
||||
center_offset = Vector(
|
||||
0 if centered[0] else max(bottom_radius, top_radius),
|
||||
0 if centered[1] else max(bottom_radius, top_radius),
|
||||
|
|
@ -913,6 +965,14 @@ class Cylinder(Compound):
|
|||
validate_inputs(self, context)
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
|
||||
self.radius = radius
|
||||
self.height = height
|
||||
self.arc_size = arc_size
|
||||
self.rotation = rotate
|
||||
self.centered = centered
|
||||
self.mode = mode
|
||||
|
||||
center_offset = Vector(
|
||||
0 if centered[0] else radius,
|
||||
0 if centered[1] else radius,
|
||||
|
|
@ -962,6 +1022,15 @@ class Sphere(Compound):
|
|||
validate_inputs(self, context)
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
|
||||
self.radius = radius
|
||||
self.arc_size1 = arc_size1
|
||||
self.arc_size2 = arc_size2
|
||||
self.arc_size3 = arc_size3
|
||||
self.rotation = rotate
|
||||
self.centered = centered
|
||||
self.mode = mode
|
||||
|
||||
center_offset = Vector(
|
||||
0 if centered[0] else radius,
|
||||
0 if centered[1] else radius,
|
||||
|
|
@ -1013,6 +1082,15 @@ class Torus(Compound):
|
|||
validate_inputs(self, context)
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
|
||||
self.major_radius = major_radius
|
||||
self.minor_radius = minor_radius
|
||||
self.major_arc_size = major_arc_size
|
||||
self.minor_arc_size = minor_arc_size
|
||||
self.rotation = rotate
|
||||
self.centered = centered
|
||||
self.mode = mode
|
||||
|
||||
center_offset = Vector(
|
||||
0 if centered[0] else major_radius,
|
||||
0 if centered[1] else major_radius,
|
||||
|
|
@ -1066,6 +1144,17 @@ class Wedge(Compound):
|
|||
validate_inputs(self, context)
|
||||
|
||||
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
|
||||
|
||||
self.dx = dx
|
||||
self.dy = dy
|
||||
self.dz = dz
|
||||
self.xmin = xmin
|
||||
self.zmin = zmin
|
||||
self.xmax = xmax
|
||||
self.zmax = zmax
|
||||
self.rotation = rotate
|
||||
self.mode = mode
|
||||
|
||||
new_solids = [
|
||||
Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax).moved(location * rotate)
|
||||
for location in LocationList._get_context().locations
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue