"""
OpenBuilds Parts
name: open_builds.py
by: Gumyr
date: June 27th 2024
desc: This python/build123d code is a parameterized set of models of parts from
https://openbuildspartstore.com/.
license:
Copyright 2024 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.
"""
import copy
import math
from build123d import *
from build123d import tuplify
from typing import Union, Literal
from bd_warehouse.bearing import SingleRowCappedDeepGrooveBallBearing
from bd_warehouse.fastener import (
ClearanceHole,
HexNut,
LowProfileScrew,
SetScrew,
SocketHeadCapScrew,
ThreadedHole,
)
from bd_warehouse.thread import MetricTrapezoidalThread, TrapezoidalThread
CAVITY_RADIUS = 2.3 * MM
FILLET_RADIUS = 1.5 * MM
m5 = SocketHeadCapScrew("M5-0.8", 20 * MM) # Used to create threaded holes
[docs]
class AcmeAntiBacklashNutBlock8mm(BasePartObject):
"""Part Object: OpenBuilds 8mm Acme Anti Backlash Nut Block
This Anti-Backlash Nut Block for 8mm Lead Screw is a great choice for many build
projects requiring lead screw linear motion where high precision and repeatability
with zero play or slop is needed.
Product Features:
- Screw and nut for secure and precise backlash adjustment
- Recesses screw holes for non obtrusive placement
- Can be mounted on plates or V-Slot
- Works best with OpenBuilds Lead Screw
Specifications:
- Tr8*8(p2) Metric Acme Tap (compatible with our customized 8mm Metric Acme Lead
Screws)
- Mounting Hole Spacing: 20mm
- Pitch: 2mm
- Lead: 8mm
- Delrin
- Color: Black
- These Anti-Backlash Nut Blocks have been customized to work directly with the
OpenBuilds system.
Args:
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
with BuildPart() as block:
with BuildSketch(Plane.XY.offset(-6)) as bs:
RectangleRounded(34, 33, 3)
with Locations((-9, 6)):
Circle(2.5, mode=Mode.SUBTRACT)
Rectangle(
26, 5, align=(Align.MIN, Align.CENTER), mode=Mode.SUBTRACT
)
with Locations((10, -6.5), (-10, -6.5)):
Circle(2.55, mode=Mode.SUBTRACT)
extrude(amount=12)
with BuildSketch(Plane.XY.offset(6)) as bs:
with Locations((10, -6.5), (-10, -6.5)) as mounts:
Circle(4.5)
extrude(amount=-2, mode=Mode.SUBTRACT)
with BuildSketch(Plane.XY.offset(-6)) as bs:
with Locations((10, -6.5), (-10, -6.5)):
RegularPolygon(4.618802153517, 6, rotation=30)
extrude(amount=5, mode=Mode.SUBTRACT)
with Locations(Plane.XZ):
Hole(4)
with Locations(-Plane.XZ.offset(-17)):
with Locations((10, 0)) as screw_hole:
ThreadedHole(m5, depth=10, counter_sunk=False)
super().__init__(block.part, rotation=rotation, align=align, mode=mode)
self.color = Color(0x030303)
self.label = "AcmeAntiBacklashNutBlock8mm"
for label, loc in zip(["a", "b"], mounts):
RigidJoint(label, self, Pos(*(loc.position + Vector(0, 0, 6))))
for label, loc in zip(["nut_a", "nut_b"], mounts):
RigidJoint(label, self, loc * Location((0, 0, -6), (0, 0, 1), 30))
RigidJoint("screw", self, screw_hole.locations[0])
[docs]
class AcmeAntiBacklashNutBlock8mmAssembly(Compound):
"""Assembly: OpenBuilds 8mm Acme Anti Backlash Nut Block Assembly
All of the components in anti backlash nut assembly:
- AcmeAntiBacklashNutBlock8mm x 1
- AluminumSpacer x 2
- M5 HexNut x 2
- SocketHeadCapScrew x 1
The RigidJoint "a" or "b" - positioned at the top of the spaces - can be used to
connect this assembly to a gantry plate.
"""
def __init__(self):
super().__init__()
screw = SocketHeadCapScrew("M5-0.8", 13 * MM)
nut0 = HexNut("M5-0.8")
nut1 = copy.copy(nut0)
acme_nut = Rot(Z=-90) * AcmeAntiBacklashNutBlock8mm()
s0 = AluminumSpacer("6mm")
s1 = copy.copy(s0)
acme_nut.joints["a"].connect_to(s0.joints["a"])
acme_nut.joints["b"].connect_to(s1.joints["a"])
acme_nut.joints["nut_a"].connect_to(nut0.joints["a"])
acme_nut.joints["nut_b"].connect_to(nut1.joints["a"])
acme_nut.joints["screw"].connect_to(screw.joints["a"])
self.children = [acme_nut, s0, s1, nut0, nut1, screw]
self.label = "AcmeAntiBacklashNutBlock8mmAssembly"
RigidJoint("a", self, s0.joints["b"].location * Rot(Z=90))
RigidJoint("b", self, s1.joints["b"].location * Rot(Z=90))
[docs]
class AluminumSpacer(BasePartObject):
"""Part Object: OpenBuilds AluminumSpacer
Aluminum spacers used throughout our system to ensure precision alignment and spacing
between components.
Product Features:
- Rigid to maintain stability during motion
- Lightweight, non-magnetic and corrosion resistance
Specifications:
- M5 ID
- Various Lengths
- 10mm OD
- Aluminum
- Color: Silver
Args:
length (Literal['3mm', '1/8in', '6mm', '1/4in', '9mm', '10mm', '13.2mm', '20mm', '35mm', '1-1/2in', '40mm']):
valid lengths
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
Raises:
ValueError: Invalid shim_type
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
length: Literal[
"3mm",
"1/8in",
"6mm",
"1/4in",
"9mm",
"10mm",
"13.2mm",
"20mm",
"35mm",
"1-1/2in",
"40mm",
],
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
valid_lengths = {
"3mm": 3 * MM,
"1/8in": IN / 8,
"6mm": 6 * MM,
"1/4in": IN / 4,
"9mm": 9 * MM,
"10mm": 10 * MM,
"13.2mm": 13.2 * MM,
"20mm": 20 * MM,
"35mm": 35 * MM,
"1-1/2in": 1.5 * IN,
"40mm": 40 * MM,
}
try:
spacer_length = valid_lengths[length]
except KeyError:
raise ValueError(
f"{length} is an invalid length, must be one of {tuple(valid_lengths.keys())}"
)
with BuildPart() as spacer:
with BuildSketch():
Circle(5)
Circle(2.6, mode=Mode.SUBTRACT)
extrude(amount=spacer_length)
super().__init__(spacer.part, rotation=rotation, align=align, mode=mode)
self.color = Color(0xC0C0C0)
self.label = f"AluminumSpacer-{length}"
RigidJoint("a", self, Location())
RigidJoint("b", self, Pos(Z=spacer_length))
RigidJoint("center", self, Pos(Z=spacer_length))
[docs]
class CBeamEndMount(BasePartObject):
"""CBeamEndMount
Ensure seamless integration of your motor with the C-Beam Linear Rail using the
versatile C-Beam End Mount. Designed with pre-threaded holes and a bearing recess
for lead screw transmission, this mount also features additional countersunk holes
for flush mounting, making it an excellent choice for various projects.
Product Features:
- Pre-tapped holes
- Countersunk holes
- Bearing recess for lead screw transmission
Args:
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = (
Align.CENTER,
Align.CENTER,
Align.MIN,
),
mode: Mode = Mode.ADD,
):
with BuildPart() as plate:
Box(80, 50, 12)
with Locations(Location((0, 7.4, -6), (0, 1, 0), 180)):
CounterBoreHole(5.588, 8.05, 4.5)
with Locations((0, 8.9 - 25, 6)):
with GridLocations(47.14, 0, 2, 1):
ThreadedHole(m5, counter_sunk=False)
with Locations((0, 10 - 25, 6)):
with GridLocations(20, 0, 2, 1):
CounterBoreHole(2.6, 4.6, 1.55)
with Locations((0, 5, 6)):
with GridLocations(60, 0, 2, 1):
CounterBoreHole(2.6, 4.6, 1.55)
with Locations(Plane(plate.faces().sort_by(Axis.Y)[-1], x_dir=(1, 0, 0))):
with GridLocations(20, 0, 2, 1):
ThreadedHole(m5, counter_sunk=False, depth=14 * MM)
super().__init__(plate.part, rotation, align, mode)
self.label = "CBeamEndMount"
self.color = Color(0x020202)
[docs]
class CBeamLinearRailProfile(BaseSketchObject):
"""Sketch Object: OpenBuilds C Beam Linear Rail Profile
Args:
rotation (float, optional): angles to rotate objects. Defaults to 0.
align (Union[Align, tuple[Align, Align]], optional): align min, center, or max
of object. Defaults to (Align.CENTER, Align.CENTER).
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildSketch._tag]
def __init__(
self,
rotation: float = 0,
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
with BuildSketch():
with BuildLine():
Polyline(
(-2.7, 38.2),
(-2.7, 36.239339828220196),
(-6.1, 32.839339828220226),
(-6.1, 27.160660171779885),
(-0.7393398282202048, 21.8),
(2.7, 21.8),
(2.7, 23.76066017177987),
(6.1, 27.160660171779845),
(6.1, 32.83933982822018),
(2.7, 36.239339828220174),
(2.7, 38.2),
close=True,
)
cavity = make_face()
with BuildSketch() as cbeam:
with Locations((0, -30), (0, 30)):
Rectangle(40, 20)
with Locations((-10, 0)):
Rectangle(20, 80)
v = cbeam.vertices().filter_by(lambda v: v.X == 0, reverse=True)
fillet(v, FILLET_RADIUS)
with Locations(
(10, 30), (-10, 30), (-10, 10), (-10, -10), (-10, -30), (10, -30)
):
Circle(CAVITY_RADIUS, mode=Mode.SUBTRACT)
with Locations((10, -30), (10, 30)):
_VSlotGroove(mode=Mode.SUBTRACT)
_VSlotGroove(90, mode=Mode.SUBTRACT)
_VSlotGroove(-90, mode=Mode.SUBTRACT)
with Locations((-10, -30)):
_VSlotGroove(-90, mode=Mode.SUBTRACT)
with Locations((-10, 30)):
_VSlotGroove(90, mode=Mode.SUBTRACT)
with Locations((-10, 0)):
with GridLocations(0, 20, 1, 4):
_VSlotGroove(180, mode=Mode.SUBTRACT)
with GridLocations(0, 20, 1, 2):
_VSlotGroove(mode=Mode.SUBTRACT)
add(cavity, mode=Mode.SUBTRACT)
add(cavity.mirror(Plane.XZ), mode=Mode.SUBTRACT)
add(cavity.mirror(Plane((20, 0, 0), z_dir=(1, 1, 0))), mode=Mode.SUBTRACT)
add(cavity.mirror(Plane((20, 0, 0), z_dir=(1, -1, 0))), mode=Mode.SUBTRACT)
add(
cavity.mirror(Plane.XZ).mirror(Plane((20, 0, 0), z_dir=(1, -1, 0))),
mode=Mode.SUBTRACT,
)
with Locations((-10, 0)):
_VSlotInternalCavity(mode=Mode.SUBTRACT)
super().__init__(obj=cbeam.sketch, rotation=rotation, align=align, mode=mode)
[docs]
class CBeamLinearRail(BasePartObject):
"""Part Object: OpenBuilds C-Beam Linear Rail
C-Beam Linear Rail aluminum extrusion profile is the ultimate solution combining both
linear motion and a modular, structural framing system. It's lightweight yet rigid and
provides an ultra smooth track for precise motion.
OpenBuilds created C-Beam Linear Rail aluminum extrusion profile and has added a
library of compatible modular Parts which today is known as the OpenBuilds System.
We have shipped over a million feet of V-Slot/C-Beam and counting to businesses,
classrooms, laboratories and makers all over the world!
Much like working with lumber, you can cut C-Beam on a chop saw (using a metal blade)
or even use a hacksaw. From there, you simply use a screw driver to make the
connections.
Product Features:
- Lightweight and strong
- Tee Nut channel
- Smooth v-groove for linear motion
- M5 tap-ready holes
- Anodized 6035 T-5 aluminum
- Available in Sleek Silver or Industrial Black
- Sizes: 80x40
Args:
length (float): rail length
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
length: float,
rotation: RotationLike = (0, 0, 0),
align: Union[Align, tuple[Align, Align, Align]] = (
Align.CENTER,
Align.CENTER,
Align.MIN,
),
mode: Mode = Mode.ADD,
):
rail = extrude(CBeamLinearRailProfile(), amount=length, dir=(0, 0, 1))
super().__init__(
part=rail, rotation=rotation, align=tuplify(align, 3), mode=mode
)
self.color = Color(0xC0C0C0)
# RigidJoint("test1", self, Location((10, 0, 0), (0, 90, 0)))
# RigidJoint("test2", self, Location((0, -10, 50), (90, 0, 0)))
self.label = "CBeamLinearRail"
LinearJoint(
"screw_axis", self, Axis((10, 0, 0), (0, 0, 1)), linear_range=(0, length)
)
[docs]
class CBeamGantryPlate(BasePartObject):
"""Part Object: OpenBuilds C-Beam Gantry Plate
Achieve a compact gantry cart footprint with the versatile C-Beam Gantry Plate. This
plate accommodates up to 4 Mini V Wheels and can be easily configured to station wheels
inside the C-Beam Linear Rail track. The plate features recessed and pre-tapped holes
for professional flush mounts, making it a reliable component for various mounting
configurations.
Product Features:
- Countersunk holes
- Pre-tapped holes
- Center recess
- Multiple mounting configurations
Specifications:
- Size: 77.5 x 77.5mm
- Thickness: 6mm
- Material: 6061-T5 Aluminum
- Finish: Brushed and Anodized
- Color: Black
Args:
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
with BuildPart() as plate:
with BuildSketch():
RectangleRounded(38.575 * 2, 38.575 * 2, 4.635)
with Locations((-5, 0)):
SlotCenterToCenter(10, 2.55 * 2, mode=Mode.SUBTRACT)
extrude(amount=6)
with BuildSketch(Plane.XY.offset(6)):
RectangleRounded(29.9, 29.9, 4.92)
extrude(amount=-1.5, mode=Mode.SUBTRACT)
with Locations((0, 14.25, 6)):
with GridLocations(60, 0, 2, 1):
CounterBoreHole(3.6, 6, 1.5)
with Locations((0, -14.25, 6)):
with GridLocations(60, 0, 2, 1):
CounterBoreHole(2.55, 4.5, 1.5)
with Locations((0, 0, 6)):
with GridLocations(40, 40, 2, 2):
ThreadedHole(m5, counter_sunk=False, depth=6 * MM)
with GridLocations(20, 60, 2, 2):
ThreadedHole(m5, counter_sunk=False, depth=6 * MM)
with GridLocations(60, 30, 2, 3):
Hole(2.55)
with GridLocations(20, 20, 3, 2):
Hole(2.55)
with GridLocations(0, 40, 1, 2):
Hole(2.55)
with Locations((-10, -10), (-10, 10), (10, 0)):
Hole(2.55)
super().__init__(plate.part, rotation=rotation, align=align, mode=mode)
self.color = Color(0x020202)
self.label = "CBeamGantryPlate"
[docs]
class CBeamGantryPlateXLarge(BasePartObject):
"""Part Object: OpenBuilds C-Beam Gantry Plate X-Large
Achieve unparalleled stability in your builds with the XLarge C-Beam Gantry Plate,
designed to accommodate up to six wheels for an extremely stable gantry. This versatile
plate is essential for both Belt and Pinion and Lead Screw transmissions. With recessed
holes for professional flush mounts and tapped holes for a 90° connection, this plate is
ideal for XY configurations, offering multiple mounting options to suit your project needs.
Product Features:
- Wide stance for enhanced stability
- Countersunk holes for a flush finish
- Tapped holes for easy 90° connections
- Center recess for additional mounting options
- Supports multiple mounting configurations
Specifications:
- Size: 125 x 125mm
- Thickness: 6mm
- Material: 6061 – T5 Aluminum
- Finish: Brushed and anodized
- Color: Black
Args:
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
with BuildPart() as plate:
with BuildSketch(Plane.XY) as plate_skt:
RectangleRounded(125, 125, 10)
with GridLocations(30, 30, 3, 3):
Circle(2.6, mode=Mode.SUBTRACT)
with GridLocations(20, 80.6, 2, 2):
Circle(2.6, mode=Mode.SUBTRACT)
with GridLocations(65, 80.6, 2, 2):
Circle(2.6, mode=Mode.SUBTRACT)
with GridLocations(60, 20, 2, 2):
Circle(2.6, mode=Mode.SUBTRACT)
with GridLocations(60, 100.6, 2, 2):
Circle(2.6, mode=Mode.SUBTRACT)
with GridLocations(20, 20, 2, 2) as nut_holes:
Circle(2.6, mode=Mode.SUBTRACT)
extrude(amount=6)
# Recess
with BuildSketch(Plane.XY.offset(6)):
RectangleRounded(35, 35, 2.8)
extrude(amount=-1.6, mode=Mode.SUBTRACT)
# Slots
with BuildSketch(Plane.XY.offset(6)):
with GridLocations(102.6, 20, 2, 2):
SlotOverall(11.135, 4.568 * 2)
extrude(amount=-1.6, mode=Mode.SUBTRACT)
with BuildSketch(Plane.XY):
with GridLocations(102.6, 20, 2, 2):
SlotOverall(7.1, 2.55 * 2)
extrude(amount=6, mode=Mode.SUBTRACT)
# CounterSink - tapped holes (thread not modelled)
with Locations((0, 0, 6)):
with GridLocations(100.6, 60, 2, 2):
CounterSinkHole(2.1, 2.7, counter_sink_angle=90)
# CounterBore
with Locations((0, -50.3, 6)):
with GridLocations(50.3, 0, 3, 1) as eccentric_mounts:
CounterBoreHole(3.6, 6.125, 1.6, 6)
with Locations((0, 50.3, 6)):
with GridLocations(50.3, 0, 3, 1) as fixed_mounts:
CounterBoreHole(2.55, 4.5675, 1.6, 6)
super().__init__(plate.part, rotation=rotation, align=align, mode=mode)
self.color = Color(0x020202)
self.label = "CBeamGantryPlateXLarge"
for label, loc in zip(["a", "b", "c"], eccentric_mounts):
RigidJoint(label, self, Pos(*(loc.position - Vector(0, 0, 6))))
for label, loc in zip(["d", "e", "f"], fixed_mounts):
RigidJoint(label, self, Pos(*(loc.position - Vector(0, 0, 6))))
for label, loc in zip(["a", "b", "c", "d"], nut_holes):
RigidJoint(f"nut_{label}", self, loc)
[docs]
class CBeamRiserPlate(BasePartObject):
"""Part Object: OpenBuilds C-Beam Riser Plate
A key component used to attach C-Beam Gantry Plates to a C-Beam Shield. Pre-threaded
holes and center recess allow for flush mounting a top plate. Each set contains 2 riser
plates.
Product Features:
- C-Beam and V-Slot 20x80mm compatible
- Pre-tapped holes
- Center recess
Specifications:
- Size: 77.5 x 14.874mm
- Thickness: 8mm
- Material: 6061-T5 Aluminum
- Finish: Brushed and Anodized
- Color: Black
Args:
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
with BuildPart() as plate:
with BuildSketch() as sk:
Rectangle(38.75 * 2, 7.437 * 2)
fillet(sk.vertices().group_by(Axis.Y)[0], 1.2)
fillet(sk.vertices().group_by(Axis.Y)[-1], 3.5)
with Locations((0, -1.323)):
with GridLocations(20, 0, 2, 1):
Circle(2.55, mode=Mode.SUBTRACT)
extrude(amount=8)
with BuildSketch(Plane((0, -7.427, 8))):
RectangleRounded(32.24, 2 * 11.86712, 0.75)
extrude(amount=-2.1, mode=Mode.SUBTRACT)
with Locations((0, -1.323, 8)):
with GridLocations(60, 0, 2, 1):
ThreadedHole(m5, counter_sunk=False, depth=8 * MM)
super().__init__(plate.part, rotation=rotation, align=align, mode=mode)
self.color = Color(0x020202)
self.label = "CBeamRiserPlate"
[docs]
class EccentricSpacer(BasePartObject):
"""Part Object: OpenBuilds Eccentric Spacer
These Eccentric Spacers are the perfect solution for creating a pre-load from the V-Wheels
to the V-Slot Linear Rail.
Pro-Tip: Eccentric Spacers comes with a divot/text on the outside which allows you to know
at all times where the smallest part of the CAM hole is located.
Product Features:
- Up to 1.5mm off center adjustment
- Tension wheel to v-slot for a snug, locked on fit
- Wide stance for stability
Specifications:
- Version - V7
- 6mm or 1/4" CAM Height
- 5mm Bore
- Rim fits into a 7.12mm Hole
- Stainless Steel
- Color: Steel
Args:
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
cam_height=Literal["6mm", "1/4in"],
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
valid_heights = {
"6mm": 6 * MM,
"1/4in": IN / 4,
}
try:
cam_length = valid_heights[cam_height]
except KeyError:
raise ValueError(
f"{cam_height} is an invalid cam height, must be one of {tuple(valid_heights.keys())}"
)
# 10mm across the flats
hex_radius = (2 / 3) * 10 * MM * math.sin(math.radians(60))
with BuildPart() as spacer:
with BuildSketch():
RegularPolygon(hex_radius, 6)
extrude(amount=6)
with BuildSketch(Plane.YZ) as chamf:
Rectangle(hex_radius, cam_length, align=Align.MIN)
chamfer(chamf.vertices().group_by(Axis.Y)[0], 0.6)
revolve(mode=Mode.INTERSECT)
with Locations((0, 0, cam_length)):
Cylinder(
hex_radius + 0.25 * MM,
1 * MM,
align=(Align.CENTER, Align.CENTER, Align.MAX),
)
with BuildSketch():
Circle(3.555)
extrude(amount=cam_length + 2.5 * MM)
with Locations((0, 0.79 * MM)): # eccentric hole
Hole(2.5)
with BuildSketch(spacer.faces().filter_by(Axis.Y).sort_by(Axis.Y)[-1]):
Text(cam_height, font_size=1.5 * MM, font_style=FontStyle.BOLD)
extrude(amount=-0.1 * MM, mode=Mode.SUBTRACT)
super().__init__(
spacer.part.locate(Pos(0, -0.79 * MM, 0)),
rotation=rotation,
align=align,
mode=mode,
)
self.color = Color(0xC0C0C0)
self.label = f"EccentricSpacer-{cam_height}"
RigidJoint("a", self, Location())
RigidJoint("b", self, Pos(Z=2.5 * MM + cam_length))
RigidJoint("center", self, Pos(0, -0.79 * MM, cam_length))
[docs]
class FlexibleCoupler(BasePartObject):
"""Part Object: OpenBuilds Flexible Coupler
These Flexible couplings are used to transmit torque from a stepper motor to a lead
screw, thus creating the movement in an actuator.
Specifications:
- Length: 25mm (.98")
- OD: 20mm (.79")
- Shaft: 5mm x 8mm or 1/4"
- Aluminum
- Color: Silver
Args:
shaft_diameter (Literal["8mm", "1/4in"]): load shaft diameter
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
Raises:
ValueError: Invalid shaft diameter
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
shaft_diameter: Literal["8mm", "1/4in"],
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
valid_diameters = {
"8mm": 8 * MM,
"1/4in": IN / 4,
}
try:
d2 = valid_diameters[shaft_diameter]
except KeyError:
raise ValueError(
f"{shaft_diameter} is an invalid shaft diameter, must be one of {tuple(valid_diameters.keys())}"
)
d1 = 5 * MM # NEMA 17
with BuildPart() as coupler:
Cylinder(10, 25, align=(Align.CENTER, Align.CENTER, Align.MAX))
with Locations(coupler.faces().sort_by(Axis.Z)[-1]):
Hole(d1 / 2, 25 * MM / 2)
Box(
10,
0.9,
8.3,
align=(Align.MIN, Align.CENTER, Align.MAX),
mode=Mode.SUBTRACT,
)
with Locations(coupler.faces().sort_by(Axis.Z)[0]):
Hole(d2 / 2, 25 * MM / 2)
Box(
11,
0.9,
8.3,
align=(Align.MIN, Align.CENTER, Align.MAX),
mode=Mode.SUBTRACT,
)
chamfer(coupler.edges().filter_by(GeomType.CIRCLE), 0.3 * MM)
with Locations(
Location((5, 9.2, -4), (1, 0, 0), -90),
Location((5, 9.2, -21), (1, 0, 0), -90),
):
Cylinder(6 / 2, 6, mode=Mode.SUBTRACT)
Hole(1.7)
with BuildLine():
spiral = Helix(2, 2, 11, center=(0, 0, -18.2))
with BuildSketch(spiral ^ 0) as x_section:
Rectangle(10, 0.7, align=(Align.MIN, Align.MIN))
spiral_cut = sweep(is_frenet=True, mode=Mode.PRIVATE).rotate(Axis.Z, 90)
for i in range(0, 10, 2):
add(spiral_cut.moved(Pos(Z=i)), mode=Mode.SUBTRACT)
super().__init__(
coupler.part.rotate(Axis.X, 180),
rotation=rotation,
align=align,
mode=mode,
)
self.color = Color(0xC0C0C0)
self.label = f"FlexibleCoupler-{shaft_diameter}"
RigidJoint("a", self, Pos(Z=12.5 * MM))
RigidJoint("b", self, Location((0, 0, 12.5 * MM), (1, 0, 0), 180))
[docs]
class LockCollar(Compound):
"""Assembly: OpenBuilds LockCollar
Lock Collars are used to create mechanical stops at any location along a shaft or to
secure components to it. A popular use is to lock the 8mm Lead Screw place. Includes
M5x4 set screw.
Product Features:
- One piece for even distributed clamping force
- Integrated set screw to prevent slippage
Product Specifications:
- 1/4" Size ID: 0.250 in, OD: .500 in, W: 7mm
- 5mm Size ID: 5mm, OD: 0.5 in, W: 7mm
- 8mm Size ID: 8mm, OD: 14mm, W: 7mm
- Black Oxide Finished Steel
- Color: Black
Args:
inside_diameter (Literal["5mm", "8mm", "1/4in"]): inside diameter of
the collor
Raises:
ValueError: Invalid inside diameter
"""
def __init__(self, inside_diameter: Literal["5mm", "8mm", "1/4in"]):
valid_sizes = {
"5mm": (5 * MM, IN / 2, 7 * MM),
"8mm": (8 * MM, 14 * MM, 7 * MM),
"1/4in": (IN / 4, IN / 2, 7 * MM),
}
try:
id, od, h = valid_sizes[inside_diameter]
except KeyError:
raise ValueError(
f"{inside_diameter} is an invalid inside diameter,"
f" must be one of {tuple(valid_sizes.keys())}"
)
super().__init__()
setscrew = SetScrew("M5-0.8", 4 * MM)
with BuildPart() as collar:
with BuildSketch():
Circle(od / 2)
Circle(id / 2, mode=Mode.SUBTRACT)
extrude(amount=h)
with Locations(Location((0, od / 2, h / 2), (1, 0, 0), -90)):
ThreadedHole(setscrew, depth=od / 2)
collar.part.color = Color(0x020202)
collar.part.label = "Collar"
self.children = [collar.part, setscrew.locate(setscrew.hole_locations[0])]
self.label = f"LockCollar{inside_diameter}"
RigidJoint("a", self, Location())
RigidJoint("b", self, -Location((0, 0, h)))
[docs]
class MetricLeadScrew(Compound):
"""Assembly: OpenBuilds MetricLeadScrew
These precision trapezoidal 8mm metric lead screws are a perfect combination of high
torque and speed.
Note that although this lead screw is a single object it is created as an Assembly
for efficiency reasons.
Product Features:
- Large diameter helps eliminate whipping
- High pitch which provides a quick 8mm translation for every single revolution
- Works best with OpenBuilds Nut Blocks and Anti-Backlash Nut Blocks
Product Specifications:
- Tr8*8-2p (4 starts)
- Pitch: 2mm
- Stainless Steel 304
- Color: Silver
- Lead Screw Diameter: 7.8~(mm) Note - These lead screws have been customized to
work directly with OpenBuilds system.
Args:
length (float): screw length
"""
def __init__(self, length: float):
thread = TrapezoidalThread(
7.8 * MM, 2 * MM, 30, length, starts=4, end_finishes=("chamfer", "chamfer")
)
super().__init__()
self.children = [
thread,
Cylinder(
thread.root_radius,
length,
align=(Align.CENTER, Align.CENTER, Align.MIN),
),
]
self.color = Color(0x71797E)
RigidJoint("axis", self, Location())
self.label = f"8mm lead screw"
[docs]
class RouterSpindleMount(BasePartObject):
"""RouterSpindleMount
Ensure the secure mounting of your router or spindle with this robust and adjustable
mount. The thick and hefty design provides a stable base, while the adjustable
faceplate accommodates size variations. This mount features pre-tapped holes for
easy installation and optional attachments like the OpenBuilds LED Light Ring.
Product Features:
- Removable faceplate for quick tool changes
- Adjustable faceplate to accommodate various router sizes
- Countersunk holes for a flush finish
- Tapped holes for easy mounting
- Flexible mounting options
Args:
parts (Literal["base", "faceplate", "both"], optional): parts to be created.
Defaults to "both".
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to None.
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
Raises:
ValueError: invalid parts parameter
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
parts: Literal["base", "faceplate", "both"] = "both",
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
m5 = SocketHeadCapScrew("M5-0.8", 20 * MM)
width = 90.8 * MM
height = 30.6 * MM + 54.5 * MM
thickness = 20 * MM
if parts in ["base", "both"]:
with BuildPart() as base:
# Create the basic shape
with BuildSketch(Plane.XY.offset(-thickness / 2)) as c:
with Locations((0, height / 2 - 54.5)):
Rectangle(width, height)
Circle(35.5, mode=Mode.SUBTRACT)
Rectangle(
28.15 * 2,
35.5,
align=(Align.CENTER, Align.MIN),
mode=Mode.SUBTRACT,
)
fillet(c.vertices().group_by(Axis.Y)[0], 5)
fillet(c.vertices().group_by(Axis.Y)[-2], 7.5)
extrude(amount=thickness)
# Create the holes
with Locations(
(30.0, -44.2, thickness / 2),
(20.0, -44.2, thickness / 2),
(-20.0, -44.2, thickness / 2),
(-30.0, -44.2, thickness / 2),
) as top_mount_centers:
ThreadedHole(m5, counter_sunk=False)
with Locations(base.faces().sort_by(Axis.Y)[0]):
with GridLocations(20, 0, 4, 1):
ThreadedHole(m5, counter_sunk=False, depth=7 * MM)
with Locations(
-Plane.YZ.offset(-width / 2), Plane.YZ.offset(width / 2)
):
with Locations((10.6, 0), (-29.4, 0)):
ThreadedHole(m5, counter_sunk=False, depth=9 * MM)
with Locations(-Plane.XZ.offset(-base.part.bounding_box().max.Y)):
with GridLocations(36.8 * 2, 0, 2, 1):
ThreadedHole(m5, counter_sunk=False, depth=13 * MM)
if parts in ["faceplate", "both"]:
with BuildPart() as faceplate:
with BuildSketch(Plane.XY.offset(-thickness / 2)) as s:
# Create basic half shape
Rectangle(23.65, 48.2, align=Align.MIN)
with Locations((0, 48.2)):
Rectangle(45.4, 48.2 - 36.2, align=(Align.MIN, Align.MAX))
Circle(35.5, mode=Mode.SUBTRACT)
# Fillet corners
fillet(s.vertices().group_by(Axis.Y)[-1].sort_by(Axis.X)[-1], 1.4)
fillet(s.vertices().group_by(Axis.Y)[-3].sort_by(Axis.X)[0], 3)
fillet(s.vertices().sort_by(Axis.Y)[0], 1.3)
# Mirror
mirror(about=Plane.YZ)
extrude(amount=thickness)
# Holes
with Locations(faceplate.faces().group_by(Axis.Y)[-1]):
with GridLocations(36.8 * 2, 0, 2, 1):
CounterBoreHole(3, 5.1, 1.5)
# Sunken embossed text
with BuildSketch(faceplate.faces().sort_by(Axis.Y)[-1]) as s2:
RectangleRounded(27.45 * 2, 13, 2.5)
extrude(amount=-1 * MM, mode=Mode.SUBTRACT)
with BuildSketch(
faceplate.faces().filter_by(Axis.Y).sort_by(Axis.Y)[-2]
) as s2:
Text("OPENBUILDS", 7.5 * MM, font_style=FontStyle.BOLD)
extrude(amount=0.1 * MM)
if parts == "both":
mount = base.part + faceplate.part
elif parts == "base":
mount = base.part
elif parts == "faceplate":
mount == faceplate.part
else:
raise ValueError(
f"the parts parameter must one of 'base', 'faceplate' or 'both' "
f"not {parts}"
)
mount.label = "RouterSpindleMount"
super().__init__(mount, rotation, align, mode)
self.color = Color(0x020202)
for label, pos in zip(["a", "b", "c", "d"], top_mount_centers):
RigidJoint(f"top_{label}", self, -pos)
for label, pos in zip(["a", "b", "c", "d"], top_mount_centers):
RigidJoint(f"bottom_{label}", self, pos * Pos(0, 0, -thickness))
[docs]
class ShimWasher(BasePartObject):
"""Part Object: OpenBuilds Shim/Washer
Product Features:
- Reduce wear and prevents binding
- Creates a tighter fit among parts
Args:
shim_type (Literal['MiniVWheel', '10x5x1', '12x8x1', 'SlotWasher', 'FlatWasher']):
shim / washer
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
Raises:
ValueError: Invalid shim_type
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
shim_type: Literal[
"MiniVWheel", "10x5x1", "12x8x1", "SlotWasher", "FlatWasher"
],
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
# OD, ID, Thickness
dimensions = {
"MiniVWheel": (8, 5, 1),
"10x5x1": (10, 5, 1),
"12x8x1": (12, 8, 1),
"SlotWasher": (15, 5, 2),
"FlatWasher": (0.75 * IN, 0.25 * IN, 0.08 * IN),
}
try:
od, id, thickness = dimensions[shim_type]
except KeyError:
raise ValueError(
f"Invalid shim_type {shim_type} must be one of {tuple(dimensions.keys())}"
)
with BuildPart() as shim:
with BuildSketch():
Circle(od / 2)
Circle(id / 2, mode=Mode.SUBTRACT)
extrude(amount=thickness)
fillet(shim.edges().group_by(SortBy.LENGTH)[-1], thickness / 5)
super().__init__(shim.part, rotation=rotation, align=align, mode=mode)
self.color = Color(0xC0C0C0)
self.label = f"ShimWasher-{shim_type}"
RigidJoint("a", self, Location())
RigidJoint("b", self, Pos(Z=thickness))
[docs]
class SpacerBlock(BasePartObject):
"""Part Object: OpenBuilds Spacer Block
This Spacer Block allows you to raise the V-Slot Gantry Plate up away from the rail
12mm so that you can run transmission components such as a Belt Drive or Threaded Rod
underneath the V-Slot Gantry Plate.
Product Features:
- Adds transmission spacing to any V-Slot Liner Actuators
- Compatible with multiple OpenBuilds plates
Specifications:
- Size: 86.4 x 20mm
- Thickness: 12mm
- M5 Tapped Holes to allow mounting to the V-Slot Gantry Plate
- 3 Holes that allow for mounting of full size OpenBuilds Wheels
- Anodized Aluminum
- Color: Black
Args:
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = None,
mode: Mode = Mode.ADD,
):
with BuildPart() as plate:
with BuildSketch() as bs:
RectangleRounded(86.4, 20, 3.36)
with GridLocations(30, 0, 3, 1):
Circle(3.6, mode=Mode.SUBTRACT)
extrude(amount=12)
with Locations((0, 0, 12)):
with GridLocations(40, 0, 2, 1):
ThreadedHole(m5, counter_sunk=False, depth=12)
super().__init__(plate.part, rotation=rotation, align=align, mode=mode)
self.color = Color(0x020202)
self.label = "CBeamRiserPlate"
class _VSlotGroove(BaseSketchObject):
"""Sketch Object: OpenBuilds V Slot Linear Rail Groove
Args:
rotation (float, optional): angles to rotate objects. Defaults to 0.
align (Union[Align, tuple[Align, Align]], optional): align min, center, or
max of object. Defaults to (Align.CENTER, Align.CENTER).
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildSketch._tag]
def __init__(
self,
rotation: float = 0,
mode: Mode = Mode.ADD,
):
with BuildSketch() as groove:
with BuildLine():
Polyline(
(3.69, 0),
(3.9, 0.21),
(3.9, 2.8393398282202034),
(6.560660171779773, 5.5),
(8.2, 5.5),
(8.2, 3.125),
(8.545, 3.125),
(10, 4.58),
(10, 0),
)
mirror(about=Plane.XZ)
make_face()
super().__init__(obj=groove.sketch, rotation=rotation, align=None, mode=mode)
class _VSlotInternalCavity(BaseSketchObject):
"""Sketch Object: OpenBuilds V Slot Linear Rail Internal Cavity
Args:
rotation (float, optional): angles to rotate objects. Defaults to 0.
align (Union[Align, tuple[Align, Align]], optional): align min, center, or
max of object. Defaults to (Align.CENTER, Align.CENTER).
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildSketch._tag]
def __init__(
self,
rotation: float = 0,
mode: Mode = Mode.ADD,
):
with BuildSketch() as cavity:
with BuildLine():
Polyline(
(-8.2, 0),
(-8.2, -2.7),
(-6.23933983, -2.7),
(-2.83933983, -6.1),
(0, -6.1),
)
mirror(about=Plane.XZ)
mirror(about=Plane.YZ)
make_face()
super().__init__(obj=cavity.sketch, rotation=rotation, align=None, mode=mode)
[docs]
class VSlotLinearRailProfile(BaseSketchObject):
"""Sketch Object: OpenBuilds V Slot Linear Rail Profile
Args:
rotation (float, optional): angles to rotate objects. Defaults to 0.
align (Union[Align, tuple[Align, Align]], optional): align min, center, or
max of object. Defaults to (Align.CENTER, Align.CENTER).
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildSketch._tag]
def __init__(
self,
rail_size: Literal["20x20", "20x40", "20x60", "20x80", "40x40"] = "20x20",
rotation: float = 0,
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
grooves: dict[str, list[tuple[tuple[int, int], list[int]]]] = {
"20x20": ([(0, 0), (0, 0), (0, 0), (0, 0)], [0, 90, 180, 270]),
"20x40": (
[(0, 10), (0, 10), (0, 10), (0, -10), (0, -10), (0, -10)],
[0, 90, 180, 180, 270, 0],
),
"20x60": (
[
(0, 0),
(0, 20),
(0, 20),
(0, 20),
(0, 0),
(0, -20),
(0, -20),
(0, -20),
],
[0, 0, 90, 180, 180, 180, 270, 0],
),
"20x80": (
[
(0, 10),
(0, 30),
(0, 30),
(0, 30),
(0, 10),
(0, -10),
(0, -30),
(0, -30),
(0, -30),
(0, -10),
],
[0, 0, 90, 180, 180, 180, 180, 270, 0, 0],
),
"40x40": (
[
(10, 10),
(10, 10),
(-10, 10),
(-10, 10),
(-10, -10),
(-10, -10),
(10, -10),
(10, -10),
],
[0, 90, 90, 180, 180, 270, 270, 0],
),
}
with BuildSketch() as cavity_40x40:
with BuildLine():
l = Polyline(
(18.2, 2.7),
(16.239339828219503, 2.7),
(12.839339828219602, 6.1),
(7.1606601717797975, 6.1),
(6.1, 7.160660171780418),
(6.1, 12.839339828220236),
(2.7, 16.23933982822021),
(2.7, 18.2),
(-2.7, 18.2),
)
add(l.rotate(Axis.Z, 90))
add(l.rotate(Axis.Z, 180))
add(l.rotate(Axis.Z, 270))
make_face()
if rail_size in ["20x20", "20x40", "20x60", "20x80", "40x40"]:
size = [int(v) for v in rail_size.split("x")]
else:
raise ValueError(
f"The rail_size of {rail_size} isn't valid"
f" - must be one of 20x20, 20x40, 20x60, 20x80, or 40x40"
)
with BuildSketch() as vslot:
RectangleRounded(*size, FILLET_RADIUS)
with GridLocations(20, 20, size[0] // 20, size[1] // 20):
Circle(CAVITY_RADIUS, mode=Mode.SUBTRACT)
for pos, angle in zip(*grooves[rail_size]):
with Locations(pos):
_VSlotGroove(angle, mode=Mode.SUBTRACT)
if rail_size in ["20x40", "20x60", "20x80"]:
with GridLocations(0, 20, 1, size[1] // 20 - 1):
_VSlotInternalCavity(mode=Mode.SUBTRACT)
elif rail_size == "40x40":
add(cavity_40x40, mode=Mode.SUBTRACT)
super().__init__(obj=vslot.sketch, rotation=rotation, align=align, mode=mode)
[docs]
class VSlotLinearRail(BasePartObject):
"""Part Object: OpenBuilds V Slot Linear Rail
V-Slot Linear Rail aluminum extrusion profile is the ultimate solution combining both
linear motion and a modular, structural framing system. It's lightweight yet rigid and
provides an ultra smooth track for precise motion.
OpenBuilds created V-Slot Linear Rail aluminum extrusion profile and has added a
library of compatible modular parts which today is known as the OpenBuilds System.
We have shipped over one million feet of V-Slot and counting to businesses, classrooms,
laboratories and makers all over the world!
Much like working with lumber, you can cut V-Slot on a chop saw (using a metal blade)
or even use a hacksaw. From there, you simply use a screw driver to make the connections.
Product Features:
- Lightweight and strong
- Tee Nut channel
- Smooth v-groove for linear motion
- M5 tap-ready holes
- Anodized 6035 T-5 aluminum
- Available in Sleek Silver or Industrial Black
- Sizes: 20x20, 20x40, 20x60, 20x80, and 40x40
Args:
rail_size (Literal["20x20", "20x40", "20x60", "20x80", "40x40"]): size in mm
length (float): rail length
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to (Align.CENTER, Align.CENTER, Align.MIN).
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
Raises:
ValueError: Invalid rail_size
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
rail_size: Literal["20x20", "20x40", "20x60", "20x80", "40x40"],
length: float,
rotation: RotationLike = (0, 0, 0),
align: Union[Align, tuple[Align, Align, Align]] = (
Align.CENTER,
Align.CENTER,
Align.MIN,
),
mode: Mode = Mode.ADD,
):
if rail_size not in ["20x20", "20x40", "20x60", "20x80", "40x40"]:
raise ValueError(
f"The rail_size of {rail_size} isn't valid"
f" - must be one of 20x20, 20x40, 20x60, 20x80, or 40x40"
)
rail = extrude(VSlotLinearRailProfile(rail_size), amount=length, dir=(0, 0, 1))
super().__init__(
part=rail, rotation=rotation, align=tuplify(align, 3), mode=mode
)
self.color = Color(0xC0C0C0)
# RigidJoint("test1", self, Location((10, 0, 0), (0, 90, 0)))
# RigidJoint("test2", self, Location((0, -10, 50), (90, 0, 0)))
self.label = f"{rail_size} VSlot Rail"
[docs]
class XLargeCBeamGantry(Compound):
"""XLargeCBeamGantry
The C-Beam XLarge Gantry Set is ideal for high-performance demands. Tailored to
excel in robust Linear Actuator gantries and CNC Machines, it fuses strength,
resilience, and unwavering stability.
Setting this Gantry kit apart are four robust Xtreme V Wheels. They shine under
intense force and weight, delivering heightened accuracy and minimal compression
for precision-driven tasks.
Builders across various industries vouch for this Kit's reliability and consistent
performance. Purpose-built for the X, Y, and Z axes of our OpenBuilds LEAD CNC Machine,
elevate your projects with the proven versatility of the C-Beam XLargeGantry Set.
Specifications:
- Plate dimensions: 125mm x 125mm
- Designed to be compatible with C-Beam Linear Rail
- Specifically engineered for Lead Screw-driven systems
Args:
wheel_count (Literal[4, 6], optional): number of wheels. Defaults to 4.
eccentric_angle (float, optional): angle of the eccentric spaces. Values
less than 90 will tighten the wheels to the rail while values greater
than 90 will loosen the fit. Defaults to 90.
Raises:
ValueError: Invalid wheel_count
"""
def __init__(self, wheel_count: Literal[4, 6] = 4, eccentric_angle: float = 90):
if wheel_count not in [4, 6]:
raise ValueError(f"wheel_count of {wheel_count} must be either 4 or 6")
plate = CBeamGantryPlateXLarge()
w0 = XtremeSolidVWheelAssembly(True)
w2 = copy.copy(w0)
w3 = XtremeSolidVWheelAssembly(False)
w5 = copy.copy(w3)
wheels = [w0, w2, w3, w5]
acme_nut_assembly = AcmeAntiBacklashNutBlock8mmAssembly()
plate.joints["a"].connect_to(w0.joints["mount"], angle=eccentric_angle)
plate.joints["c"].connect_to(w2.joints["mount"], angle=eccentric_angle)
plate.joints["d"].connect_to(w3.joints["mount"])
plate.joints["f"].connect_to(w5.joints["mount"])
if wheel_count == 6:
w1 = copy.copy(w0)
w4 = copy.copy(w3)
wheels.extend([w1, w4])
plate.joints["b"].connect_to(w1.joints["mount"], angle=eccentric_angle)
plate.joints["e"].connect_to(w4.joints["mount"])
plate.joints["nut_a"].connect_to(acme_nut_assembly.joints["a"])
super().__init__()
self.label = "XLargeCBeamGantry"
self.children = [plate, acme_nut_assembly] + wheels
RigidJoint("nut", self, Location((0, 0, -12.5 * MM), (0, 1, 0), -90))
[docs]
class XtremeSolidVWheel(BasePartObject):
"""Part Object: OpenBuilds Xtreme Solid V Wheel
A heavy-duty alternative to Delrin Wheels. Suitable during applications where substantial
force and weight are introduced into the system. Perfect when additional accuracy is
needed and less compression than in the Delrin is desired. Flat profile designed to work
effortlessly with a belt on a belt and pinion system as well as lead screw and belt driven
systems.
Product Features:
- Heavy Duty
- Long lasting solid construction
- Ultra smooth finish for precise motion
- Flat wheel surface for belt travel (belt & pinion option)
- Self-Centering and Self- Lubricating
- Even distribution of weight
- Easily assembled and maintained
Args:
length (float): rail length
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
align (Union[Align, tuple[Align, Align, Align]], optional): align min, center,
or max of object. Defaults to Align.CENTER.
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildPart._tag]
def __init__(
self,
rotation: RotationLike = (0, 0, 0),
align: Union[None, Align, tuple[Align, Align, Align]] = Align.CENTER,
mode: Mode = Mode.ADD,
):
with BuildPart() as wheel:
with BuildSketch(Plane.YZ) as x_section:
with Locations((8, 0)):
Rectangle(3.95, 10.2, align=(Align.MIN, Align.CENTER))
Rectangle(2, 1)
chamfer(x_section.vertices().group_by(Axis.X)[-1], 2.15)
revolve()
super().__init__(
part=wheel.part, rotation=rotation, align=tuplify(align, 3), mode=mode
)
self.color = Color(0xE0E0D8)
self.label = "XtremeSolidVWHeel"
RigidJoint("a", self, Pos(Z=-11 / 2))
RigidJoint("b", self, Pos(Z=+11 / 2))
[docs]
class XtremeSolidVWheelAssembly(Compound):
"""Assembly: OpenBuilds Xtreme Solid V Wheel Assembly
All of the components in a Xtreme Solid V Wheel assembly:
- Xtreme Solid V WHeel x 1
- M5-16-5 bearing x 2
- 10x5x1 Shim x 2
- Aluminum Spacer or Eccentric Spacer
- Hex Nut
Note that the RevoluteJoint "mount" should be used to attach this wheel assembly
to a gantry plate as it enables the eccentric spacer to precisely align the wheel
to the rail by changing the joint angle.
Args:
eccentric (bool): Use an eccentric spacer.
"""
def __init__(self, eccentric: bool):
nut = HexNut("M5-0.8")
b0 = SingleRowCappedDeepGrooveBallBearing("M5-16-5")
b1 = copy.copy(b0)
shim0 = ShimWasher("10x5x1")
shim1 = copy.copy(shim0)
spacer = EccentricSpacer("6mm") if eccentric else AluminumSpacer("6mm")
tire = XtremeSolidVWheel()
tire.joints["a"].connect_to(b0.joints["a"])
b0.joints["b"].connect_to(shim0.joints["a"])
b0.joints["a"].connect_to(nut.joints["b"])
tire.joints["b"].connect_to(b1.joints["b"])
b1.joints["b"].connect_to(shim1.joints["a"])
shim1.joints["b"].connect_to(spacer.joints["a"])
super().__init__()
self.label = "XtremeSolidVWHeelAssembly"
self.children = [tire, b0, shim0, shim1, b1, spacer, nut]
RevoluteJoint(
"mount", self, Axis(spacer.joints["center"].location.position, (0, 0, 1))
)
[docs]
class StepperMotor(Compound):
"""Assembly: OpenBuilds StepperMotors
This custom Openbuilds NEMA Stepper Motor series offers a range of options, from the
NEMA 17 for lighter duty, high-precision CNC projects, to the NEMA 23 for larger builds
requiring more power, and the NEMA 23 High Torque for enhanced holding power and
strength to elevate any build.
Product Features:
- Plug and Play Xtension connector for easy and fast install
- Flat shaft section for better torque
Common Product Specifications:
- Step Angle: 1.8
- 4 Wire Bi-Polar
- RoHS Compliant
NEMA 17 Product Specifications:
- Shaft Size: 5mm
- Torque: 76 oz*in
- Peak Current is 1.68A/phase
- M3 Mounting Holes
- 12-24VDC (Recommended)
NEMA 23 Product Specifications
- Shaft Size: 1/4"
- Torque: 175 oz*in
- Peak Current is 2.8A/phase
- 12-48VDC (Recommended)
NEMA 23 High Torque Product Specifications:
- 2 Phase
- Shaft Size: 1/4"
- Torque: 345 oz*in
- 24-48VDC (Recommended)
Args:
motor_type (Literal["Nema17", "Nema23", "Nema23HighTorque"]): steppers
motor_length (Union[float, None], optional): The length of the motor body
in the axis of the shaft. If None, uses a standard length for the
specified motor type. Defaults to None.
Raises:
ValueError: Invalid motor_type
"""
def __init__(
self,
motor_type: Literal["Nema17", "Nema23", "Nema23HighTorque"],
motor_length: Union[float, None] = None,
):
motor_data = {
"Nema17": (48, 5, 2, 9, 24),
"Nema23": (56, IN / 4, 2.675, 5.6, 20.6),
"Nema23HighTorque": (86, IN / 4, 2.675, 5.6, 20.6),
}
try:
len, shaft_d, flat, shaft_l1, shaft_l2 = motor_data[motor_type]
except KeyError:
raise ValueError(
f"{motor_type} is invalid, must be one of {motor_data.keys()}"
)
if motor_length is not None:
if motor_length <= 0:
raise ValueError(
f"Expected motor_length greater than 0, got {motor_length}"
)
len = motor_length
m3 = SocketHeadCapScrew("M3-0.5", 20)
with BuildPart() as stepper:
if motor_type in ["Nema23", "Nema23HighTorque"]:
skt1_length = min(len / 2, 11)
skt2_length = min(len, 4.8)
with BuildSketch() as skt1:
with BuildLine():
l1 = Line((28.2, 0), (28.2, 16.07))
l2 = JernArc(l1 @ 1, l1 % 1, 2.5, 90)
l3 = Line(l2 @ 1, (23.57, 18.57))
l4 = JernArc(l3 @ 1, l3 % 1, 5, -45)
mirror(about=Plane(l4 ^ 1))
mirror(about=Plane.YZ)
mirror(about=Plane.XZ)
make_face()
extrude(amount=-skt1_length)
add(Face(skt1.wire().offset_2d(-0.1)))
extrude(amount=-len + skt1_length)
add(skt1.face().located(Pos(Z=-len + skt1_length)))
extrude(amount=-skt1_length)
with BuildSketch() as skt2:
RectangleRounded(28.2 * 2, 28.2 * 2, 4.6)
with GridLocations(23.57 * 2, 23.57 * 2, 2, 2) as mount_holes:
Circle(2.55, mode=Mode.SUBTRACT)
extrude(amount=-skt2_length)
with BuildSketch():
Circle(19.05)
extrude(amount=1.6)
else:
skt1_length = min(len / 2, 8.75)
with BuildSketch() as skt1:
Rectangle(21.209 * 2, 21.209 * 2)
Circle(26.5, mode=Mode.INTERSECT)
extrude(amount=-skt1_length)
with BuildSketch() as skt2:
Rectangle(21.109 * 2, 21.109 * 2)
Circle(25, mode=Mode.INTERSECT)
extrude(amount=-len)
add(skt1.face().located(Pos(Z=-len + skt1_length)))
extrude(amount=-skt1_length)
with GridLocations(15.5 * 2, 15.5 * 2, 2, 2) as mount_holes:
Hole(1.5, 6)
ThreadedHole(m3, depth=7.5, counter_sunk=False)
with BuildSketch():
Circle(11)
extrude(amount=2)
with BuildPart() as shaft:
with BuildSketch():
Circle(shaft_d / 2)
extrude(amount=shaft_l1)
with BuildSketch():
Circle(shaft_d / 2)
with Locations((0, flat)):
Rectangle(
shaft_d,
shaft_d,
align=(Align.CENTER, Align.MIN),
mode=Mode.SUBTRACT,
)
extrude(amount=shaft_l2)
stepper.part.color = Color(0x020202)
shaft.part.color = Color(0xC0C0C0)
super().__init__()
self.label = f"StepperMotor-{motor_type}"
self.children = [stepper.part, shaft.part]
for label, loc in zip(["a", "b", "c", "d"], mount_holes.locations):
RigidJoint(label, self, -loc)
if __name__ == "__main__":
# from ocp_vscode import show, show_all, set_defaults, Camera
# set_defaults(reset_camera=Camera.CENTER)
# AluminumSpacer("6mm") # 2
# AluminumSpacer("40mm") # 2
# FlexibleCoupler("1/4in") # 1
# AluminumSpacer("3mm") # 2
# EccentricSpacer("6mm") # 2
# XtremeSolidVWheelAssembly(eccentric=True) # 2
# XtremeSolidVWheelAssembly(eccentric=False) # 2
# LowProfileScrew("M5-0.8", 20 * MM), # 10
# LowProfileScrew("M5-0.8", 50 * MM), # 2
# LowProfileScrew("M5-0.8", 27 * MM), # 4
# LockCollar("8mm") # 2
# SingleRowCappedDeepGrooveBallBearing("M8-16-5") # 2
# ShimWasher("12x8x1") # 2
# CBeamEndMount() # 2
# CBeamGantryPlateXLarge() # 1
# CBeamLinearRail(500 * MM) # 1
# MetricLeadScrew(500 * MM) # 1
# AcmeAntiBacklashNutBlock8mm() # 1
# ShimWasher("10x5x1") # 2
# StepperMotor("Nema23") # 1
# show(XLargeCBeamGantry(4))
# show(
# pack(
# [
# CBeamLinearRailProfile(),
# VSlotLinearRailProfile("20x20"),
# VSlotLinearRailProfile("20x40"),
# VSlotLinearRailProfile("20x60"),
# VSlotLinearRailProfile("20x80"),
# VSlotLinearRailProfile("40x40"),
# ],
# 10,
# )
# )
# show(
# pack(
# [
# AcmeAntiBacklashNutBlock8mm(),
# AluminumSpacer("3mm"),
# AluminumSpacer("1/8in"),
# AluminumSpacer("6mm"),
# AluminumSpacer("1/4in"),
# AluminumSpacer("9mm"),
# AluminumSpacer("10mm"),
# AluminumSpacer("13.2mm"),
# AluminumSpacer("20mm"),
# AluminumSpacer("35mm"),
# AluminumSpacer("1-1/2in"),
# AluminumSpacer("40mm"),
# CBeamEndMount(),
# CBeamLinearRail(25),
# CBeamGantryPlate(),
# CBeamGantryPlateXLarge(),
# CBeamRiserPlate(),
# EccentricSpacer("6mm"),
# EccentricSpacer("1/4in"),
# FlexibleCoupler("1/4in"),
# FlexibleCoupler("8mm"),
# LockCollar("1/4in"),
# LockCollar("5mm"),
# LockCollar("8mm"),
# LowProfileScrew("M3-0.5", 6, simple=False),
# LowProfileScrew("M4-0.7", 8, simple=False),
# LowProfileScrew("M5-0.8", 10, simple=False),
# MetricLeadScrew(25 * MM),
# RouterSpindleMount().rotate(Axis.Z, 180),
# ShimWasher("MiniVWheel"),
# ShimWasher("10x5x1"),
# ShimWasher("12x8x1"),
# ShimWasher("SlotWasher"),
# ShimWasher("FlatWasher"),
# SingleRowCappedDeepGrooveBallBearing("M5-10-4", "OpenBuilds"),
# SpacerBlock(),
# StepperMotor("Nema17"),
# StepperMotor("Nema23"),
# StepperMotor("Nema23HighTorque"),
# VSlotLinearRail("20x20", 25),
# VSlotLinearRail("20x40", 25),
# VSlotLinearRail("20x60", 25),
# VSlotLinearRail("20x80", 25),
# VSlotLinearRail("40x40", 25),
# XtremeSolidVWheel(),
# ],
# 20,
# )
# )
exit()
# show(
# pack(
# [
# AcmeAntiBacklashNutBlock8mmAssembly(),
# XLargeCBeamGantry(4),
# XLargeCBeamGantry(6),
# XtremeSolidVWheelAssembly(True),
# XtremeSolidVWheelAssembly(False),
# ],
# 20,
# )
# )