diff --git a/nhf/materials.py b/nhf/materials.py index 714995c..c4c4e49 100644 --- a/nhf/materials.py +++ b/nhf/materials.py @@ -14,10 +14,15 @@ class Role(Flag): Describes the role of a part """ + # Externally supplied object + FIXTURE = auto() + # Parent and child sides of joints PARENT = auto() CHILD = auto() CASING = auto() + # Springs, cushions DAMPING = auto() + # Main structural support STRUCTURE = auto() DECORATION = auto() ELECTRONIC = auto() @@ -38,8 +43,10 @@ class Role(Flag): head = next(iter(self)) return ROLE_COLOR_MAP[head] + # Maps roles to their colours ROLE_COLOR_MAP = { + Role.FIXTURE: _color('black', 0.2), Role.PARENT: _color('blue4', 0.6), Role.CASING: _color('dodgerblue3', 0.6), Role.CHILD: _color('darkorange2', 0.6), diff --git a/nhf/touhou/houjuu_nue/harness.py b/nhf/touhou/houjuu_nue/harness.py index 3a5ea90..4a036fd 100644 --- a/nhf/touhou/houjuu_nue/harness.py +++ b/nhf/touhou/houjuu_nue/harness.py @@ -5,7 +5,46 @@ from nhf import Material, Role from nhf.build import Model, TargetKind, target, assembly import nhf.utils -@dataclass +@dataclass(frozen=True, kw_only=True) +class Mannequin: + """ + A mannequin for calibration + """ + + shoulder_width: float = 400 + shoulder_to_waist: float = 440 + waist_width: float = 250 + head_height: float = 220.0 + neck_height: float = 105.0 + neck_diam: float = 140 + head_diam: float = 210 + torso_thickness: float = 150 + + def generate(self) -> Cq.Workplane: + head_neck = ( + Cq.Workplane("XY") + .cylinder( + radius=self.neck_diam/2, + height=self.neck_height, + centered=(True, True, False)) + .faces(">Z") + .workplane() + .cylinder( + radius=self.head_diam/2, + height=self.head_height, + combine=True, centered=(True, True, False)) + ) + result = ( + Cq.Workplane("XY") + .rect(self.waist_width, self.torso_thickness) + .workplane(offset=self.shoulder_to_waist) + .rect(self.shoulder_width, self.torso_thickness) + .loft(combine=True) + .union(head_neck.translate((0, 0, self.shoulder_to_waist))) + ) + return result.translate((0, self.torso_thickness / 2, 0)) + +@dataclass(kw_only=True) class Harness(Model): thickness: float = 25.4 / 8 width: float = 200.0 @@ -42,6 +81,8 @@ class Harness(Model): hs_joint_axis_cbore_diam: float = 20 hs_joint_axis_cbore_depth: float = 3 + mannequin: Mannequin = Mannequin() + def __post_init__(self): super().__init__(name="harness") @@ -164,22 +205,27 @@ class Harness(Model): @assembly() def assembly(self) -> Cq.Assembly: harness = self.surface() + mannequin_z = self.mannequin.shoulder_to_waist * 0.6 result = ( Cq.Assembly() - .addS(harness, name="base", - material=Material.WOOD_BIRCH, - role=Role.STRUCTURE) + .addS( + harness, name="base", + material=Material.WOOD_BIRCH, + role=Role.STRUCTURE) .constrain("base", "Fixed") + .addS( + self.mannequin.generate(), + name="mannequin", + role=Role.FIXTURE, + loc=Cq.Location((0, -self.thickness, -mannequin_z), (0, 0, 1), 180)) + .constrain("mannequin", "Fixed") ) for name in ["l1", "l2", "l3", "r1", "r2", "r3"]: j = self.hs_joint_parent() - ( - result - .addS(j, name=name, - role=Role.PARENT, - material=Material.PLASTIC_PLA) - #.constrain("base?mount", f"{name}?base", "Axis") - ) + result.addS( + j, name=name, + role=Role.PARENT, + material=Material.PLASTIC_PLA) for i in range(4): result.constrain(f"base?{name}_{i}", f"{name}?h{i}", "Point") result.solve()