2024-07-14 23:58:42 -07:00
|
|
|
from dataclasses import dataclass, field
|
|
|
|
import cadquery as Cq
|
|
|
|
from nhf.parts.joints import HirthJoint
|
|
|
|
from nhf import Material, Role
|
2024-07-19 15:06:57 -07:00
|
|
|
from nhf.build import Model, TargetKind, target, assembly, submodel
|
|
|
|
from nhf.touhou.houjuu_nue.joints import RootJoint
|
2024-07-14 23:58:42 -07:00
|
|
|
import nhf.utils
|
|
|
|
|
2024-07-18 21:33:17 -07:00
|
|
|
@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))
|
|
|
|
|
2024-07-18 21:40:47 -07:00
|
|
|
|
|
|
|
BASE_POS_X = 60.0
|
|
|
|
BASE_POS_Y = 100.0
|
|
|
|
|
2024-07-18 21:33:17 -07:00
|
|
|
@dataclass(kw_only=True)
|
2024-07-16 15:42:39 -07:00
|
|
|
class Harness(Model):
|
2024-07-14 23:58:42 -07:00
|
|
|
thickness: float = 25.4 / 8
|
2024-07-18 21:40:47 -07:00
|
|
|
width: float = 220.0
|
|
|
|
height: float = 310.0
|
2024-07-14 23:58:42 -07:00
|
|
|
fillet: float = 10.0
|
|
|
|
|
|
|
|
wing_base_pos: list[tuple[str, float, float]] = field(default_factory=lambda: [
|
2024-07-18 21:40:47 -07:00
|
|
|
("r1", BASE_POS_X + 10, BASE_POS_Y),
|
|
|
|
("l1", -BASE_POS_X - 10, BASE_POS_Y),
|
|
|
|
("r2", BASE_POS_X + 10, 0),
|
|
|
|
("l2", -BASE_POS_X - 10, 0),
|
|
|
|
("r3", BASE_POS_X, -BASE_POS_Y),
|
|
|
|
("l3", -BASE_POS_X, -BASE_POS_Y),
|
2024-07-14 23:58:42 -07:00
|
|
|
])
|
2024-07-19 15:06:57 -07:00
|
|
|
|
|
|
|
root_joint: RootJoint = field(default_factory=lambda: RootJoint())
|
2024-07-14 23:58:42 -07:00
|
|
|
|
2024-07-18 21:33:17 -07:00
|
|
|
mannequin: Mannequin = Mannequin()
|
|
|
|
|
2024-07-16 15:42:39 -07:00
|
|
|
def __post_init__(self):
|
|
|
|
super().__init__(name="harness")
|
|
|
|
|
2024-07-19 15:06:57 -07:00
|
|
|
@submodel(name="root-joint")
|
|
|
|
def submodel_root_joint(self) -> Model:
|
|
|
|
return self.root_joint
|
|
|
|
|
2024-07-16 15:42:39 -07:00
|
|
|
@target(name="profile", kind=TargetKind.DXF)
|
2024-07-14 23:58:42 -07:00
|
|
|
def profile(self) -> Cq.Sketch:
|
|
|
|
"""
|
|
|
|
Creates the harness shape
|
|
|
|
"""
|
|
|
|
w, h = self.width / 2, self.height / 2
|
|
|
|
sketch = (
|
|
|
|
Cq.Sketch()
|
|
|
|
.polygon([
|
2024-07-16 21:20:45 -07:00
|
|
|
(w, h),
|
|
|
|
(w, -h),
|
|
|
|
(-w, -h),
|
|
|
|
(-w, h),
|
|
|
|
#(0.7 * w, h),
|
|
|
|
#(w, 0),
|
|
|
|
#(0.7 * w, -h),
|
|
|
|
#(0.7 * -w, -h),
|
|
|
|
#(-w, 0),
|
|
|
|
#(0.7 * -w, h),
|
2024-07-14 23:58:42 -07:00
|
|
|
])
|
|
|
|
#.rect(self.harness_width, self.harness_height)
|
|
|
|
.vertices()
|
|
|
|
.fillet(self.fillet)
|
|
|
|
)
|
|
|
|
for tag, x, y in self.wing_base_pos:
|
2024-07-19 15:06:57 -07:00
|
|
|
conn = [(px + x, py + y) for px, py in self.root_joint.corner_pos()]
|
2024-07-14 23:58:42 -07:00
|
|
|
sketch = (
|
|
|
|
sketch
|
|
|
|
.push(conn)
|
|
|
|
.tag(tag)
|
2024-07-19 15:06:57 -07:00
|
|
|
.circle(self.root_joint.corner_hole_diam / 2, mode='s')
|
2024-07-14 23:58:42 -07:00
|
|
|
.reset()
|
|
|
|
)
|
|
|
|
return sketch
|
|
|
|
|
|
|
|
def surface(self) -> Cq.Workplane:
|
|
|
|
"""
|
|
|
|
Creates the harness shape
|
|
|
|
"""
|
|
|
|
result = (
|
|
|
|
Cq.Workplane('XZ')
|
|
|
|
.placeSketch(self.profile())
|
|
|
|
.extrude(self.thickness)
|
|
|
|
)
|
|
|
|
result.faces(">Y").tag("mount")
|
|
|
|
plane = result.faces(">Y").workplane()
|
|
|
|
for tag, x, y in self.wing_base_pos:
|
|
|
|
conn = [(px + x, py + y) for px, py
|
2024-07-19 15:06:57 -07:00
|
|
|
in self.root_joint.corner_pos()]
|
2024-07-14 23:58:42 -07:00
|
|
|
for i, (px, py) in enumerate(conn):
|
|
|
|
plane.moveTo(px, py).tagPoint(f"{tag}_{i}")
|
|
|
|
return result
|
|
|
|
|
2024-07-19 15:06:57 -07:00
|
|
|
def add_root_joint_constraint(
|
|
|
|
self,
|
|
|
|
a: Cq.Assembly,
|
|
|
|
harness_tag: str,
|
|
|
|
joint_tag: str,
|
|
|
|
mount_tag: str):
|
|
|
|
for i in range(4):
|
|
|
|
a.constrain(f"{harness_tag}?{mount_tag}_{i}", f"{joint_tag}/parent?h{i}", "Point")
|
2024-07-14 23:58:42 -07:00
|
|
|
|
|
|
|
|
2024-07-16 15:42:39 -07:00
|
|
|
@assembly()
|
2024-07-19 15:06:57 -07:00
|
|
|
def assembly(self, with_root_joint: bool = False) -> Cq.Assembly:
|
2024-07-14 23:58:42 -07:00
|
|
|
harness = self.surface()
|
2024-07-18 21:33:17 -07:00
|
|
|
mannequin_z = self.mannequin.shoulder_to_waist * 0.6
|
2024-07-19 15:06:57 -07:00
|
|
|
|
|
|
|
|
2024-07-14 23:58:42 -07:00
|
|
|
result = (
|
|
|
|
Cq.Assembly()
|
2024-07-18 21:33:17 -07:00
|
|
|
.addS(
|
|
|
|
harness, name="base",
|
|
|
|
material=Material.WOOD_BIRCH,
|
|
|
|
role=Role.STRUCTURE)
|
2024-07-14 23:58:42 -07:00
|
|
|
.constrain("base", "Fixed")
|
2024-07-18 21:33:17 -07:00
|
|
|
.addS(
|
|
|
|
self.mannequin.generate(),
|
|
|
|
name="mannequin",
|
|
|
|
role=Role.FIXTURE,
|
|
|
|
loc=Cq.Location((0, -self.thickness, -mannequin_z), (0, 0, 1), 180))
|
|
|
|
.constrain("mannequin", "Fixed")
|
2024-07-14 23:58:42 -07:00
|
|
|
)
|
2024-07-19 15:06:57 -07:00
|
|
|
if with_root_joint:
|
|
|
|
for name in ["l1", "l2", "l3", "r1", "r2", "r3"]:
|
|
|
|
result.addS(
|
|
|
|
self.root_joint.assembly(), name=name,
|
|
|
|
role=Role.PARENT,
|
|
|
|
material=Material.PLASTIC_PLA)
|
|
|
|
self.add_root_joint_constraint(result, "base", name, name)
|
2024-07-14 23:58:42 -07:00
|
|
|
result.solve()
|
|
|
|
return result
|