build123d/docs/tutorial_joints.py
2023-01-25 15:03:58 -05:00

319 lines
12 KiB
Python

"""
name: tutorial_joints.py
by: Gumyr
date: December 27h 2022
desc:
This example a box with a lid attached with a hinge.
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 *
# SVG Export options
svg_opts = {"pixel_scale": 5, "show_axes": False, "show_hidden": True}
class Hinge(Compound):
"""Hinge
Half a simple hinge with several joints. The joints are:
- "leaf": RigidJoint where hinge attaches to object
- "hinge_axis": RigidJoint (inner) or RevoluteJoint (outer)
- "hole0", "hole1", "hole2": CylindricalJoints for attachment screws
Args:
width (float): width of one leaf
length (float): hinge length
barrel_diameter (float): size of hinge pin barrel
thickness (float): hinge leaf thickness
pin_diameter (float): hinge pin diameter
inner (bool, optional): inner or outer half of hinge . Defaults to True.
"""
def __init__(
self,
width: float,
length: float,
barrel_diameter: float,
thickness: float,
pin_diameter: float,
inner: bool = True,
):
# The profile of the hinge used to create the tabs
with BuildPart() as hinge_profile:
with BuildSketch():
for i, loc in enumerate(
GridLocations(0, length / 5, 1, 5, centered=(False, False))
):
if i % 2 == inner:
with Locations(loc):
Rectangle(width, length / 5, centered=(False, False))
Rectangle(
width - barrel_diameter,
length,
centered=(False, False),
)
Extrude(amount=-barrel_diameter)
# The hinge pin
with BuildPart() as pin:
Cylinder(
radius=pin_diameter / 2,
height=length,
align=(Align.CENTER, Align.CENTER, Align.MIN),
)
with BuildPart(pin.part.faces().sort_by(Axis.Z)[-1]) as pin_head:
Cylinder(
radius=barrel_diameter / 2,
height=pin_diameter,
align=(Align.CENTER, Align.CENTER, Align.MIN),
)
Fillet(
*pin_head.edges(Select.LAST).filter_by(GeomType.CIRCLE),
radius=pin_diameter / 3,
)
# Either the external and internal leaf with joints
with BuildPart() as leaf_builder:
with BuildSketch():
with BuildLine():
l1 = Line((0, 0), (width - barrel_diameter / 2, 0))
l2 = RadiusArc(
l1 @ 1,
l1 @ 1 + Vector(0, barrel_diameter),
-barrel_diameter / 2,
)
l3 = RadiusArc(
l2 @ 1,
(
width - barrel_diameter,
barrel_diameter / 2,
),
-barrel_diameter / 2,
)
l4 = Line(l3 @ 1, (width - barrel_diameter, thickness))
l5 = Line(l4 @ 1, (0, thickness))
Line(l5 @ 1, l1 @ 0)
MakeFace()
with Locations(
(width - barrel_diameter / 2, barrel_diameter / 2)
) as pin_center:
Circle(pin_diameter / 2 + 0.1 * MM, mode=Mode.SUBTRACT)
Extrude(amount=length)
Add(hinge_profile.part, rotation=(90, 0, 0), mode=Mode.INTERSECT)
# Create holes for fasteners
with Workplanes(leaf_builder.part.faces().filter_by(Axis.Y)[-1]):
with GridLocations(0, length / 3, 1, 3):
holes = CounterSinkHole(3 * MM, 5 * MM)
# Add the hinge pin to the external leaf
if not inner:
with Locations(pin_center.locations[0]):
Add(pin.part)
#
# Create the Joints
#
# Leaf attachment
RigidJoint(
label="leaf",
to_part=leaf_builder.part,
joint_location=Location(
(width - barrel_diameter, 0, length / 2), (90, 0, 0)
),
)
# Hinge axis (fixed with inner)
if inner:
RigidJoint(
"hinge_axis",
leaf_builder.part,
Location((width - barrel_diameter / 2, barrel_diameter / 2, 0)),
)
else:
RevoluteJoint(
"hinge_axis",
leaf_builder.part,
axis=Axis(
(width - barrel_diameter / 2, barrel_diameter / 2, 0), (0, 0, 1)
),
angular_range=(90, 270),
)
# Fastener holes
hole_locations = [hole.location for hole in holes]
for hole, hole_location in enumerate(hole_locations):
CylindricalJoint(
label="hole" + str(hole),
to_part=leaf_builder.part,
axis=hole_location.to_axis(),
linear_range=(0, 2 * CM),
angular_range=(0, 360),
)
super().__init__(leaf_builder.part.wrapped, joints=leaf_builder.part.joints)
# Create instances of the two leaves of the hinge
hinge_inner = Hinge(
width=5 * CM,
length=12 * CM,
barrel_diameter=1 * CM,
thickness=2 * MM,
pin_diameter=4 * MM,
)
hinge_outer = Hinge(
width=5 * CM,
length=12 * CM,
barrel_diameter=1 * CM,
thickness=2 * MM,
pin_diameter=4 * MM,
inner=False,
)
Compound.make_compound(
[
hinge_inner,
hinge_inner.joints["leaf"].symbol,
hinge_inner.joints["hinge_axis"].symbol,
hinge_inner.joints["hole0"].symbol,
hinge_inner.joints["hole1"].symbol,
hinge_inner.joints["hole2"].symbol,
]
).export_svg(
"tutorial_joint_inner_leaf.svg", (100, 100, -50), (0, 0, 1), svg_opts=svg_opts
)
Compound.make_compound(
[
hinge_outer,
hinge_outer.joints["leaf"].symbol,
hinge_outer.joints["hinge_axis"].symbol,
hinge_outer.joints["hole0"].symbol,
hinge_outer.joints["hole1"].symbol,
hinge_outer.joints["hole2"].symbol,
]
).export_svg(
"tutorial_joint_outer_leaf.svg", (100, 100, -50), (0, 0, 1), svg_opts=svg_opts
)
# Create the box with a RigidJoint to mount the hinge
with BuildPart() as box_builder:
Box(30 * CM, 30 * CM, 10 * CM)
Offset(amount=-1 * CM, openings=box_builder.faces().sort_by(Axis.Z)[-1])
# Create a notch for the hinge
with Locations((-15 * CM, 0, 5 * CM)):
Box(2 * CM, 12 * CM, 4 * MM, mode=Mode.SUBTRACT)
with Workplanes(
box_builder.part.faces().sort_by(Axis.X)[0].located(Location((0, 0, 2 * CM)))
):
with GridLocations(0, 40 * MM, 1, 3):
Hole(3 * MM, 1 * CM)
RigidJoint(
"hinge_attachment",
box_builder.part,
Location((-15 * CM, 0, 4 * CM), (180, 90, 0)),
)
# Demonstrate that objects with Joints can be moved and the joints follow
box = box_builder.part.moved(Location((0, 0, 5 * CM)))
Compound.make_compound([box, box.joints["hinge_attachment"].symbol]).export_svg(
"tutorial_joint_box.svg", (-100, 100, 150), (0, 0, 1), svg_opts=svg_opts
)
# The lid with a RigidJoint for the hinge
with BuildPart() as lid_builder:
Box(30 * CM, 30 * CM, 1 * CM, align=(Align.MIN, Align.CENTER, Align.MIN))
with Locations((2 * CM, 0, 0)):
with GridLocations(0, 40 * MM, 1, 3):
Hole(3 * MM, 1 * CM)
RigidJoint(
"hinge_attachment",
lid_builder.part,
Location((0, 0, 0), (0, 0, 180)),
)
lid = lid_builder.part
Compound.make_compound([lid, lid.joints["hinge_attachment"].symbol]).export_svg(
"tutorial_joint_lid.svg", (-100, 100, 150), (0, 0, 1), svg_opts=svg_opts
)
# A screw to attach the hinge to the box
m6_screw = Compound.import_step("M6-1x12-countersunk-screw.step")
m6_joint = RigidJoint("head", m6_screw, Location((0, 0, 0), (1, 0, 0), 180))
Compound.make_compound([m6_screw, m6_joint.symbol]).export_svg(
"tutorial_joint_m6_screw.svg",
(-100, 100, 150),
(0, 0, 1),
svg_opts={"pixel_scale": 20, "show_axes": False, "show_hidden": False},
)
# Connect the parts together
box.joints["hinge_attachment"].connect_to(hinge_outer.joints["leaf"])
hinge_outer.joints["hinge_axis"].connect_to(hinge_inner.joints["hinge_axis"], angle=120)
hinge_inner.joints["leaf"].connect_to(lid.joints["hinge_attachment"])
hinge_outer.joints["hole2"].connect_to(m6_joint, position=5, angle=30)
Compound.make_compound([box, hinge_outer]).export_svg(
"tutorial_joint_box_outer.svg", (-100, -100, 50), (0, 0, 1), svg_opts=svg_opts
)
Compound.make_compound([box, hinge_outer, hinge_inner]).export_svg(
"tutorial_joint_box_outer_inner.svg", (-100, -100, 50), (0, 0, 1), svg_opts=svg_opts
)
Compound.make_compound([box, hinge_outer, hinge_inner, lid]).export_svg(
"tutorial_joint_box_outer_inner_lid.svg",
(-100, -100, 50),
(0, 0, 1),
svg_opts=svg_opts,
)
Compound.make_compound([box, lid, hinge_inner, hinge_outer, m6_screw]).export_svg(
"tutorial_joint.svg", (-100, -100, 50), (0, 0, 1), svg_opts=svg_opts
)
box.label = "box"
lid.label = "lid"
hinge_outer.label = "outer hinge"
hinge_inner.label = "inner hinge"
m6_screw.label = "M6 screw"
# Create assembly and display it
box_assembly = Compound(label="assembly", children=[box, lid, hinge_inner, hinge_outer])
print(box_assembly.show_topology())
# Add to the assembly by assigning the parent attribute of an object
m6_screw.parent = box_assembly
print(box_assembly.show_topology())
if "show_object" in locals():
show_object(box, name="box", options={"alpha": 0.8})
# show_object(box.joints["hinge_attachment"].symbol, name="box attachment point")
show_object(hinge_outer, name="hinge_outer")
# show_object(hinge_outer.joints["leaf"].symbol, name="hinge_outer leaf joint")
# show_object(hinge_outer.joints["hinge_axis"].symbol, name="hinge_outer hinge axis")
show_object(lid, name="lid")
# show_object(lid.joints["hinge_attachment"].symbol, name="lid attachment point")
show_object(hinge_inner, name="hinge_inner")
# show_object(hinge_inner.joints["leaf"].symbol, name="hinge_inner leaf joint")
# show_object(hinge_inner.joints["hinge_axis"].symbol, name="hinge_inner hinge axis")
# for hole in [0, 1, 2]:
# show_object(
# hinge_inner.joints["hole" + str(hole)].symbol,
# name="hinge_inner hole " + str(hole),
# )
# show_object(
# hinge_outer.joints["hole" + str(hole)].symbol,
# name="hinge_outer hole " + str(hole),
# )
show_object(m6_screw, name="m6 screw")
# show_object(m6_joint.symbol, name="m6 screw symbol")
show_object(box_assembly, name="box assembly")