Files
cadSet/text-to-cad/models/simple/simple_model_library.py
2026-07-22 19:38:36 +08:00

452 lines
16 KiB
Python

# Prompt: Shared implementation library for the simple CAD prompt scripts.
from math import cos, sin, tau
from build123d import (
Align,
Box,
BuildPart,
BuildSketch,
Color,
Cylinder,
Location,
Locations,
Mode,
Plane,
Polygon,
RectangleRounded,
Torus,
add,
chamfer,
extrude,
fillet,
)
def _polar_point(radius: float, angle: float) -> tuple[float, float]:
return (radius * cos(angle), radius * sin(angle))
def _safe_fillet(shape, edges, radius: float):
selected = list(edges)
if not selected:
return shape
try:
return fillet(selected, radius=radius)
except Exception:
return shape
def _safe_chamfer(shape, edges, length: float):
selected = list(edges)
if not selected:
return shape
try:
return chamfer(selected, length=length)
except Exception:
return shape
def _line_edges(shape):
return [edge for edge in shape.edges() if str(edge.geom_type).endswith("LINE")]
def _circular_edges(shape, *, radius: float | None = None, axis: str | None = None, coordinate: float | None = None):
edges = []
for edge in shape.edges():
try:
edge_radius = edge.radius
edge_center = edge.arc_center
edge_normal = edge.normal()
except Exception:
continue
if radius is not None and abs(edge_radius - radius) > 0.08:
continue
if axis is not None:
components = {
"x": abs(edge_normal.X),
"y": abs(edge_normal.Y),
"z": abs(edge_normal.Z),
}
if components[axis] < 1.0 - 1e-4:
continue
if coordinate is not None:
coord = {"x": edge_center.X, "y": edge_center.Y, "z": edge_center.Z}[axis or "z"]
if abs(coord - coordinate) > 0.08:
continue
edges.append(edge)
return edges
def _finish(shape, label: str, color: Color):
shape.label = label
shape.color = color
return shape
def _hole_z(diameter: float, height: float, x: float, y: float, z_min: float = -1.0):
with Locations(Location((x, y, z_min))):
Cylinder(
radius=diameter / 2.0,
height=height,
align=(Align.CENTER, Align.CENTER, Align.MIN),
mode=Mode.SUBTRACT,
)
def _hole_x(diameter: float, length: float, x: float, y: float, z: float):
with Locations(Location((x, y, z))):
Cylinder(
radius=diameter / 2.0,
height=length,
rotation=(0.0, 90.0, 0.0),
align=(Align.CENTER, Align.CENTER, Align.CENTER),
mode=Mode.SUBTRACT,
)
def _hole_y(diameter: float, length: float, x: float, y: float, z: float):
with Locations(Location((x, y, z))):
Cylinder(
radius=diameter / 2.0,
height=length,
rotation=(90.0, 0.0, 0.0),
align=(Align.CENTER, Align.CENTER, Align.CENTER),
mode=Mode.SUBTRACT,
)
def _slot_z(length: float, width: float, height: float, center: tuple[float, float], z_min: float, *, axis: str = "x"):
span = max(0.0, length - width)
if axis == "x":
if span > 0.01:
with Locations(Location((center[0], center[1], z_min))):
Box(span, width, height, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
for x_offset in (-span / 2.0, span / 2.0):
_hole_z(width, height, center[0] + x_offset, center[1], z_min)
else:
if span > 0.01:
with Locations(Location((center[0], center[1], z_min))):
Box(width, span, height, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
for y_offset in (-span / 2.0, span / 2.0):
_hole_z(width, height, center[0], center[1] + y_offset, z_min)
def _trapezoid_tooth_profile(
*,
teeth: int,
root_radius: float,
tip_radius: float,
phase: float,
root_span_fraction: float = 0.72,
tip_span_fraction: float = 0.38,
):
points = []
pitch_angle = tau / teeth
for tooth_index in range(teeth):
center_angle = phase + tooth_index * pitch_angle
points.extend(
(
_polar_point(root_radius, center_angle - root_span_fraction * pitch_angle / 2.0),
_polar_point(tip_radius, center_angle - tip_span_fraction * pitch_angle / 2.0),
_polar_point(tip_radius, center_angle + tip_span_fraction * pitch_angle / 2.0),
_polar_point(root_radius, center_angle + root_span_fraction * pitch_angle / 2.0),
)
)
return points
def make_cylindrical_spacer_sleeve():
outer_radius = 15.0
bore_radius = 5.0
height = 28.0
with BuildPart() as model:
Cylinder(outer_radius, height, align=(Align.CENTER, Align.CENTER, Align.MIN))
_hole_z(bore_radius * 2.0, height + 2.0, 0.0, 0.0)
rim_edges = []
for z_pos in (0.0, height):
rim_edges.extend(_circular_edges(model.part, radius=outer_radius, axis="z", coordinate=z_pos))
rim_edges.extend(_circular_edges(model.part, radius=bore_radius, axis="z", coordinate=z_pos))
part = _safe_fillet(model.part, rim_edges, 1.1)
return _finish(part, "cylindrical_spacer_sleeve_through_bore_rounded_rims", Color(0.68, 0.68, 0.64, 1.0))
def make_square_mounting_block():
size = 48.0
height = 28.0
with BuildPart() as model:
Box(size, size, height, align=(Align.CENTER, Align.CENTER, Align.MIN))
_hole_z(12.0, height + 2.0, 0.0, 0.0)
for y_pos in (-14.0, 14.0):
_hole_x(5.5, size + 2.0, 0.0, y_pos, height / 2.0)
part = _safe_chamfer(model.part, _line_edges(model.part), 0.8)
return _finish(part, "square_mounting_block_vertical_and_side_clearance_holes", Color(0.50, 0.58, 0.64, 1.0))
def make_gusset_plate():
base_length = 88.0
base_width = 30.0
base_thickness = 6.0
web_thickness = 8.0
web_height = 48.0
with BuildPart() as web:
with BuildSketch(Plane.YZ):
Polygon(
[
(-12.0, base_thickness),
(12.0, base_thickness),
(12.0, base_thickness + web_height),
],
align=None,
)
extrude(amount=web_thickness)
web_part = web.part.moved(Location((-web_thickness / 2.0, 0.0, 0.0)))
with BuildPart() as model:
Box(base_length, base_width, base_thickness, align=(Align.CENTER, Align.CENTER, Align.MIN))
add(web_part, mode=Mode.ADD)
for x_pos in (-28.0, 28.0):
_hole_z(6.0, base_thickness + 2.0, x_pos, -6.0)
part = _safe_fillet(model.part, _line_edges(model.part), 0.9)
return _finish(part, "gusset_plate_triangular_web_base_holes_softened_edges", Color(0.58, 0.63, 0.58, 1.0))
def make_rectangular_clamp_block():
length = 70.0
width = 34.0
height = 28.0
with BuildPart() as model:
Box(length, width, height, align=(Align.CENTER, Align.CENTER, Align.MIN))
with Locations(Location((0.0, 0.0, -1.0))):
Box(length + 2.0, 4.0, height + 2.0, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
for x_pos in (-20.0, 20.0):
_hole_y(5.5, width + 2.0, x_pos, 0.0, height / 2.0)
part = _safe_chamfer(model.part, _line_edges(model.part), 0.7)
return _finish(part, "rectangular_clamp_block_split_slot_two_transverse_screw_holes", Color(0.57, 0.60, 0.64, 1.0))
def make_shaft_collar():
outer_radius = 21.0
bore_radius = 10.0
height = 16.0
with BuildPart() as model:
Cylinder(outer_radius, height, align=(Align.CENTER, Align.CENTER, Align.MIN))
_hole_z(bore_radius * 2.0, height + 2.0, 0.0, 0.0)
_hole_x(5.0, outer_radius + 3.0, outer_radius / 2.0, 0.0, height / 2.0)
edge_set = []
for z_pos in (0.0, height):
edge_set.extend(_circular_edges(model.part, axis="z", coordinate=z_pos))
part = _safe_chamfer(model.part, edge_set, 0.8)
return _finish(part, "shaft_collar_bore_radial_set_screw_hole_chamfered_faces", Color(0.46, 0.50, 0.55, 1.0))
def make_pulley_wheel():
outer_radius = 35.0
thickness = 16.0
hub_radius = 18.0
hub_height = 24.0
with BuildPart() as model:
Cylinder(outer_radius, thickness, align=(Align.CENTER, Align.CENTER, Align.MIN))
with Locations(Location((0.0, 0.0, thickness / 2.0))):
Cylinder(hub_radius, hub_height, align=(Align.CENTER, Align.CENTER, Align.CENTER), mode=Mode.ADD)
Torus(outer_radius - 1.2, 3.0, mode=Mode.SUBTRACT)
_hole_z(10.0, hub_height + 4.0, 0.0, 0.0, z_min=-4.0)
part = _safe_fillet(model.part, _circular_edges(model.part, axis="z"), 0.8)
return _finish(part, "pulley_wheel_central_hub_outer_groove_through_bore", Color(0.42, 0.46, 0.50, 1.0))
def make_spur_gear_blank():
thickness = 10.0
teeth = 24
root_radius = 35.0
tip_radius = 39.0
hub_radius = 17.0
hub_height = 16.0
with BuildPart() as model:
with BuildSketch(Plane.XY):
Polygon(
_trapezoid_tooth_profile(
teeth=teeth,
root_radius=root_radius,
tip_radius=tip_radius,
phase=-tau / teeth / 2.0,
),
align=None,
)
extrude(amount=thickness)
with Locations(Location((0.0, 0.0, -3.0))):
Cylinder(hub_radius, hub_height, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.ADD)
_hole_z(18.0, hub_height + 4.0, 0.0, 0.0, z_min=-4.0)
part = _safe_chamfer(model.part, _circular_edges(model.part, axis="z"), 0.6)
return _finish(part, "spur_gear_blank_central_bore_raised_hub_simplified_teeth", Color(0.70, 0.58, 0.36, 1.0))
def make_flywheel_disk():
radius = 45.0
thickness = 12.0
with BuildPart() as model:
Cylinder(radius, thickness, align=(Align.CENTER, Align.CENTER, Align.MIN))
with Locations(Location((0.0, 0.0, thickness - 2.2))):
Cylinder(34.0, 3.0, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
with Locations(Location((0.0, 0.0, -0.8))):
Cylinder(34.0, 3.0, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
_hole_z(18.0, thickness + 2.0, 0.0, 0.0)
for index in range(6):
x_pos, y_pos = _polar_point(25.0, tau * index / 6)
_hole_z(10.0, thickness + 2.0, x_pos, y_pos)
part = _safe_fillet(model.part, _circular_edges(model.part, axis="z"), 0.8)
return _finish(part, "flywheel_disk_central_bore_annular_rim_lightening_holes", Color(0.59, 0.61, 0.63, 1.0))
def make_cam_follower_roller():
radius = 16.0
width = 18.0
with BuildPart() as model:
Cylinder(radius, width, align=(Align.CENTER, Align.CENTER, Align.MIN))
_hole_z(12.0, width + 2.0, 0.0, 0.0)
edge_set = []
for z_pos in (0.0, width):
edge_set.extend(_circular_edges(model.part, radius=radius, axis="z", coordinate=z_pos))
edge_set.extend(_circular_edges(model.part, radius=6.0, axis="z", coordinate=z_pos))
part = _safe_fillet(model.part, edge_set, 1.8)
return _finish(part, "cam_follower_roller_bearing_bore_rounded_outer_profile", Color(0.60, 0.64, 0.67, 1.0))
def make_small_enclosure_cover():
length = 90.0
width = 60.0
base_thickness = 4.0
rim_height = 3.0
with BuildPart() as model:
with BuildSketch(Plane.XY):
RectangleRounded(length, width, 4.0, align=(Align.CENTER, Align.CENTER))
extrude(amount=base_thickness)
with Locations(Location((0.0, 0.0, base_thickness))):
with BuildSketch(Plane.XY):
RectangleRounded(length - 10.0, width - 10.0, 3.0, align=(Align.CENTER, Align.CENTER))
extrude(amount=rim_height, mode=Mode.ADD)
with Locations(Location((0.0, 0.0, base_thickness - 0.8))):
Box(58.0, 28.0, rim_height + 1.2, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
with Locations(Location((0.0, 0.0, base_thickness))):
Box(66.0, 36.0, rim_height + 1.0, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
for x_pos in (-35.0, 35.0):
for y_pos in (-22.0, 22.0):
_hole_z(4.2, base_thickness + 1.5, x_pos, y_pos)
part = _safe_fillet(model.part, _line_edges(model.part), 0.5)
return _finish(part, "small_enclosure_cover_raised_rim_corner_screw_holes_recessed_center", Color(0.38, 0.51, 0.62, 1.0))
def make_cylindrical_cap():
outer_radius = 21.0
height = 28.0
wall = 3.0
top_thickness = 4.0
with BuildPart() as model:
Cylinder(outer_radius, height, align=(Align.CENTER, Align.CENTER, Align.MIN))
with Locations(Location((0.0, 0.0, -1.0))):
Cylinder(outer_radius - wall, height - top_thickness + 1.0, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
with Locations(Location((0.0, 0.0, height))):
Cylinder(10.0, 6.0, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.ADD)
part = _safe_fillet(model.part, _circular_edges(model.part, axis="z"), 1.0)
return _finish(part, "cylindrical_cap_hollow_interior_top_boss_rounded_external_edges", Color(0.52, 0.57, 0.61, 1.0))
def make_retainer_plate():
length = 82.0
width = 34.0
thickness = 6.0
with BuildPart() as model:
Box(length, width, thickness, align=(Align.CENTER, Align.CENTER, Align.MIN))
_slot_z(36.0, 10.0, thickness + 2.0, (0.0, 0.0), -1.0)
for x_pos in (-30.0, 30.0):
_hole_z(7.0, thickness + 2.0, x_pos, 0.0)
part = _safe_chamfer(model.part, _line_edges(model.part), 0.8)
return _finish(part, "retainer_plate_elongated_slot_two_holes_chamfered_perimeter", Color(0.60, 0.62, 0.66, 1.0))
def make_keyed_shaft_hub():
outer_radius = 27.0
height = 22.0
bore_radius = 10.0
with BuildPart() as model:
Cylinder(outer_radius, height, align=(Align.CENTER, Align.CENTER, Align.MIN))
_hole_z(bore_radius * 2.0, height + 2.0, 0.0, 0.0)
with Locations(Location((0.0, bore_radius + 2.0, -1.0))):
Box(6.0, 7.0, height + 2.0, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
for index in range(4):
x_pos, y_pos = _polar_point(19.5, tau * index / 4.0 + tau / 8.0)
_hole_z(5.5, height + 2.0, x_pos, y_pos)
part = _safe_chamfer(model.part, _circular_edges(model.part, axis="z"), 0.7)
return _finish(part, "keyed_shaft_hub_central_bore_keyway_bolt_hole_pattern", Color(0.49, 0.52, 0.56, 1.0))
def make_t_slot_slider_block():
length = 80.0
width = 36.0
height = 18.0
with BuildPart() as model:
Box(length, width, height, align=(Align.CENTER, Align.CENTER, Align.MIN))
with Locations(Location((0.0, 0.0, 6.0))):
Box(length + 2.0, 24.0, 6.0, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
with Locations(Location((0.0, 0.0, 6.0))):
Box(length + 2.0, 10.0, height, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.SUBTRACT)
for y_pos in (-18.0, 18.0):
with Locations(Location((0.0, y_pos, 8.0))):
Box(48.0, 5.0, 8.0, align=(Align.CENTER, Align.CENTER, Align.CENTER), mode=Mode.SUBTRACT)
for x_pos in (-28.0, 28.0):
for y_pos in (-14.0, 14.0):
_hole_z(4.5, height + 2.0, x_pos, y_pos)
part = _safe_chamfer(model.part, _line_edges(model.part), 0.5)
return _finish(part, "t_slot_slider_block_central_channel_side_reliefs_mounting_holes", Color(0.47, 0.53, 0.55, 1.0))
def make_mounting_plate():
length = 100.0
width = 70.0
thickness = 8.0
with BuildPart() as model:
with BuildSketch(Plane.XY):
RectangleRounded(length, width, 5.0, align=(Align.CENTER, Align.CENTER))
extrude(amount=thickness)
_hole_z(28.0, thickness + 2.0, 0.0, 0.0)
_slot_z(30.0, 8.0, thickness + 2.0, (32.0, 0.0), -1.0, axis="y")
for x_pos in (-38.0, 38.0):
for y_pos in (-24.0, 24.0):
_hole_z(5.5, thickness + 2.0, x_pos, y_pos)
part = _safe_fillet(model.part, _line_edges(model.part), 0.8)
return _finish(part, "mounting_plate_central_cutout_side_slot_four_corner_holes_rounded_edges", Color(0.58, 0.65, 0.69, 1.0))