Files
cadSet/text-to-cad/models/robots/juno/juno_parts/torso.py
T
2026-07-22 19:38:36 +08:00

205 lines
6.4 KiB
Python

"""juno torso/chest.
Local frame: origin at the waist-yaw joint center (bottom of torso),
+X forward, +Y robot-left, +Z up. Units mm.
Locked interfaces (do not change):
- nothing below z=+2; graphite waist collar dia 100 spans z 2..36
- chest volume z 36..320, |y| <= 118, x -78..+82
- shoulder bosses: ALU cylinders along Y at (0, +-y, 290), |y| 112..132, dia 76
- neck: ALU collar dia 52, z 318..324; z > 324 kept clear within r < 40
- top deck <= z 330
Construction: a graphite lofted core plus a 5 mm conformal shell ring
(outer loft minus inner loft). Ice-gray armor panels are pieces of that
ring clipped by rounded region boxes, so every seam is a 5 mm-deep
recess exposing the graphite core (G1-style two-tone panel lines).
"""
from __future__ import annotations
import sys
from pathlib import Path
import math # noqa: F401 (kept for parity with sibling part modules)
from build123d import (
Axis,
Box,
Cylinder,
Plane,
Pos,
RectangleRounded,
Rot,
SortBy,
chamfer,
fillet,
loft,
)
from .juno_lib import (
ALU_COLOR,
SHELL_COLOR,
STRUCT_COLOR,
VISOR_COLOR,
part_compound,
styled,
)
# Loft sections: (z, depth_x, width_y, corner_r, center_x).
# Front line bulges to x=+78 at the chest and pulls back at the top for a
# slightly forward-leaning athletic silhouette; width V-tapers 148 -> 222.
_OUTER = [
(36.0, 104.0, 132.0, 22.0, 2.0),
(90.0, 112.0, 150.0, 26.0, 5.0),
(160.0, 122.0, 172.0, 30.0, 9.0),
(225.0, 132.0, 196.0, 33.0, 12.0),
(272.0, 138.0, 216.0, 35.0, 9.0),
(300.0, 132.0, 222.0, 36.0, 6.0),
(310.0, 124.0, 210.0, 34.0, 4.0),
]
_SHELL_T = 5.0
_INNER = [
(z, d - 2 * _SHELL_T, w - 2 * _SHELL_T, r - _SHELL_T, cx)
for (z, d, w, r, cx) in _OUTER
]
def _loft_solid(specs):
sections = [
Plane.XY.offset(z) * Pos(cx, 0, 0) * RectangleRounded(d, w, r)
for (z, d, w, r, cx) in specs
]
return loft(sections)
def _safe_fillet(solid, edges, radius):
try:
return fillet(edges, radius)
except Exception:
return solid
def _safe_chamfer(solid, edges, length):
try:
return chamfer(edges, length)
except Exception:
return solid
def _region(size, center, radius):
"""Clip volume with rounded Y/Z seam corners (edges along X filleted)."""
box = Pos(*center) * Box(*size)
try:
box = fillet(box.edges().filter_by(Axis.X), radius)
except Exception:
pass
return box
def _clip_panel(ring, region):
piece = ring & region
solids = piece.solids()
if len(solids) > 1:
piece = solids.sort_by(SortBy.VOLUME)[-1]
return _safe_fillet(piece, piece.edges(), 1.0)
def build_torso():
children = []
# --- graphite waist collar (locked: dia 100, z 2..36) ----------------
collar = Pos(0, 0, 19) * Cylinder(radius=50, height=34)
collar = _safe_chamfer(collar, collar.edges(), 2.0)
for zc in (9.0, 27.0):
groove = Pos(0, 0, zc) * (
Cylinder(radius=52, height=2.4) - Cylinder(radius=48, height=2.4)
)
collar = collar - groove
children.append(styled(collar, "torso_waist_collar", STRUCT_COLOR))
# --- graphite core + shoulder gusset barrels --------------------------
outer = _loft_solid(_OUTER)
inner = _loft_solid(_INNER)
ring = outer - inner
core = inner
for sgn in (1.0, -1.0):
gusset = Pos(0, sgn * 100.0, 290) * Rot(-90, 0, 0) * Cylinder(
radius=40, height=24
)
gusset = _safe_chamfer(gusset, gusset.edges(), 1.5)
core = core + gusset
# side intake louvres: three slanted grooves cut into each flank
for sgn in (1.0, -1.0):
for xc in (-14.0, 4.0, 22.0):
slot = Pos(xc, sgn * 90.0, 205) * Rot(0, -12, 0) * Box(9, 14, 64)
core = core - slot
# upper-chest sensor pocket (flat floor at x = 59)
pocket = Pos(69.5, 0, 302) * Box(21, 48, 11)
core = core - pocket
children.append(styled(core, "torso_core", STRUCT_COLOR))
# gloss-black sensor bar recessed in the pocket
sensor = Pos(61.5, 0, 301.5) * Box(5, 44, 8)
sensor = _safe_fillet(sensor, sensor.edges().filter_by(Axis.X), 2.0)
children.append(styled(sensor, "torso_chest_sensor", VISOR_COLOR))
# --- ice-gray armor panels (conformal ring pieces) --------------------
# twin pectoral plates with a 10 mm center crease
for sgn, tag in ((1.0, "l"), (-1.0, "r")):
pec = _clip_panel(
ring, _region((95, 68, 146), (47.5, sgn * 39.0, 223), 12)
)
children.append(styled(pec, f"torso_pec_panel_{tag}", SHELL_COLOR))
# abdomen plate below an 8 mm horizontal reveal
abdomen = _clip_panel(ring, _region((95, 90, 92), (47.5, 0, 98), 10))
children.append(styled(abdomen, "torso_abdomen_panel", SHELL_COLOR))
# thin backpack panels either side of the graphite spine column
for sgn, tag in ((1.0, "l"), (-1.0, "r")):
back = _clip_panel(
ring, _region((65, 45, 220), (-62.5, sgn * 35.5, 180), 10)
)
children.append(styled(back, f"torso_back_panel_{tag}", SHELL_COLOR))
# slim graphite spine column, flush with the back panels
spine = _clip_panel(ring, _region((65, 16, 258), (-62.5, 0, 173), 6))
children.append(styled(spine, "torso_spine_column", STRUCT_COLOR))
# --- top deck + neck collar -------------------------------------------
deck_specs = [
(310.0, 110.0, 170.0, 30.0, 4.0),
(318.0, 110.0, 170.0, 30.0, 4.0),
]
deck = _loft_solid(deck_specs)
deck = _safe_chamfer(deck, deck.edges().group_by(Axis.Z)[-1], 2.5)
children.append(styled(deck, "torso_top_deck", SHELL_COLOR))
neck = Pos(0, 0, 321) * Cylinder(radius=26, height=6)
neck = _safe_chamfer(neck, neck.edges().group_by(Axis.Z)[-1], 1.0)
children.append(styled(neck, "torso_neck_collar", ALU_COLOR))
# --- aluminum shoulder bosses (locked: dia 76, |y| 112..132) ----------
for sgn, tag in ((1.0, "l"), (-1.0, "r")):
boss = Pos(0, sgn * 122.0, 290) * Rot(-90, 0, 0) * Cylinder(
radius=38, height=20
)
boss = _safe_chamfer(boss, boss.edges(), 2.0)
groove = Pos(0, sgn * 126.0, 290) * Rot(-90, 0, 0) * (
Cylinder(radius=39, height=2.5) - Cylinder(radius=36.5, height=2.5)
)
boss = boss - groove
children.append(styled(boss, f"torso_shoulder_boss_{tag}", ALU_COLOR))
return part_compound("torso", children)
def gen_step():
return build_torso()