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 grid_height: float = 30.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="crown") @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, ) 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.width - 2 * self.grid_margin - self.grid_thickness) / (self.grid_layers - 1) 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() if len(sys.argv) == 1: p.build_all() sys.exit(0)