Cosplay/nhf/build.py

82 lines
2.3 KiB
Python

"""
The NHF build system
Usage: For any parametric assembly, inherit the `Model` class, and mark the
output objects with the `@target` decorator
"""
from pathlib import Path
from typing import Union
import cadquery as Cq
from colorama import Fore, Style
class Target:
def __init__(self,
method,
name: str):
self._method = method
self.name = name
def __call__(self, obj, *args, **kwargs):
return self._method(obj, *args, **kwargs)
@classmethod
def methods(cls, subject):
"""
List of all methods of a class or objects annotated with this decorator.
"""
def g():
for name in dir(subject):
method = getattr(subject, name)
if isinstance(method, Target):
yield name, method
return {name: method for name, method in g()}
def target(name, **kwargs):
"""
Decorator for annotating a build output
"""
def f(method):
return Target(method, name=name, **kwargs)
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:
"""
Base class for a parametric assembly
"""
def target_names(self) -> list[str]:
"""
List of decorated target functions
"""
return list(Target.methods(self).keys())
def build_all(self, output_dir: Union[Path, str] ="build", verbose=1):
"""
Build all targets in this model
"""
output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True, parents=True)
for k, f in Target.methods(self).items():
output_file = output_dir / f"{k}.stl"
if output_file.is_file():
if verbose >= 1:
print(f"{Fore.GREEN}Skipping{Style.RESET_ALL} {output_file}")
continue
model = f(self)
if verbose >= 1:
print(f"{Fore.BLUE}Building{Style.RESET_ALL} {output_file}", end="")
_to_shape(model).exportStl(str(output_file))
if verbose >= 1:
print("\r", end="")
print(f"{Fore.GREEN}Built{Style.RESET_ALL} {output_file}")