Relocate DHT11 to rear snap bay and fix front/back closing lips.
Regenerate STLs, update viewer closed pose, and document assembly. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,33 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate printable STL for CYD climate case using trimesh + manifold boolean."""
|
||||
"""
|
||||
CYD climate case STL generator.
|
||||
|
||||
Design intent:
|
||||
- DHT11 on the BACK cover, held by snap/clip posts (卡位), tip slightly proud of back face
|
||||
- Front + back close with a interlocking lip (合盖)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import trimesh
|
||||
from trimesh.creation import box, cylinder
|
||||
from trimesh.creation import box, cylinder # noqa: F401 — cylinder used for holes/bosses
|
||||
|
||||
# ---- mm, aligned with cyd_climate_case.scad ----
|
||||
# ---- mm ----
|
||||
PCB_L, PCB_W = 86.0, 50.0
|
||||
MH_INSET = 4.0
|
||||
DISP_L, DISP_W, DISP_H, DISP_X = 69.0, 50.0, 4.0, 8.0
|
||||
DISP_X, DISP_L, DISP_W, DISP_H = 8.0, 69.0, 50.0, 4.0
|
||||
VIEW_L, VIEW_W, VIEW_IX, VIEW_IY = 59.5, 45.0, 7.0, 2.5
|
||||
WALL, GAP = 2.4, 0.4
|
||||
BEZEL_T, CAVITY = 2.0, 7.0
|
||||
PCB_T_HALF = 0.8
|
||||
USB_W, USB_H, USB_Y = 14.0, 8.5, 30.5
|
||||
STAND_FOOT = 18.0
|
||||
DHT_L, DHT_W, DHT_H = 22.0, 18.0, 14.0
|
||||
WALL, GAP = 2.4, 0.35
|
||||
BEZEL_T, CAVITY = 2.2, 7.5
|
||||
LIP_H, LIP_T = 2.4, 1.15 # front male / back female for 合盖
|
||||
USB_W, USB_H, USB_Y = 15.0, 9.0, 30.5
|
||||
STAND_FOOT = 16.0
|
||||
SCREW = 3.3
|
||||
BOSS_OD, BOSS_ID = 7.2, 2.5
|
||||
BOSS_OD, BOSS_ID = 7.0, 2.5
|
||||
|
||||
# DHT11 module (common PCB module ~15.5 x 12 x 7.5 + pins)
|
||||
DHT_MOD_L, DHT_MOD_W, DHT_MOD_H = 16.0, 12.5, 8.0
|
||||
DHT_PROUD = 1.8 # how much grill/tip sticks out past back outer face
|
||||
DHT_CLIP = 1.1 # undercut lip thickness for 卡位
|
||||
|
||||
INNER_L = PCB_L + 2 * GAP
|
||||
INNER_W = PCB_W + 2 * GAP
|
||||
OUTER_L = INNER_L + 2 * WALL
|
||||
OUTER_W = INNER_W + 2 * WALL
|
||||
SHELL_H = CAVITY + WALL
|
||||
SKIRT_H = DISP_H + 1.0
|
||||
|
||||
MH = [
|
||||
(MH_INSET, MH_INSET),
|
||||
@@ -37,141 +47,240 @@ MH = [
|
||||
]
|
||||
|
||||
|
||||
def place(mesh: trimesh.Trimesh, center: tuple[float, float, float]) -> trimesh.Trimesh:
|
||||
m = mesh.copy()
|
||||
m.apply_translation(np.array(center) - m.bounding_box.centroid)
|
||||
return m
|
||||
|
||||
|
||||
def aabb(x0, y0, z0, x1, y1, z1) -> trimesh.Trimesh:
|
||||
dx, dy, dz = x1 - x0, y1 - y0, z1 - z0
|
||||
xa, xb = sorted((float(x0), float(x1)))
|
||||
ya, yb = sorted((float(y0), float(y1)))
|
||||
za, zb = sorted((float(z0), float(z1)))
|
||||
dx, dy, dz = xb - xa, yb - ya, zb - za
|
||||
if dx < 1e-6 or dy < 1e-6 or dz < 1e-6:
|
||||
raise ValueError(f"degenerate box {[xa,ya,za,xb,yb,zb]}")
|
||||
m = box(extents=[dx, dy, dz])
|
||||
m.apply_translation([x0 + dx / 2, y0 + dy / 2, z0 + dz / 2])
|
||||
m.apply_translation([xa + dx / 2, ya + dy / 2, za + dz / 2])
|
||||
return m
|
||||
|
||||
|
||||
def u(*meshes: trimesh.Trimesh) -> trimesh.Trimesh:
|
||||
result = meshes[0]
|
||||
r = meshes[0]
|
||||
for m in meshes[1:]:
|
||||
result = result.union(m, engine="manifold")
|
||||
return result
|
||||
r = r.union(m, engine="manifold")
|
||||
return r
|
||||
|
||||
|
||||
def d(a: trimesh.Trimesh, *subs: trimesh.Trimesh) -> trimesh.Trimesh:
|
||||
result = a
|
||||
r = a
|
||||
for s in subs:
|
||||
result = result.difference(s, engine="manifold")
|
||||
return result
|
||||
r = r.difference(s, engine="manifold")
|
||||
return r
|
||||
|
||||
|
||||
def make_front() -> trimesh.Trimesh:
|
||||
skirt_h = DISP_H + PCB_T_HALF
|
||||
z1 = BEZEL_T + skirt_h
|
||||
"""Front bezel: screen window + skirt + male lip for closing onto back."""
|
||||
z1 = BEZEL_T + SKIRT_H + LIP_H
|
||||
|
||||
body = aabb(0, 0, 0, OUTER_L, OUTER_W, z1)
|
||||
cavity = aabb(WALL, WALL, BEZEL_T, OUTER_L - WALL, OUTER_W - WALL, z1 + 1)
|
||||
body = aabb(0, 0, 0, OUTER_L, OUTER_W, BEZEL_T + SKIRT_H)
|
||||
# hollow skirt
|
||||
cavity = aabb(WALL, WALL, BEZEL_T, OUTER_L - WALL, OUTER_W - WALL, BEZEL_T + SKIRT_H + 1)
|
||||
|
||||
vx = WALL + GAP + DISP_X + VIEW_IX
|
||||
vy = WALL + GAP + VIEW_IY
|
||||
view = aabb(vx - 0.3, vy - 0.3, -0.5, vx + VIEW_L + 0.3, vy + VIEW_W + 0.3, BEZEL_T + 0.5)
|
||||
view = aabb(vx - 0.25, vy - 0.25, -0.5, vx + VIEW_L + 0.25, vy + VIEW_W + 0.25, BEZEL_T + 0.6)
|
||||
|
||||
# screen pocket
|
||||
sx = WALL + GAP + DISP_X - 0.3
|
||||
pocket = aabb(sx, WALL + GAP - 0.2, BEZEL_T - 0.05, sx + DISP_L + 0.6, WALL + GAP + DISP_W + 0.2, z1 + 0.5)
|
||||
sx = WALL + GAP + DISP_X - 0.25
|
||||
pocket = aabb(
|
||||
sx, WALL + GAP - 0.15, BEZEL_T - 0.05,
|
||||
sx + DISP_L + 0.5, WALL + GAP + DISP_W + 0.15, BEZEL_T + DISP_H + 0.8,
|
||||
)
|
||||
|
||||
out = d(body, cavity, view, pocket)
|
||||
|
||||
# male lip (inserts into back female groove)
|
||||
lip_outer = aabb(
|
||||
WALL - LIP_T, WALL - LIP_T, BEZEL_T + SKIRT_H - 0.05,
|
||||
OUTER_L - (WALL - LIP_T), OUTER_W - (WALL - LIP_T), BEZEL_T + SKIRT_H + LIP_H,
|
||||
)
|
||||
lip_inner = aabb(
|
||||
WALL + 0.15, WALL + 0.15, BEZEL_T + SKIRT_H - 0.1,
|
||||
OUTER_L - WALL - 0.15, OUTER_W - WALL - 0.15, BEZEL_T + SKIRT_H + LIP_H + 0.5,
|
||||
)
|
||||
lip = d(lip_outer, lip_inner)
|
||||
out = u(out, lip)
|
||||
|
||||
# screw clearance through bezel
|
||||
for px, py in MH:
|
||||
x = WALL + GAP + px
|
||||
y = WALL + GAP + py
|
||||
hole = cylinder(radius=SCREW / 2, height=z1 + 2, sections=32)
|
||||
hole.apply_translation([x, y, z1 / 2])
|
||||
hole = cylinder(radius=SCREW / 2 + 0.1, height=z1 + 4, sections=28)
|
||||
hole.apply_translation([x, y, (BEZEL_T + SKIRT_H) / 2])
|
||||
out = d(out, hole)
|
||||
|
||||
out.merge_vertices()
|
||||
return out
|
||||
|
||||
|
||||
def make_back() -> trimesh.Trimesh:
|
||||
body = aabb(0, 0, 0, OUTER_L, OUTER_W, SHELL_H)
|
||||
cavity = aabb(WALL, WALL, WALL, OUTER_L - WALL, OUTER_W - WALL, SHELL_H + 1)
|
||||
def dht_pocket_on_back() -> trimesh.Trimesh:
|
||||
"""
|
||||
Pocket on outer back face (z=0 side is exterior bottom of shell).
|
||||
Module inserts from outside; clips hold it; grill protrudes DHT_PROUD.
|
||||
Coordinate: back shell sits z=0 exterior, z=SHELL_H toward PCB.
|
||||
Pocket carved into floor from outside, clips on the outer face.
|
||||
"""
|
||||
# center DHT on back wall area away from USB (right-ish of board back)
|
||||
cx = WALL + GAP + 62
|
||||
cy = WALL + GAP + PCB_W / 2
|
||||
|
||||
# USB on -X
|
||||
usb_y0 = WALL + GAP + USB_Y - USB_W / 2
|
||||
usb = aabb(-1, usb_y0, WALL + 1.5, WALL + 1, usb_y0 + USB_W, WALL + 1.5 + USB_H)
|
||||
# outer boss that contains the module (sits mostly outside z<=0)
|
||||
bay_l = DHT_MOD_L + 3.2
|
||||
bay_w = DHT_MOD_W + 3.2
|
||||
bay_h = DHT_MOD_H + WALL * 0.6 + DHT_PROUD
|
||||
|
||||
# SD slot on +Y
|
||||
sd = aabb(WALL + GAP + 28, OUTER_W - WALL - 1, WALL + 2, WALL + GAP + 44, OUTER_W + 1, WALL + 5.2)
|
||||
# bay extends below z=0 (outside) and a bit into floor
|
||||
bay = aabb(
|
||||
cx - bay_l / 2, cy - bay_w / 2, -bay_h + WALL * 0.35,
|
||||
cx + bay_l / 2, cy + bay_w / 2, WALL + 0.2,
|
||||
)
|
||||
|
||||
# button holes on floor
|
||||
btn1 = cylinder(radius=2.3, height=WALL + 2, sections=24)
|
||||
btn1.apply_translation([WALL + GAP + 10, WALL + GAP + 12, WALL / 2])
|
||||
btn2 = cylinder(radius=2.3, height=WALL + 2, sections=24)
|
||||
btn2.apply_translation([WALL + GAP + 10, WALL + GAP + 22, WALL / 2])
|
||||
# cavity for module (from outside)
|
||||
cav = aabb(
|
||||
cx - DHT_MOD_L / 2 - 0.25, cy - DHT_MOD_W / 2 - 0.25, -bay_h + WALL * 0.35 + 1.0,
|
||||
cx + DHT_MOD_L / 2 + 0.25, cy + DHT_MOD_W / 2 + 0.25, WALL - 0.3,
|
||||
)
|
||||
|
||||
# vent slots on floor
|
||||
# window through back wall into interior for wires (small)
|
||||
wire = aabb(
|
||||
cx - 3.5, cy - 4.0, WALL - 0.5,
|
||||
cx + 3.5, cy + 4.0, WALL + 2.5,
|
||||
)
|
||||
|
||||
# front face of bay: grill opening, leaving rim so tip is proud
|
||||
grill = aabb(
|
||||
cx - DHT_MOD_L / 2 + 1.2, cy - DHT_MOD_W / 2 + 1.2, -bay_h - 0.2,
|
||||
cx + DHT_MOD_L / 2 - 1.2, cy + DHT_MOD_W / 2 - 1.2, -bay_h + WALL + DHT_PROUD + 0.5,
|
||||
)
|
||||
|
||||
# side vent slots
|
||||
vents = []
|
||||
for i in range(5):
|
||||
v = aabb(WALL + 12 + i * 12, WALL + 8, -0.5, WALL + 14 + i * 12, WALL + 18, WALL + 0.5)
|
||||
vents.append(v)
|
||||
for i in range(3):
|
||||
vents.append(aabb(
|
||||
cx - DHT_MOD_L / 2 - 2, cy - 4 + i * 3.2, -bay_h + 3,
|
||||
cx - DHT_MOD_L / 2 + 0.2, cy - 4 + i * 3.2 + 1.4, -bay_h + 8,
|
||||
))
|
||||
vents.append(aabb(
|
||||
cx + DHT_MOD_L / 2 - 0.2, cy - 4 + i * 3.2, -bay_h + 3,
|
||||
cx + DHT_MOD_L / 2 + 2, cy - 4 + i * 3.2 + 1.4, -bay_h + 8,
|
||||
))
|
||||
|
||||
out = d(body, cavity, usb, sd, btn1, btn2, *vents)
|
||||
# 卡位: L/R snap ledges + end stops (press-fit module from outside)
|
||||
z_base = -bay_h + WALL * 0.35 + 1.2
|
||||
left_clip = aabb(
|
||||
cx - DHT_MOD_L / 2 - 1.2, cy - 3.5, z_base,
|
||||
cx - DHT_MOD_L / 2 + DHT_CLIP, cy + 3.5, z_base + 3.2,
|
||||
)
|
||||
right_clip = aabb(
|
||||
cx + DHT_MOD_L / 2 - DHT_CLIP, cy - 3.5, z_base,
|
||||
cx + DHT_MOD_L / 2 + 1.2, cy + 3.5, z_base + 3.2,
|
||||
)
|
||||
# end stop ledge near grill (prevents module falling out front)
|
||||
stop = aabb(
|
||||
cx - DHT_MOD_L / 2 + 1.5, cy - DHT_MOD_W / 2 + 0.8, -bay_h + WALL * 0.35 + 0.8,
|
||||
cx + DHT_MOD_L / 2 - 1.5, cy + DHT_MOD_W / 2 - 0.8, -bay_h + WALL * 0.35 + 0.8 + 1.0,
|
||||
)
|
||||
# rear retention bar (卡入后沿)
|
||||
retain = aabb(
|
||||
cx - 5.0, cy - DHT_MOD_W / 2 - 0.2, WALL - 2.2,
|
||||
cx + 5.0, cy + DHT_MOD_W / 2 + 0.2, WALL - 0.4,
|
||||
)
|
||||
|
||||
bay = d(bay, cav, grill, wire, *vents)
|
||||
bay = u(bay, left_clip, right_clip, stop, retain)
|
||||
return bay
|
||||
|
||||
|
||||
def make_back() -> trimesh.Trimesh:
|
||||
"""Back shell: cavity, female lip, screw bosses, stand, DHT snap bay on outer back."""
|
||||
body = aabb(0, 0, 0, OUTER_L, OUTER_W, SHELL_H + LIP_H)
|
||||
cavity = aabb(WALL, WALL, WALL, OUTER_L - WALL, OUTER_W - WALL, SHELL_H + LIP_H + 1)
|
||||
|
||||
# female groove for front lip (near top rim)
|
||||
groove = aabb(
|
||||
WALL - LIP_T - 0.15, WALL - LIP_T - 0.15, SHELL_H,
|
||||
OUTER_L - (WALL - LIP_T - 0.15), OUTER_W - (WALL - LIP_T - 0.15), SHELL_H + LIP_H + 0.2,
|
||||
)
|
||||
# keep inner wall so groove is a channel, not open to cavity fully
|
||||
# actually: cut a U-channel from the top rim
|
||||
groove_cut = aabb(
|
||||
WALL - LIP_T - 0.1, WALL - LIP_T - 0.1, SHELL_H - 0.05,
|
||||
OUTER_L - (WALL - LIP_T - 0.1), OUTER_W - (WALL - LIP_T - 0.1), SHELL_H + LIP_H + 0.3,
|
||||
)
|
||||
keep_inner = aabb(
|
||||
WALL + 0.05, WALL + 0.05, SHELL_H - 0.2,
|
||||
OUTER_L - WALL - 0.05, OUTER_W - WALL - 0.05, SHELL_H + LIP_H + 0.5,
|
||||
)
|
||||
# rim solid then groove: start from body, remove cavity, then remove groove channel between lip walls
|
||||
out = d(body, cavity)
|
||||
|
||||
# Add rim collar then cut female slot
|
||||
collar = aabb(0, 0, SHELL_H - 0.05, OUTER_L, OUTER_W, SHELL_H + LIP_H)
|
||||
collar_in = aabb(WALL + 0.2, WALL + 0.2, SHELL_H - 0.2, OUTER_L - WALL - 0.2, OUTER_W - WALL - 0.2, SHELL_H + LIP_H + 0.5)
|
||||
collar = d(collar, collar_in)
|
||||
out = u(out, collar)
|
||||
|
||||
# female slot (accepts front male lip)
|
||||
slot = aabb(
|
||||
WALL - LIP_T - 0.12, WALL - LIP_T - 0.12, SHELL_H + 0.15,
|
||||
OUTER_L - (WALL - LIP_T - 0.12), OUTER_W - (WALL - LIP_T - 0.12), SHELL_H + LIP_H - 0.15,
|
||||
)
|
||||
slot_keep = aabb(
|
||||
WALL + 0.05, WALL + 0.05, SHELL_H,
|
||||
OUTER_L - WALL - 0.05, OUTER_W - WALL - 0.05, SHELL_H + LIP_H + 0.2,
|
||||
)
|
||||
# slot is the ring between slot outer and slot_keep — cut only the ring
|
||||
slot_ring = d(slot, slot_keep)
|
||||
out = d(out, slot_ring)
|
||||
|
||||
# USB
|
||||
usb_y0 = WALL + GAP + USB_Y - USB_W / 2
|
||||
usb = aabb(-1, usb_y0, WALL + 1.2, WALL + 1.2, usb_y0 + USB_W, WALL + 1.2 + USB_H)
|
||||
out = d(out, usb)
|
||||
|
||||
# SD optional slit
|
||||
sd = aabb(WALL + GAP + 28, OUTER_W - WALL - 1, WALL + 2, WALL + GAP + 44, OUTER_W + 1, WALL + 5)
|
||||
out = d(out, sd)
|
||||
|
||||
# BOOT/RST access
|
||||
for y in (12, 22):
|
||||
btn = cylinder(radius=2.2, height=WALL + 2, sections=20)
|
||||
btn.apply_translation([WALL + GAP + 10, WALL + GAP + y, WALL / 2])
|
||||
out = d(out, btn)
|
||||
|
||||
# floor vents (not under DHT)
|
||||
vents = []
|
||||
for i in range(4):
|
||||
vents.append(aabb(WALL + 10 + i * 12, WALL + 8, -0.5, WALL + 12 + i * 12, WALL + 16, WALL + 0.5))
|
||||
out = d(out, *vents)
|
||||
|
||||
# screw bosses
|
||||
bosses = []
|
||||
for px, py in MH:
|
||||
x = WALL + GAP + px
|
||||
y = WALL + GAP + py
|
||||
boss = cylinder(radius=BOSS_OD / 2, height=CAVITY - 0.3, sections=32)
|
||||
boss.apply_translation([x, y, WALL + (CAVITY - 0.3) / 2])
|
||||
hole = cylinder(radius=BOSS_ID / 2, height=CAVITY + 1, sections=24)
|
||||
boss = cylinder(radius=BOSS_OD / 2, height=CAVITY - 0.4, sections=28)
|
||||
boss.apply_translation([x, y, WALL + (CAVITY - 0.4) / 2])
|
||||
hole = cylinder(radius=BOSS_ID / 2, height=CAVITY + 1, sections=20)
|
||||
hole.apply_translation([x, y, WALL + CAVITY / 2])
|
||||
bosses.append(d(boss, hole))
|
||||
out = u(out, *bosses)
|
||||
|
||||
# stand foot
|
||||
stand_len = OUTER_L * 0.72
|
||||
# stand foot (desk)
|
||||
stand_len = OUTER_L * 0.7
|
||||
sx = (OUTER_L - stand_len) / 2
|
||||
foot = aabb(sx, -STAND_FOOT + 0.5, 0, sx + stand_len, WALL, SHELL_H)
|
||||
# hollow foot
|
||||
foot_in = aabb(sx + 2, -STAND_FOOT + 2.5, WALL, sx + stand_len - 2, WALL - 0.2, SHELL_H + 1)
|
||||
foot = aabb(sx, -STAND_FOOT + 0.4, 0, sx + stand_len, WALL, SHELL_H * 0.85)
|
||||
foot_in = aabb(sx + 2, -STAND_FOOT + 2.2, WALL, sx + stand_len - 2, WALL - 0.15, SHELL_H)
|
||||
foot = d(foot, foot_in)
|
||||
rib = aabb(OUTER_L * 0.5 - 1.2, -STAND_FOOT + 1, WALL, OUTER_L * 0.5 + 1.2, WALL, SHELL_H - 1)
|
||||
rib = aabb(OUTER_L / 2 - 1.2, -STAND_FOOT + 1, WALL, OUTER_L / 2 + 1.2, WALL, SHELL_H * 0.8)
|
||||
out = u(out, foot, rib)
|
||||
|
||||
# DHT bay
|
||||
dx = WALL + GAP + 48
|
||||
dy = OUTER_W - 0.2
|
||||
bay = aabb(dx, dy, WALL, dx + DHT_L, dy + DHT_W + WALL, WALL + DHT_H)
|
||||
bay_in = aabb(dx + 1.2, dy + 1.2, WALL + 1.0, dx + DHT_L - 1.2, dy + DHT_W, WALL + DHT_H + 1)
|
||||
# top vents
|
||||
top_vents = []
|
||||
for i in range(5):
|
||||
tv = aabb(
|
||||
dx + 2.5 + i * 3.6,
|
||||
dy + 3,
|
||||
WALL + DHT_H - WALL - 0.2,
|
||||
dx + 2.5 + i * 3.6 + 1.5,
|
||||
dy + DHT_W - 2,
|
||||
WALL + DHT_H + 0.5,
|
||||
)
|
||||
top_vents.append(tv)
|
||||
# side vents
|
||||
for i in range(3):
|
||||
sv = aabb(
|
||||
dx + 4 + i * 5.5,
|
||||
dy + DHT_W + WALL - 0.5,
|
||||
WALL + 3,
|
||||
dx + 4 + i * 5.5 + 1.5,
|
||||
dy + DHT_W + WALL + 1,
|
||||
WALL + 9,
|
||||
)
|
||||
top_vents.append(sv)
|
||||
bay = d(bay, bay_in, *top_vents)
|
||||
|
||||
# cable channel from bay to CN1
|
||||
channel = aabb(dx + 2, OUTER_W - WALL - 2, WALL + 2, dx + 10, OUTER_W + DHT_W, WALL + 6)
|
||||
out = d(u(out, bay), channel)
|
||||
# DHT11 snap bay on outer back (protruding)
|
||||
out = u(out, dht_pocket_on_back())
|
||||
|
||||
out.merge_vertices()
|
||||
return out
|
||||
@@ -181,19 +290,18 @@ def main() -> None:
|
||||
out_dir = Path(__file__).resolve().parent / "export"
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print("Building front…")
|
||||
print("Building front (合盖公止口)…")
|
||||
front = make_front()
|
||||
print("Building back…")
|
||||
print("Building back (DHT后盖卡位 + 母止口)…")
|
||||
back = make_back()
|
||||
|
||||
front.export(out_dir / "front.stl")
|
||||
back.export(out_dir / "back.stl")
|
||||
|
||||
print(f"front: {front.faces.shape[0]} faces, watertight={front.is_watertight}")
|
||||
print(f"back: {back.faces.shape[0]} faces, watertight={back.is_watertight}")
|
||||
print(f"Outer: {OUTER_L:.1f} x {OUTER_W:.1f} x front/back shells")
|
||||
print(f"front watertight={front.is_watertight} faces={len(front.faces)}")
|
||||
print(f"back watertight={back.is_watertight} faces={len(back.faces)}")
|
||||
print(f"Wrote {out_dir / 'front.stl'}")
|
||||
print(f"Wrote {out_dir / 'back.stl'}")
|
||||
print(f"Outer ~ {OUTER_L:.1f} x {OUTER_W:.1f} mm; DHT proud ~{DHT_PROUD} mm")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user