mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 10:41:20 -08:00
305 lines
11 KiB
Python
305 lines
11 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, centered=(True, True, False)
|
|
)
|
|
with BuildPart(pin.part.faces().sort_by(Axis.Z)[-1]) as pin_head:
|
|
Cylinder(
|
|
radius=barrel_diameter / 2,
|
|
height=pin_diameter,
|
|
centered=(True, True, False),
|
|
)
|
|
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)
|
|
with Workplanes(
|
|
lid_builder.part.faces().sort_by(Axis.Z)[-1].located(Location((-13 * CM, 0, 0)))
|
|
):
|
|
with GridLocations(0, 40 * MM, 1, 3):
|
|
Hole(3 * MM, 1 * CM)
|
|
RigidJoint(
|
|
"hinge_attachment",
|
|
lid_builder.part,
|
|
Location((-15 * CM, 0, 5 * MM), (180, 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
|
|
)
|
|
|
|
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")
|