cosplay: Touhou/Houjuu Nue #4
|
@ -0,0 +1 @@
|
||||||
|
from nhf.materials import Material
|
|
@ -0,0 +1,18 @@
|
||||||
|
"""
|
||||||
|
A catalog of material properties
|
||||||
|
"""
|
||||||
|
from enum import Enum
|
||||||
|
import cadquery as Cq
|
||||||
|
|
||||||
|
def _color(name: str, alpha: float) -> Cq.Color:
|
||||||
|
r, g, b, _ = Cq.Color(name).toTuple()
|
||||||
|
return Cq.Color(r, g, b, alpha)
|
||||||
|
|
||||||
|
class Material(Enum):
|
||||||
|
|
||||||
|
WOOD_BIRCH = 0.8, _color('bisque', 0.9)
|
||||||
|
PLASTIC_PLA = 0.5, _color('azure3', 0.6)
|
||||||
|
|
||||||
|
def __init__(self, density: float, color: Cq.Color):
|
||||||
|
self.density = density
|
||||||
|
self.color = color
|
|
@ -2,6 +2,7 @@ from dataclasses import dataclass
|
||||||
import unittest
|
import unittest
|
||||||
import cadquery as Cq
|
import cadquery as Cq
|
||||||
import nhf.joints
|
import nhf.joints
|
||||||
|
from nhf import Material
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Parameters:
|
class Parameters:
|
||||||
|
@ -11,8 +12,39 @@ class Parameters:
|
||||||
"""
|
"""
|
||||||
panel_thickness: float = 25.4 / 16
|
panel_thickness: float = 25.4 / 16
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Wing root properties
|
# Wing root properties
|
||||||
"""
|
"""
|
||||||
|
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 = 12
|
||||||
|
hs_joint_corner_inset = 15
|
||||||
|
"""
|
||||||
Radius of the mounting mechanism of the wing root. This is constrained by
|
Radius of the mounting mechanism of the wing root. This is constrained by
|
||||||
the size of the harness.
|
the size of the harness.
|
||||||
"""
|
"""
|
||||||
|
@ -26,26 +58,77 @@ class Parameters:
|
||||||
wing_r2_height = 100
|
wing_r2_height = 100
|
||||||
wing_r3_height = 100
|
wing_r3_height = 100
|
||||||
|
|
||||||
"""
|
|
||||||
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 = 10
|
|
||||||
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 = 12
|
|
||||||
hs_joint_corner_diam = 15
|
|
||||||
hs_joint_corner_inset = 15
|
|
||||||
|
|
||||||
def print_geometries(self):
|
def print_geometries(self):
|
||||||
return [
|
return [
|
||||||
self.hs_joint_parent()
|
self.hs_joint_parent()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
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),
|
||||||
|
]
|
||||||
|
|
||||||
def hs_joint_parent(self):
|
def hs_joint_parent(self):
|
||||||
"""
|
"""
|
||||||
Parent part of the Houjuu-Scarlett joint, which is composed of a Hirth
|
Parent part of the Houjuu-Scarlett joint, which is composed of a Hirth
|
||||||
|
@ -56,7 +139,6 @@ class Parameters:
|
||||||
radius_inner=self.hs_joint_radius_inner,
|
radius_inner=self.hs_joint_radius_inner,
|
||||||
tooth_height=self.hs_joint_tooth_height,
|
tooth_height=self.hs_joint_tooth_height,
|
||||||
base_height=self.hs_joint_ring_thickness)
|
base_height=self.hs_joint_ring_thickness)
|
||||||
hole_rect_width = self.hs_joint_base_width - 2 * self.hs_joint_corner_inset
|
|
||||||
hirth = (
|
hirth = (
|
||||||
hirth
|
hirth
|
||||||
.faces("<Z")
|
.faces("<Z")
|
||||||
|
@ -69,6 +151,7 @@ class Parameters:
|
||||||
.hole(5)
|
.hole(5)
|
||||||
.val()
|
.val()
|
||||||
)
|
)
|
||||||
|
conn = self.hs_joint_harness_conn()
|
||||||
result = (
|
result = (
|
||||||
Cq.Workplane('XY')
|
Cq.Workplane('XY')
|
||||||
.box(
|
.box(
|
||||||
|
@ -80,17 +163,29 @@ class Parameters:
|
||||||
.fillet(self.hs_joint_corner_fillet)
|
.fillet(self.hs_joint_corner_fillet)
|
||||||
.faces(">Z")
|
.faces(">Z")
|
||||||
.workplane()
|
.workplane()
|
||||||
.rect(hole_rect_width, hole_rect_width, forConstruction=True)
|
.pushPoints(conn)
|
||||||
.vertices()
|
|
||||||
.cboreHole(
|
.cboreHole(
|
||||||
diameter=self.hs_joint_corner_diam,
|
diameter=self.harness_to_wing_base_hole_diam,
|
||||||
cboreDiameter=self.hs_joint_corner_cbore_diam,
|
cboreDiameter=self.hs_joint_corner_cbore_diam,
|
||||||
cboreDepth=self.hs_joint_corner_cbore_depth)
|
cboreDepth=self.hs_joint_corner_cbore_depth)
|
||||||
|
)
|
||||||
|
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
|
||||||
.faces(">Z")
|
.faces(">Z")
|
||||||
.workplane()
|
.workplane()
|
||||||
.union(hirth.move((0, 0, self.hs_joint_base_thickness)), tol=0.1)
|
.union(hirth.move(Cq.Location((0, 0, self.hs_joint_base_thickness))), tol=0.1)
|
||||||
.clean()
|
.clean()
|
||||||
)
|
)
|
||||||
|
result.faces("<Z").tag("base")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@ -152,3 +247,22 @@ class Parameters:
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -9,6 +9,9 @@ class Test(unittest.TestCase):
|
||||||
def test_wings(self):
|
def test_wings(self):
|
||||||
p = M.Parameters()
|
p = M.Parameters()
|
||||||
p.wing_r1()
|
p.wing_r1()
|
||||||
|
def test_harness(self):
|
||||||
|
p = M.Parameters()
|
||||||
|
p.harness_assembly()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue