Loading viewer…

v1-drum

Fresh · 1d 📐 Draft
No compiled artifact yet
"""V1 drum project spec — derived from RUBISCO2 drum_v1.py.

V1 is the historical reference drum (700 mm OD × 2500 mm, 11 helical lifters,
gas engine + V-cradle support tires). V2 in the bible is the production
design (1100 × 4000 mm, electric, raceway band) — V1 stays for visual
comparison and the iterate-then-render demo loop.

Edit numerical params here to drive geometry; cadquery_part.py reads SPEC
on every run + Assembly.save() exports STEP/STL/GLB.
"""

SPEC = {
    "slug": "v1-drum",
    "title": "V1 drum (RUBISCO2 historical reference)",
    "units": "mm",

    # Drum body
    "drum_od_mm":       700.0,
    "drum_length_mm":  2500.0,
    "shell_thickness_mm": 3.0,

    # Welded segmented end rings
    "end_ring_radial_proud_mm": 25.0,
    "end_ring_thickness_mm":     8.0,
    "end_ring_inset_mm":        50.0,

    # Sheet overlap mid-ring
    "mid_ring_radial_proud_mm": 15.0,
    "mid_ring_thickness_mm":    6.0,

    # Lifters — 11 helical shovels (short plates, spiral arrangement)
    "lifter_count":              11,
    "lifter_axial_plate_mm":    200.0,
    "lifter_inward_height_mm":   60.0,
    "lifter_thickness_mm":        4.0,
    "lifter_x_start_mm":        250.0,
    "lifter_x_step_mm":         200.0,

    # Running bands — wide flat-bar where support tires contact
    "back_running_band_x_frac": 0.20,   # fraction of drum_length, near inlet/back support
    "running_band_x_frac":      0.80,   # fraction of drum_length, near discharge/front support
    "running_band_axial_w_mm": 110.0,
    "running_band_proud_mm":     6.0,

    # Longitudinal stringers (basket structure)
    "stringer_count":   8,
    "stringer_w_mm":   25.0,
    "stringer_h_mm":    5.0,

    # Support tires (rubber, V-cradle)
    "support_tire_radius_mm":     175.0,
    "support_tire_width_mm":       90.0,
    "support_tire_v_angle_deg":    25.0,

    # Gas engine block (visual concept)
    "engine_w_mm":   400.0,
    "engine_d_mm":   300.0,
    "engine_h_mm":   350.0,
    "engine_x_frac": 0.78,
    "engine_clearance_under_drum_mm": 300.0,

    # Gearbox block (visual concept)
    "gearbox_w_mm":  220.0,
    "gearbox_d_mm":  200.0,
    "gearbox_h_mm":  180.0,

    "outputs": {
        "step": "v1-drum.step",
        "stl":  "v1-drum.stl",
        "glb":  "v1-drum.glb",
    },
}
"""V1 drum — CadQuery build, exports STEP/STL/GLB.

Reads numerical params from spec.py. Iterations to spec.py drive geometry.

Mirrors the architecture of RUBISCO2/Sand Separator/docs/v2/drum_v1.py but
uses cq.Assembly().save() for GLB (cadquery 2.7.0 verified Apr 29 — the
ExportTypes.GLTF attribute does not exist; Assembly.save infers from .glb
extension).
"""
from __future__ import annotations

import math
import sys

import cadquery as cq

if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(encoding="utf-8", errors="replace")

from spec import SPEC


def build_assembly(s: dict = SPEC) -> cq.Assembly:
    drum_od = s["drum_od_mm"]
    drum_len = s["drum_length_mm"]
    shell_t = s["shell_thickness_mm"]
    drum_id = drum_od - 2 * shell_t

    asm = cq.Assembly()

    # Shell — perforated cylinder (perforations not modelled — visual only)
    outer = cq.Workplane("YZ").cylinder(drum_len, drum_od / 2)
    inner = cq.Workplane("YZ").cylinder(drum_len + 2, drum_id / 2)
    shell = outer.cut(inner).translate((drum_len / 2, 0, 0))
    asm.add(shell, name="V1 drum shell (3 mm perforated CS)",
            color=cq.Color(0.45, 0.42, 0.38))

    # End rings — upstream + downstream
    er_proud = s["end_ring_radial_proud_mm"]
    er_t     = s["end_ring_thickness_mm"]
    er_inset = s["end_ring_inset_mm"]
    er_od = drum_od + 2 * er_proud
    for x_pos, label in ((er_inset, "upstream"), (drum_len - er_inset, "downstream")):
        outer = cq.Workplane("YZ").cylinder(er_t, er_od / 2)
        inner = cq.Workplane("YZ").cylinder(er_t + 2, drum_od / 2)
        ring = outer.cut(inner).translate((x_pos, 0, 0))
        asm.add(ring, name=f"V1 {label} welded end ring",
                color=cq.Color(0.40, 0.40, 0.40))

    # Mid ring — sheet overlap join
    mr_proud = s["mid_ring_radial_proud_mm"]
    mr_t = s["mid_ring_thickness_mm"]
    mr_od = drum_od + 2 * mr_proud
    outer = cq.Workplane("YZ").cylinder(mr_t, mr_od / 2)
    inner = cq.Workplane("YZ").cylinder(mr_t + 2, drum_od / 2)
    mid_ring = outer.cut(inner).translate((drum_len / 2, 0, 0))
    asm.add(mid_ring, name="V1 mid-ring (sheet overlap join)",
            color=cq.Color(0.40, 0.40, 0.40))

    # Running bands — wide rolled flat-bar, one at back/inlet and one near discharge
    rb_positions = (
        (drum_len * s["back_running_band_x_frac"], "back/inlet"),
        (drum_len * s["running_band_x_frac"], "front/discharge"),
    )
    rb_w = s["running_band_axial_w_mm"]
    rb_proud = s["running_band_proud_mm"]
    rb_od = drum_od + 2 * rb_proud
    for rb_x, rb_label in rb_positions:
        outer = cq.Workplane("YZ").cylinder(rb_w, rb_od / 2)
        inner = cq.Workplane("YZ").cylinder(rb_w + 2, drum_od / 2)
        rb = outer.cut(inner).translate((rb_x, 0, 0))
        asm.add(rb, name=f"V1 {rb_label} running band (wide flat-bar — support tires roll on this)",
                color=cq.Color(0.32, 0.32, 0.34))

    # Longitudinal stringers (basket structure)
    n_str = s["stringer_count"]
    str_w = s["stringer_w_mm"]
    str_h = s["stringer_h_mm"]
    str_axial = drum_len - 2 * er_inset
    drum_radius = drum_od / 2
    for i in range(n_str):
        theta = i * 360.0 / n_str
        bar = (cq.Workplane("XY").box(str_axial, str_w, str_h)
               .translate((0, 0, drum_radius + str_h / 2))
               .rotate((0, 0, 0), (1, 0, 0), theta)
               .translate((drum_len / 2, 0, 0)))
        asm.add(bar, name=f"V1 longitudinal stringer {i+1}/{n_str}",
                color=cq.Color(0.42, 0.42, 0.44))

    # Lifters — 11 short plates, helical
    n_lift = s["lifter_count"]
    lift_axial = s["lifter_axial_plate_mm"]
    lift_inward = s["lifter_inward_height_mm"]
    lift_t = s["lifter_thickness_mm"]
    lift_x_start = s["lifter_x_start_mm"]
    lift_x_step = s["lifter_x_step_mm"]
    theta_step = 360.0 / n_lift
    for i in range(n_lift):
        plate_x = lift_x_start + i * lift_x_step
        plate_theta = i * theta_step
        plate = (cq.Workplane("XY").box(lift_axial, lift_t, lift_inward)
                 .translate((0, 0, drum_id / 2 - lift_inward / 2))
                 .rotate((0, 0, 0), (1, 0, 0), plate_theta)
                 .translate((plate_x, 0, 0)))
        asm.add(plate,
                name=f"V1 lifter {i+1}/{n_lift} @ X={plate_x:.0f}, θ={plate_theta:.0f}°",
                color=cq.Color(0.55, 0.45, 0.30))

    # Engine block + gearbox (visual)
    eng_w = s["engine_w_mm"]; eng_d = s["engine_d_mm"]; eng_h = s["engine_h_mm"]
    eng_x = drum_len * s["engine_x_frac"]
    ground_z = -(drum_radius + s["engine_clearance_under_drum_mm"])
    eng_y = -(drum_radius + 220)
    eng_z = ground_z + eng_h / 2
    engine = cq.Workplane("XY").box(eng_w, eng_d, eng_h).translate((eng_x, eng_y, eng_z))
    asm.add(engine, name="V1 gas engine (7.5 HP, ~212cc — red Honda-style)",
            color=cq.Color(0.78, 0.10, 0.10))

    gb_w = s["gearbox_w_mm"]; gb_d = s["gearbox_d_mm"]; gb_h = s["gearbox_h_mm"]
    gb_x = eng_x - eng_w/2 - gb_w/2 - 20
    gearbox = cq.Workplane("XY").box(gb_w, gb_d, gb_h).translate((gb_x, eng_y, eng_z))
    asm.add(gearbox, name="V1 gearbox (engine → chain → driven tire shaft)",
            color=cq.Color(0.28, 0.28, 0.30))

    # Support tires (V-cradle) — front pair plus added back pair
    tire_r = s["support_tire_radius_mm"]
    tire_w = s["support_tire_width_mm"]
    v_deg  = s["support_tire_v_angle_deg"]
    r_band = drum_od / 2 + rb_proud
    contact_r = r_band + tire_r
    angle_rad = v_deg * math.pi / 180.0
    for rb_x, rb_label in rb_positions:
        for side, label in ((+1, "driven"), (-1, "passive")):
            dy = side * contact_r * math.sin(angle_rad)
            dz = -contact_r * math.cos(angle_rad)
            cyl = cq.Workplane("YZ").cylinder(tire_w, tire_r).translate((rb_x, dy, dz))
            asm.add(cyl, name=f"V1 {rb_label} {label} support tire (V-cradle {'+' if side > 0 else '-'})",
                    color=cq.Color(0.10, 0.10, 0.10))

    return asm


def main():
    asm = build_assembly(SPEC)
    out = SPEC["outputs"]
    asm.save(out["step"])
    cq.exporters.export(asm.toCompound(), out["stl"], exportType="STL", tolerance=0.5)
    asm.save(out["glb"])  # Assembly.save infers GLB from .glb extension (cq 2.7.0)
    print(f"V1 drum: {SPEC['drum_length_mm']}×{SPEC['drum_od_mm']} mm OD, "
          f"{SPEC['lifter_count']} lifters → step + stl + glb")


if __name__ == "__main__":
    main()
"""V1 drum — SolidWorks native (placeholder).

Filled when Pablo wants the parametric .SLDPRT version. The CadQuery STEP
in build/ can be imported via the local sw_compile_server.py helper as the
fallback path (operator 14 STEP-import pattern). For now, this is a stub
so the project structure is consistent across all scaffolds.
"""
from __future__ import annotations

# TODO: when ready, expand into a full SW-native build mirroring drum_v1.py.
# For now, the CadQuery output is the source of truth; SW imports it as a
# read-only solid until the parametric SW tree is built.

print("native.py is a stub — build a SolidWorks-native version when needed.")
Working…
0s