diff --git a/nhf/build.py b/nhf/build.py index 106577c..3e6ece3 100644 --- a/nhf/build.py +++ b/nhf/build.py @@ -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) @@ -214,7 +214,7 @@ class Submodel: def write_to(self, obj, path: str): x = self._method(obj) assert isinstance(x, Model), f"Unexpected type: {type(x)}" - x.build_all(path) + x.build_all(path, prefix=False) @classmethod def methods(cls, subject): @@ -271,11 +271,17 @@ class Model: total += 1 return total - def build_all(self, output_dir: Union[Path, str] = "build", verbose=1): + def build_all( + self, + output_dir: Union[Path, str] = "build", + prefix: bool = True, + verbose=1): """ Build all targets in this model and write the results to file """ output_dir = Path(output_dir) + if prefix: + output_dir = output_dir / self.name targets = Target.methods(self) for t in targets.values(): file_name = t.file_name diff --git a/nhf/tool/__init__.py b/nhf/tool/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nhf/tool/light_panel.py b/nhf/tool/light_panel.py new file mode 100644 index 0000000..44c7920 --- /dev/null +++ b/nhf/tool/light_panel.py @@ -0,0 +1,121 @@ +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="light-panel") + + @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)