QUELLCODE / PY

model_components.py

design/model_components.py

1# OpenAss: nur privater, experimenteller Gebrauch; auf eigene Verantwortung.2# Niemals Unternehmensrechner, Produktivsysteme, Industrieanlagen oder3# sicherheitskritische Systeme anschließen oder bedienen.4# Haftungshinweis und gesetzliche Grenzen: ../DISCLAIMER.txt.5"""Presentation-grade internals for the universal hardware-box concept.67All helper coordinates are in millimetres. Board outlines use manufacturer8reference dimensions; component placement and cable bends are visual estimates.9This is a concept model, not PCB data or an enclosure fit certificate.10"""1112import math131415def build_internals(api, mats):16    """Build detailed Pi, capture, Pico, isolation board and internal harness.1718    Return a mapping of independently movable component-root Blender objects.19    The supplied API handles mm-to-m scaling and material assignment.20    """21    roots = {}2223    def root(key, name):24        ob = api.empty(name)25        roots[key] = ob26        return ob2728    def box(name, p, s, mat, bevel=0.35, parent=None):29        return api.box(name, p, s, mats[mat], bevel_mm=bevel, parent=parent)3031    def cyl(name, p, radius, depth, mat, parent=None, rotation=(0, 0, 0), vertices=40):32        return api.cylinder(name, p, radius, depth, mats[mat], rotation=rotation,33                            parent=parent, vertices=vertices)3435    def label(name, text, p, size, mat, parent):36        return api.text(name, text, p, size, mats[mat], parent=parent)3738    def wire(name, points, radius, mat, parent):39        return api.curve(name, points, radius, mats[mat], parent=parent)4041    def chip(name, p, s, parent, pin_count=6):42        ob = box(name, p, s, "black", 0.2, parent)43        # Exposed gull-wing leads make the boards legible in the exploded view.44        for side in (-1, 1):45            for n in range(pin_count):46                x = p[0] - s[0] * .35 + n * s[0] * .7 / max(1, pin_count-1)47                box(f"{name}_lead_{side}_{n}",48                    (x, p[1] + side * (s[1]/2+.55), p[2]-.15),49                    (.55, 1.15, .25), "silver", .08, parent)50        return ob5152    def mounting(name, xy, boardz, parent, footz=8):53        x, y = xy54        # A dark centre indicates the through hole, surrounded by a copper ring.55        cyl(name+"_pad", (x, y, boardz+.84), 2.05, .12, "gold", parent)56        cyl(name+"_standoff", (x, y, (footz+boardz-.8)/2), 1.65,57            boardz-.8-footz, "silver", parent, vertices=6)58        cyl(name+"_screw", (x, y, boardz+1.06), 1.38, .46, "silver", parent)59        box(name+"_screw_slot", (x, y, boardz+1.3), (1.65, .33, .07),60            "black", .04, parent)6162    # Raspberry Pi 5: 85 x 56 mm PCB, deliberately rotated so its USB/Ethernet63    # stack faces the left service edge. The active cooler remains visible.64    pi = root("pi", "COMPONENT_01_Raspberry_Pi_5")65    px, py, pz = -55.0, 14.0, 13.066    box("Pi5_85x56_PCB", (px, py, pz), (85, 56, 1.6), "pcb", 2.1, pi)67    box("Pi5_underside", (px, py, pz-.88), (79, 51, .25), "pcb_dark", 1.3, pi)68    for i, (dx, dy) in enumerate(((-29,-24.5), (29,-24.5), (-29,24.5), (29,24.5))):69        mounting(f"Pi5_mount_{i+1}", (px+dx, py+dy), pz, pi)7071    # Double-height USB ports and RJ45 metal shield.72    for idx, (yy, insert) in enumerate(((py+19, "teal"), (py+1, "black"))):73        cx = px-35.174        box(f"Pi5_USB_stack_{idx+1}", (cx, yy, 22.2), (18.5, 15.2, 16.6),75            "silver", .6, pi)76        for level, zz in enumerate((18.1, 25.6)):77            box(f"Pi5_USB_opening_{idx}_{level}", (cx-9.3, yy, zz),78                (.4, 12.7, 5.7), "black", .5, pi)79            box(f"Pi5_USB_tongue_{idx}_{level}", (cx-9.57, yy, zz-.7),80                (.2, 10.9, 1.25), insert, .1, pi)81            for pin in range(4):82                box(f"Pi5_USB_contact_{idx}_{level}_{pin}",83                    (cx-9.7, yy-3.5+pin*2.3, zz-.03), (.09, .52, .25),84                    "gold", .03, pi)85    ex, ey = px-34.5, py-17.386    box("Pi5_RJ45_shield", (ex, ey, 20.7), (19.5, 16, 13.6), "silver", .7, pi)87    box("Pi5_RJ45_socket", (ex-9.9, ey, 20.2), (.45, 12.5, 9.2), "black", .55, pi)88    box("Pi5_RJ45_clip", (ex-10.15, ey, 16), (.2, 5.5, 1.4), "silver", .15, pi)89    for i in range(8):90        box(f"Pi5_RJ45_gold_{i}", (ex-10.2, ey-4.55+i*1.3, 22.45),91            (.16,.55,3.2), "gold", .06, pi)92    for dy, material in ((-5.6, "teal"), (5.6, "gold")):93        box("Pi5_RJ45_activity_"+material, (ex-10.2, ey+dy, 24.2),94            (.15, 1.35, 1.35), material, .12, pi)9596    # GPIO 2 x 20 header, 2.54 mm pitch.97    gx, gy = px+5, py+24.498    box("Pi5_GPIO_header_body", (gx, gy, 15.55), (52.1, 5.4, 3.3), "black", .4, pi)99    for row in range(2):100        for pin in range(20):101            box(f"Pi5_GPIO_{row}_{pin}",102                (gx-24.13+pin*2.54, gy-1.27+row*2.54, 19.3),103                (.65,.65,5.3), "gold", .09, pi)104    label("Pi5_GPIO_mark", "GPIO  40", (px-17, py+18.3, 13.86), 2, "white", pi)105106    # Fin heat sink and the round black active-cooler blower.107    hx, hy = px+7, py-1.7108    box("Pi5_active_cooler_base", (hx,hy,16.65), (42,33,5.4), "silver", 2.2, pi)109    for i in range(14):110        box(f"Pi5_cooler_fin_{i:02}", (hx-19.5+i*3,hy,20.2),111            (1.1,31,4), "silver", .22, pi)112    fx, fy = hx-6.5, hy113    cyl("Pi5_fan_outer_frame", (fx,fy,24.05), 12.8, 3.5, "black", pi, vertices=64)114    cyl("Pi5_fan_recess", (fx,fy,25.85), 11.45, .2, "graphite", pi, vertices=64)115    cyl("Pi5_fan_hub", (fx,fy,26.27), 3.2, .9, "black", pi)116    for i in range(9):117        angle = math.tau*i/9+.25118        blade = box(f"Pi5_fan_blade_{i}",119                    (fx+7*math.cos(angle),fy+7*math.sin(angle),26.12),120                    (8.3,2.3,.6), "black", .7, pi)121        blade.rotation_euler[2] = angle+.5122    for i, (dx,dy) in enumerate(((-10,-10),(-10,10),(10,-10),(10,10))):123        cyl(f"Pi5_fan_fastener_{i}",(fx+dx,fy+dy,26),.75,.5,"silver",pi)124    wire("Pi5_fan_power_wire", [(fx+10,fy+10,25),(px+22,py+14,23),125                               (px+24,py+17,15)], .43, "black", pi)126    box("Pi5_fan_power_header", (px+24,py+17,15), (4.5,3.4,2.2), "white", .35, pi)127    chip("Pi5_RP1_IO_controller", (px+27, py-13.5, 15), (10.5,10.5,2.2), pi, 8)128    box("Pi5_RAM_package", (px+3,py-21.5,14.8), (13,6.8,1.8), "black", .3, pi)129    label("Pi5_RAM_print", "8 GB", (px-2.5,py-22.2,15.8), 1.7, "white", pi)130    label("Pi5_board_label", "RASPBERRY PI 5", (px-24,py-26.2,13.86), 2.3, "white", pi)131    # Surface components, test pads, small ribbon sockets and power port.132    for i in range(12):133        xx = px+16+(i%4)*5.2134        yy = py+5+(i//4)*4.5135        box(f"Pi5_SMD_{i}", (xx,yy,14.3), (2.4,1.35,.8), "black", .12, pi)136        for side in (-1,1):137            box(f"Pi5_SMD_end_{i}_{side}", (xx+side*1.2,yy,14.4),138                (.65,1.4,.7), "silver", .08, pi)139    for i in range(6):140        cyl(f"Pi5_testpad_{i}", (px+35,py-12+i*6.5,13.85), .65, .1, "gold", pi, vertices=20)141    for i in range(2):142        box(f"Pi5_FFC_socket_{i}", (px-7+i*17,py-24.2,15),143            (12.5,3.3,2.2), "white", .25, pi)144        box(f"Pi5_FFC_latch_{i}", (px-7+i*17,py-24.8,16.15),145            (12.1,1.15,.4), "black", .12, pi)146    box("Pi5_USBC_power_shell", (px+32.7,py-25.3,15.7), (9.5,7.8,3.5), "silver", .8, pi)147    box("Pi5_USBC_power_aperture", (px+32.7,py-29.25,15.7),148        (7.6,.25,2.2), "black", .6, pi)149150    # Magewell USB Capture HDMI 4K Plus reference envelope 98.1 x 56.8 x 17.151    capture = root("capture", "COMPONENT_02_HDMI_capture_loop_module")152    cx, cy = 43.5,14.0153    box("Capture_98_1x56_8x17_body", (cx,cy,24.5), (98.1,56.8,17), "black", 3.1, capture)154    box("Capture_top_panel", (cx,cy,33.07), (94,52,.25), "graphite", 2.2, capture)155    # Fine ridges evoke the metal module body without hiding the core label.156    for side in (-1,1):157        for i in range(6):158            box(f"Capture_heat_ridge_{side}_{i}", (cx,cy+side*(19.2+i*1.2),33.5),159                (87,.5,.55), "black", .16, capture)160    box("Capture_badge", (cx,cy,33.56), (76,27,.42), "black", 1.7, capture)161    label("Capture_badge_title", "HDMI CAPTURE", (cx-32,cy+4.8,33.83), 4, "white", capture)162    label("Capture_badge_line2", "4K60  LOOP THROUGH", (cx-32,cy-3.4,33.83), 2.7, "teal", capture)163    label("Capture_badge_line3", "USB 3   /   1080p60 CAPTURE", (cx-32,cy-9.8,33.83), 2.05, "white", capture)164    box("Capture_signal_LED", (cx+36,cy+8,33.9), (3,1.3,.3), "teal", .3, capture)165    # Paired HDMI modules are at the rear; the enclosure's panel ports are166    # separate objects in the main scene builder.167    for i, xx in enumerate((cx+22,cx-6)):168        box(f"Capture_HDMI_rear_shell_{i}", (xx,cy+25.65,24),169            (16.2,6.8,6.3), "silver", .75, capture)170        box(f"Capture_HDMI_rear_inset_{i}", (xx,cy+29.12,24),171            (13.8,.3,4.7), "black", .55, capture)172        box(f"Capture_HDMI_rear_tongue_{i}", (xx,cy+29.29,24.25),173            (11.6,.18,1.4), "gold", .18, capture)174    box("Capture_USB3_shell", (cx-48.4,cy,24), (6.5,13.3,5.7), "silver", .65, capture)175    box("Capture_USB3_insert", (cx-51.8,cy,24), (.25,10.9,3.9), "teal", .35, capture)176    for i, (dx,dy) in enumerate(((-43,-22),(43,-22),(-43,22),(43,22))):177        cyl(f"Capture_bottom_mount_{i}",(cx+dx,cy+dy,12),2.1,8,"silver",capture,vertices=6)178179    # Pico 2: the 51 x 21 x 1 mm reference PCB, two rows of 20 pads at180    # 2.54 mm pitch and central RP2350 chip.181    pico = root("pico", "COMPONENT_03_Pico_2_HID_controller")182    qx,qy,qz=-46,-45,14.0183    box("Pico2_51x21x1_PCB", (qx,qy,qz), (51,21,1), "pcb", 1.3, pico)184    for side in (-1,1):185        for i in range(20):186            xx=qx-24.13+i*2.54187            yy=qy+side*8.89188            cyl(f"Pico2_castellation_{side}_{i}", (xx,yy,14.56), .92,.1,"gold",pico,vertices=24)189            cyl(f"Pico2_pin_hole_{side}_{i}", (xx,yy,14.62), .4,.05,"black",pico,vertices=20)190            box(f"Pico2_header_pin_{side}_{i}", (xx,yy,16.4), (.55,.55,3.2),"gold",.06,pico)191    chip("Pico2_RP2350", (qx+1,qy,15.35), (8.2,8.2,1.8), pico, 6)192    label("Pico2_RP2350_print", "RP2350", (qx-2.5,qy-.65,16.3),1.05,"white",pico)193    box("Pico2_micro_USB_shell", (qx-23.4,qy,16.3), (7.4,7.8,3.3),"silver",.65,pico)194    box("Pico2_micro_USB_inset", (qx-27.17,qy,16.3),(.25,5.6,1.9),"black",.55,pico)195    box("Pico2_BOOTSEL",(qx-14.2,qy+2.3,16), (4.4,3.1,2.2),"silver",.35,pico)196    box("Pico2_BOOTSEL_button",(qx-14.2,qy+2.3,17.2),(2.6,1.9,.4),"white",.2,pico)197    cyl("Pico2_crystal",(qx+11,qy,15.5),2.1,1.1,"silver",pico)198    box("Pico2_LED",(qx-16.4,qy-4.3,15),(2,1.5,.9),"teal",.2,pico)199    label("Pico2_board_mark", "PICO 2 / HID",(qx-6,qy+5.15,14.58),1.65,"white",pico)200    label("Pico2_USB_mark", "USB",(qx-23.5,qy-6,14.58),1.5,"white",pico)201    for i in range(6):202        box(f"Pico2_SMD_{i}",(qx+13+(i%2)*4,qy-5+(i//2)*4.7,15),203            (2,1.2,.8),"black",.12,pico)204    for i,xx in enumerate((qx-18.5,qx+20)):205        mounting(f"Pico2_mount_{i}",(xx,qy),qz-.3,pico)206207    # Compact concept board for isolated UART, the independent HID data208    # switch and distributed power. This custom board is explicitly conceptual.209    safe = root("safety", "COMPONENT_04_Isolation_and_power_concept_PCB")210    sx,sy,sz=43.5,-45,13.5211    box("Safety_55x22_PCB",(sx,sy,sz),(55,22,1.6),"pcb_dark",1.8,safe)212    chip("Safety_UART_ISO6721",(sx-12,sy+1,15.6),(7.4,6,2.5),safe,4)213    chip("Safety_HID_data_switch",(sx+1,sy+1,15.35),(5.5,4.2,2),safe,3)214    chip("Safety_DC_distribution",(sx+11,sy+1,15.45),(5.5,6.5,2.2),safe,4)215    box("Safety_isolation_gap",(sx-5.9,sy+1,14.34),(1.7,13.5,.04),"black",.15,safe)216    for i,xx in enumerate((sx-21,sx+21)):217        box(f"Safety_terminal_block_{i}",(xx,sy,18),(7,12,7.3),"teal",.8,safe)218        for j in range(3):219            yy=sy-4+j*4220            cyl(f"Safety_terminal_screw_{i}_{j}",(xx,yy,21.85),1.15,.24,"silver",safe)221            box(f"Safety_terminal_slot_{i}_{j}",(xx,yy,22),(1.4,.27,.05),"black",.05,safe)222    for i in range(5):223        box(f"Safety_passive_{i}",(sx-15+i*7.2,sy-6.6,14.7),(2.5,1.3,.75),"silver",.1,safe)224    label("Safety_board_caption", "ISOLATED UART + USB SAFETY",(sx-16.5,sy+7.5,14.36),1.55,"white",safe)225    label("Safety_concept_caption", "CUSTOM PCB CONCEPT",(sx-12.8,sy-9.5,14.36),1.5,"teal",safe)226    for i,xy in enumerate(((sx-24,sy-8),(sx+24,sy+8))):227        mounting(f"Safety_mount_{i}",xy,sz,safe)228229    harness = root("harness", "COMPONENT_05_Internal_harness")230    # Short USB 3 capture path, dressed into the service channel along the231    # front of the Pi to avoid covering the fan or the capture-module badge.232    wire("Harness_USB3_capture", [(-8.9,14,24),(-17,14,24),(-18,-17,19),233                                  (-20,-26,18),(-82,-26,18),(-100,-19,20),234                                  (-100,33,25.6),(-98.7,33,25.6)], 2.2, "rubber", harness)235    box("Harness_USB3_capture_boot",(-9.7,14,24),(10,11.6,7.2),"black",1.25,harness)236    box("Harness_USB3_Pi_boot",(-98.7,33,25.6),(7,12,7.4),"black",1.1,harness)237    # Red/teal/black UART and local control leads, kept in the lower front238    # wiring channel for a clean presentation and a credible wiring path.239    for i,mat in enumerate(("red","teal","black")):240        off=(i-1)*1.7241        wire(f"Harness_UART_{mat}", [(-28,37.2+off,20),(-20,30+off,19),242                                    (-17,-25+off,12),(-23,-30+off,12),243                                    (-29,-35.8+off,16)], .62,mat,harness)244        wire(f"Harness_isolated_link_{mat}", [(-24,-48+off,16),(-12,-58+off,11),245                                             (3,-58+off,11),(22.5,-46+off,20)],246             .62,mat,harness)247    for i,mat in enumerate(("red","black")):248        wire(f"Harness_power_{mat}",[(64.5,-43+i*4,20),(82,-34+i*3,13),249                                   (84,-16+i*2,13),(87,-10+i*2,18)],250             .9,mat,harness)251    # Soft cable saddles and tie bands are explicitly modeled so the internals252    # feel assembled rather than floating inside the concept enclosure.253    for i,xx in enumerate((-72,-39,5)):254        box(f"Harness_saddle_{i}",(xx,-28,10.6),(6.5,8,4.8),"black",.6,harness)255        box(f"Harness_tie_{i}",(xx,-28,13.1),(1.6,8.2,.6),"rubber",.25,harness)256    return roots