Merge pull request 'tool: Light panel' (#9) from tool/lighting into main

Reviewed-on: #9
This commit is contained in:
Leni Aniva 2025-01-28 17:56:20 -08:00
commit 9ff3e72474
3 changed files with 147 additions and 1 deletions

View File

@ -90,7 +90,7 @@ class Target:
x = (
Cq.Workplane()
.add(x._faces)
.add(x._wires)
.add(x.wires)
.add(x._edges)
)
assert isinstance(x, Cq.Workplane)

0
nhf/tool/__init__.py Normal file
View File

146
nhf/tool/light_panel.py Normal file
View File

@ -0,0 +1,146 @@
from dataclasses import dataclass
import cadquery as Cq
from nhf import Material, Role
from nhf.build import Model, target, assembly, TargetKind, submodel
from nhf.parts.box import MountingBox, Hole
from nhf.parts.electronics import ArduinoUnoR3
import nhf.utils
@dataclass
class LightPanel(Model):
# Dimensions of the base panel
length: float = 300.0
width: float = 200.0
attach_height: float = 20.0
attach_diam: float = 8.0
attach_depth: float = 12.7
grid_height: float = 20.0
grid_top_height: float = 5.0
# Distance from grid to edge
grid_margin: float = 20.0
# Number of holes in each row of the grid
grid_holes: int = 9
grid_layers: int = 6
grid_hole_width: float = 15.0
base_thickness: float = 25.4/16
grid_thickness: float = 25.4/4
base_material: Material = Material.WOOD_BIRCH
grid_material: Material = Material.ACRYLIC_TRANSPARENT
controller: ArduinoUnoR3 = ArduinoUnoR3()
def __post_init__(self):
assert self.grid_holes >= 2
super().__init__(name="light-panel")
@property
def grid_spacing_y(self) -> float:
return (self.width - 2 * self.grid_margin - self.grid_thickness) / (self.grid_layers - 1)
@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)
# The spacing is such that the first and last holes are a distance `margin`
# 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')
)
def grid(self) -> Cq.Workplane:
return (
Cq.Workplane('XY')
.placeSketch(self.grid_profile())
.extrude(self.grid_thickness)
)
@submodel(name="base")
def base(self) -> MountingBox:
xshift = self.length / 2 - self.controller.length - self.grid_margin / 2
yshift = self.grid_margin / 2
holes = [
Hole(
x=x + xshift, y=y + yshift,
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,
)
@target(name="attachment")
def attachment(self) -> Cq.Workplane:
l = self.length / 2
w = self.width / 2
return (
Cq.Workplane('XY')
.box(
l, w, self.attach_height,
centered=(True, True, False),
)
.faces(">Z")
.hole(self.attach_diam, self.attach_depth)
)
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
# margin + t + (n-1) * spacing + margin = width
spacing = self.grid_spacing_y
shift = self.grid_margin + self.grid_thickness / 2
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
if __name__ == '__main__':
import sys
p = LightPanel()
print(p.grid_spacing_y)
if len(sys.argv) == 1:
p.build_all()
sys.exit(0)