cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
2 changed files with 54 additions and 21 deletions
Showing only changes of commit 66fc02ef44 - Show all commits

View File

@ -2,25 +2,69 @@
The NHF build system The NHF build system
Usage: For any parametric assembly, inherit the `Model` class, and mark the Usage: For any parametric assembly, inherit the `Model` class, and mark the
output objects with the `@target` decorator output objects with the `@target` decorator. Each marked function should only
take `self` as an argument.
```python
class BuildScaffold(Model):
@target(name="obj1")
def o1(self):
return Cq.Solid.makeBox(10, 10, 10)
def o2(self):
return Cq.Solid.makeCylinder(10, 20)
```
""" """
from enum import Enum
from pathlib import Path from pathlib import Path
from typing import Union from typing import Union
from functools import wraps from functools import wraps
from colorama import Fore, Style from colorama import Fore, Style
import cadquery as Cq import cadquery as Cq
class TargetKind(Enum):
STL = "stl",
DXF = "dxf",
def __init__(self, ext: str):
self.ext = ext
class Target: class Target:
def __init__(self, def __init__(self,
method, method,
name: str): name: str,
kind: TargetKind = TargetKind.STL,
**kwargs):
self._method = method self._method = method
self.name = name self.name = name
self.kind = kind
self.kwargs = kwargs
def __str__(self): def __str__(self):
return f"<target {self.name}>" return f"<target {self.name}>"
def __call__(self, obj, *args, **kwargs): def __call__(self, obj, *args, **kwargs):
"""
Raw call function which passes arguments directly to `_method`
"""
return self._method(obj, *args, **kwargs) return self._method(obj, *args, **kwargs)
def file_name(self, file_name):
return f"{file_name}.{self.kind.ext}"
def write_to(self, obj, path: str):
x = self._method(obj)
if self.kind == TargetKind.STL:
assert isinstance(x, Union[
Cq.Workplane, Cq.Shape, Cq.Compound, Cq.Assembly])
if isinstance(x, Cq.Workplane):
x = x.val()
if isinstance(x, Cq.Assembly):
x = x.toCompound()
x.exportStl(path, **self.kwargs)
elif self.kind == TargetKind.DXF:
assert isinstance(x, Cq.Workplane)
Cq.exporters.exportDXF(x, path, **self.kwargs)
else:
assert False, f"Invalid kind: {self.kind}"
@classmethod @classmethod
def methods(cls, subject): def methods(cls, subject):
@ -49,13 +93,6 @@ def target(name, **deco_kwargs):
return wrapper return wrapper
return f return f
def _to_shape(x: Union[Cq.Workplane, Cq.Shape, Cq.Compound, Cq.Assembly]) -> Cq.Shape:
if isinstance(x, Cq.Workplane):
x = x.val()
if isinstance(x, Cq.Assembly):
x = x.toCompound()
return x
class Model: class Model:
""" """
@ -76,22 +113,21 @@ class Model:
total += 1 total += 1
return total return total
def build_all(self, output_dir: Union[Path, str] ="build", verbose=1): def build_all(self, output_dir: Union[Path, str] = "build", verbose=1):
""" """
Build all targets in this model Build all targets in this model
""" """
output_dir = Path(output_dir) output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True, parents=True) output_dir.mkdir(exist_ok=True, parents=True)
for k, f in Target.methods(self).items(): for k, target in Target.methods(self).items():
output_file = output_dir / f"{k}.stl" output_file = output_dir / target.file_name(k)
if output_file.is_file(): if output_file.is_file():
if verbose >= 1: if verbose >= 1:
print(f"{Fore.GREEN}Skipping{Style.RESET_ALL} {output_file}") print(f"{Fore.GREEN}Skipping{Style.RESET_ALL} {output_file}")
continue continue
model = f(self)
if verbose >= 1: if verbose >= 1:
print(f"{Fore.BLUE}Building{Style.RESET_ALL} {output_file}") print(f"{Fore.BLUE}Building{Style.RESET_ALL} {output_file}")
_to_shape(model).exportStl(str(output_file)) target.write_to(self, str(output_file))
if verbose >= 1: if verbose >= 1:
print(f"{Fore.GREEN}Built{Style.RESET_ALL} {output_file}") print(f"{Fore.GREEN}Built{Style.RESET_ALL} {output_file}")

View File

@ -33,7 +33,7 @@ from dataclasses import dataclass, field
import unittest import unittest
import cadquery as Cq import cadquery as Cq
from nhf import Material from nhf import Material
from nhf.build import Model, target from nhf.build import Model, TargetKind, target
from nhf.parts.joints import HirthJoint from nhf.parts.joints import HirthJoint
from nhf.parts.handle import Handle from nhf.parts.handle import Handle
import nhf.touhou.houjuu_nue.wing as MW import nhf.touhou.houjuu_nue.wing as MW
@ -113,11 +113,6 @@ class Parameters(Model):
"Wing root must be large enough to accomodate joint" "Wing root must be large enough to accomodate joint"
def print_geometries(self):
return [
self.hs_joint_parent()
]
def harness_profile(self) -> Cq.Sketch: def harness_profile(self) -> Cq.Sketch:
""" """
Creates the harness shape Creates the harness shape
@ -148,6 +143,7 @@ class Parameters(Model):
) )
return sketch return sketch
@target(name="harness", kind=TargetKind.DXF)
def harness(self) -> Cq.Shape: def harness(self) -> Cq.Shape:
""" """
Creates the harness shape Creates the harness shape
@ -249,7 +245,8 @@ class Parameters(Model):
result.faces("<Z").tag("base") result.faces("<Z").tag("base")
return result return result
def wing_root(self) -> Cq.Shape: @target(name="wing_root")
def wing_root(self) -> Cq.Assembly:
""" """
Generate the wing root which contains a Hirth joint at its base and a Generate the wing root which contains a Hirth joint at its base and a
rectangular opening on its side, with the necessary interfaces. rectangular opening on its side, with the necessary interfaces.