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