Cosplay/nhf/touhou/houjuu_nue/__init__.py

285 lines
8.5 KiB
Python
Raw Normal View History

2024-06-19 21:23:41 -07:00
from dataclasses import dataclass
2024-06-20 23:29:18 -07:00
import unittest
import cadquery as Cq
2024-06-22 13:40:06 -07:00
import nhf.joints
2024-06-23 22:27:15 -07:00
from nhf import Material
2024-06-19 15:54:09 -07:00
2024-06-19 21:23:41 -07:00
@dataclass(frozen=True)
class Parameters:
"""
Thickness of the exoskeleton panel in millimetres
"""
panel_thickness: float = 25.4 / 16
2024-06-23 22:27:15 -07:00
# Harness
harness_thickness: float = 25.4 / 8
harness_width = 300
harness_height = 400
harness_fillet = 10
harness_wing_base_pos = [
("r1", 70, 150),
("l1", -70, 150),
("r2", 100, 0),
("l2", -100, 0),
("r3", 70, -150),
("l3", -70, -150),
]
# Holes drilled onto harness for attachment with HS joint
harness_to_wing_base_hole_diam = 6
2024-06-19 21:23:41 -07:00
# Wing root properties
"""
2024-06-23 22:27:15 -07:00
The Houjuu-Scarlett joint mechanism at the base of the wing
"""
hs_joint_base_width = 85
hs_joint_base_thickness = 10
hs_joint_ring_thickness = 5
hs_joint_tooth_height = 10
hs_joint_radius = 30
hs_joint_radius_inner = 20
hs_joint_corner_fillet = 5
hs_joint_corner_cbore_diam = 12
hs_joint_corner_cbore_depth = 2
hs_joint_corner_inset = 12
hs_joint_axis_diam = 12
hs_joint_axis_cbore_diam = 20
hs_joint_axis_cbore_depth = 3
2024-06-23 22:27:15 -07:00
"""
2024-06-19 21:23:41 -07:00
Radius of the mounting mechanism of the wing root. This is constrained by
the size of the harness.
"""
root_radius: float = 60
2024-06-20 23:29:18 -07:00
"""
Heights for various wing joints, where the numbers start from the first joint.
"""
wing_r1_height = 100
wing_r1_width = 400
wing_r2_height = 100
wing_r3_height = 100
2024-06-22 13:40:06 -07:00
def print_geometries(self):
return [
self.hs_joint_parent()
]
2024-06-23 22:27:15 -07:00
def harness_profile(self) -> Cq.Sketch:
"""
Creates the harness shape
"""
w, h = self.harness_width / 2, self.harness_height / 2
sketch = (
Cq.Sketch()
.polygon([
(0.7 * w, h),
(w, 0),
(0.7 * w, -h),
(0.7 * -w, -h),
(-w, 0),
(0.7 * -w, h),
])
#.rect(self.harness_width, self.harness_height)
.vertices()
.fillet(self.harness_fillet)
)
for tag, x, y in self.harness_wing_base_pos:
conn = [(px + x, py + y) for px, py in self.hs_joint_harness_conn()]
sketch = (
sketch
.push(conn)
.tag(tag)
.circle(self.harness_to_wing_base_hole_diam / 2, mode='s')
.reset()
)
return sketch
def harness(self) -> Cq.Shape:
"""
Creates the harness shape
"""
result = (
Cq.Workplane('XZ')
.placeSketch(self.harness_profile())
.extrude(self.harness_thickness)
)
result.faces(">Y").tag("mount")
plane = result.faces(">Y").workplane()
for tag, x, y in self.harness_wing_base_pos:
conn = [(px + x, py + y) for px, py in self.hs_joint_harness_conn()]
for i, (px, py) in enumerate(conn):
(
plane
.moveTo(px, py)
.circle(1, forConstruction='True')
.edges()
.tag(f"{tag}_{i}")
)
return result
def hs_joint_harness_conn(self) -> list[tuple[int, int]]:
"""
Generates a set of points corresponding to the connectorss
"""
dx = self.hs_joint_base_width / 2 - self.hs_joint_corner_inset
return [
(dx, dx),
(dx, -dx),
(-dx, -dx),
(-dx, dx),
]
2024-06-22 13:40:06 -07:00
def hs_joint_parent(self):
"""
Parent part of the Houjuu-Scarlett joint, which is composed of a Hirth
coupling, a cylindrical base, and a mounting base.
"""
hirth = nhf.joints.hirth_joint(
radius=self.hs_joint_radius,
radius_inner=self.hs_joint_radius_inner,
tooth_height=self.hs_joint_tooth_height,
base_height=self.hs_joint_ring_thickness).val()
#hirth = (
# hirth
# .faces("<Z")
# .workplane()
# .transformed(
# offset=Cq.Vector(0, 0, -self.hs_joint_ring_thickness / 2),
# rotate=Cq.Vector(90, 0, 0))
# # This hole will drill only to the centre and not through. This is
# # intended.
# .hole(5)
# .val()
#)
2024-06-23 22:27:15 -07:00
conn = self.hs_joint_harness_conn()
2024-06-22 13:40:06 -07:00
result = (
Cq.Workplane('XY')
.box(
self.hs_joint_base_width,
self.hs_joint_base_width,
self.hs_joint_base_thickness,
centered=(True, True, False))
.edges("|Z")
.fillet(self.hs_joint_corner_fillet)
.faces(">Z")
.workplane()
2024-06-23 22:27:15 -07:00
.pushPoints(conn)
2024-06-22 13:40:06 -07:00
.cboreHole(
2024-06-23 22:27:15 -07:00
diameter=self.harness_to_wing_base_hole_diam,
2024-06-22 13:40:06 -07:00
cboreDiameter=self.hs_joint_corner_cbore_diam,
cboreDepth=self.hs_joint_corner_cbore_depth)
2024-06-23 22:27:15 -07:00
)
plane = result.faces(">Z").workplane(offset=-self.hs_joint_base_thickness)
for i, (px, py) in enumerate(conn):
(
plane
.moveTo(px, py)
.circle(1, forConstruction='True')
.edges()
.tag(f"h{i}")
)
result = (
result
2024-06-22 13:40:06 -07:00
.faces(">Z")
.workplane()
2024-06-23 22:27:15 -07:00
.union(hirth.move(Cq.Location((0, 0, self.hs_joint_base_thickness))), tol=0.1)
2024-06-22 13:40:06 -07:00
.clean()
)
result = (
result.faces("<Z")
.workplane()
.cboreHole(
diameter=self.hs_joint_axis_diam,
cboreDiameter=self.hs_joint_axis_cbore_diam,
cboreDepth=self.hs_joint_axis_cbore_depth,
)
.clean()
)
2024-06-23 22:27:15 -07:00
result.faces("<Z").tag("base")
2024-06-22 13:40:06 -07:00
return result
2024-06-20 23:29:18 -07:00
def wing_r1_profile(self) -> Cq.Sketch:
"""
Generates the first wing segment profile, with the wing root pointing in
the positive x axis.
"""
# Depression of the wing middle
bend = 200
factor = 0.7
result = (
Cq.Sketch()
.segment((0, 0), (0, self.wing_r1_height))
.spline([
(0, self.wing_r1_height),
(0.5 * self.wing_r1_width, self.wing_r1_height - factor * bend),
(self.wing_r1_width, self.wing_r1_height - bend),
])
.segment(
(self.wing_r1_width, self.wing_r1_height - bend),
(self.wing_r1_width, -bend),
)
.spline([
(self.wing_r1_width, - bend),
(0.5 * self.wing_r1_width, - factor * bend),
(0, 0),
])
.assemble()
)
return result
2024-06-20 23:45:24 -07:00
def wing_r1(self) -> Cq.Solid:
profile = self.wing_r1_profile()
result = (
Cq.Workplane("XY")
.placeSketch(profile)
.extrude(self.panel_thickness)
.val()
)
return result
2024-06-19 21:23:41 -07:00
def wing_root(self,
side_width=30,
2024-06-20 23:29:18 -07:00
side_height=100) -> Cq.Shape:
2024-06-19 21:23:41 -07:00
"""
Generate the wing root which contains a Hirth joint at its base and a
rectangular opening on its side, with the necessary interfaces.
"""
result = (
Cq.Workplane("XY")
.circle(self.root_radius)
.transformed(offset=Cq.Vector(80, 0, 80),
rotate=Cq.Vector(0, 45, 0))
.rect(side_width, side_height)
.loft()
2024-06-20 23:29:18 -07:00
.val()
2024-06-19 21:23:41 -07:00
)
return result
2024-06-20 23:29:18 -07:00
2024-06-23 22:27:15 -07:00
def harness_assembly(self):
harness = self.harness()
result = (
Cq.Assembly()
.add(harness, name="harness", color=Material.WOOD_BIRCH.color)
.constrain("harness", "Fixed")
)
for name in ["l1", "l2", "l3", "r1", "r2", "r3"]:
j = self.hs_joint_parent()
(
result
.add(j, name=f"hs_{name}p", color=Material.PLASTIC_PLA.color)
#.constrain(f"harness?{name}", f"hs_{name}p?mate", "Point")
.constrain("harness?mount", f"hs_{name}p?base", "Axis")
)
for i in range(4):
result.constrain(f"harness?{name}_{i}", f"hs_{name}p?h{i}", "Point")
result.solve()
return result