Cosplay/nhf/tool/light_panel.py

122 lines
3.7 KiB
Python
Raw Normal View History

2024-12-06 16:40:07 -08:00
from dataclasses import dataclass
import cadquery as Cq
from nhf import Material, Role
2024-12-07 12:09:41 -08:00
from nhf.build import Model, target, assembly, TargetKind, submodel
from nhf.parts.box import MountingBox, Hole
from nhf.parts.electronics import ArduinoUnoR3
2024-12-06 16:40:07 -08:00
import nhf.utils
@dataclass
class LightPanel(Model):
# Dimensions of the base panel
length: float = 300.0
width: float = 200.0
grid_height: float = 30.0
2024-12-07 13:44:35 -08:00
grid_top_height: float = 5.0
2024-12-06 16:40:07 -08:00
# Distance from grid to edge
grid_margin: float = 20.0
# Number of holes in each row of the grid
2024-12-07 12:09:41 -08:00
grid_holes: int = 9
grid_layers: int = 6
grid_hole_width: float = 15.0
2024-12-06 16:40:07 -08:00
base_thickness: float = 25.4/16
2024-12-07 13:44:35 -08:00
grid_thickness: float = 25.4/4
2024-12-07 12:09:41 -08:00
base_material: Material = Material.WOOD_BIRCH
grid_material: Material = Material.ACRYLIC_TRANSPARENT
controller: ArduinoUnoR3 = ArduinoUnoR3()
2024-12-06 16:40:07 -08:00
def __post_init__(self):
assert self.grid_holes >= 2
2024-12-07 13:47:32 -08:00
super().__init__(name="light-panel")
2024-12-06 16:40:07 -08:00
@target(name="grid", kind=TargetKind.DXF)
def grid_profile(self):
w = self.length - self.grid_margin * 2
h = self.grid_height + self.grid_top_height
# The width of one hole (w0) satisfies
# n * w0 + (n+1) t = w
# where t is the thickness of the edge
n = self.grid_holes
w0 = self.grid_hole_width
t = (w - n * w0) / (n + 1)
2024-12-07 12:09:41 -08:00
# The spacing is such that the first and last holes are a distance `margin`
2024-12-06 16:40:07 -08:00
# away from the edges, so it satisfies
# t + w0/2 + (n-1) * s + w0/2 + t = w
step = (w - t*2 - w0) / (n - 1)
return (
Cq.Sketch()
.push([(0, h/2)])
.rect(w, h)
.push([
(i * step + t + w0/2 - w/2, self.grid_height/2)
for i in range(0, n)
])
.rect(w0, self.grid_height, mode='s')
)
2024-12-06 16:43:12 -08:00
2024-12-07 12:09:41 -08:00
def grid(self) -> Cq.Workplane:
return (
Cq.Workplane('XY')
.placeSketch(self.grid_profile())
.extrude(self.grid_thickness)
)
@submodel(name="base")
def base(self) -> MountingBox:
2024-12-07 13:44:35 -08:00
xshift = self.length / 2 - self.controller.length - self.grid_margin / 2
yshift = self.grid_margin / 2
2024-12-07 12:09:41 -08:00
holes = [
Hole(
2024-12-07 13:44:35 -08:00
x=x + xshift, y=y + yshift,
2024-12-07 12:09:41 -08:00
diam=self.controller.hole_diam,
tag=f"controller_conn{i}",
)
for i, (x, y) in enumerate(self.controller.holes)
]
return MountingBox(
holes=holes,
hole_diam=self.controller.hole_diam,
length=self.length,
width=self.width,
centred=(True, False),
thickness=self.base_thickness,
)
def assembly(self) -> Cq.Assembly:
assembly = (
Cq.Assembly()
.addS(
self.base().generate(),
name="base",
role=Role.STRUCTURE,
material=self.base_material,
)
)
# Grid thickness t is fixed, so the spacing of the grid satisfies
2024-12-07 13:44:35 -08:00
# margin + t + (n-1) * spacing + margin = width
spacing = (self.width - 2 * self.grid_margin - self.grid_thickness) / (self.grid_layers - 1)
shift = self.grid_margin + self.grid_thickness / 2
2024-12-07 12:09:41 -08:00
for i in range(self.grid_layers):
assembly = assembly.addS(
self.grid(),
name=f"grid_{i}",
role=Role.STRUCTURE,
material=self.grid_material,
loc=Cq.Location(0, spacing * i + shift, self.base_thickness, 90, 0, 0),
)
return assembly
2024-12-06 16:43:12 -08:00
if __name__ == '__main__':
import sys
p = LightPanel()
if len(sys.argv) == 1:
p.build_all()
sys.exit(0)