cosplay: Touhou/Houjuu Nue #1

Closed
aniva wants to merge 37 commits from touhou/houjuu-nue into main
6 changed files with 208 additions and 1 deletions
Showing only changes of commit a41906d130 - Show all commits

156
nhf/handle.py Normal file
View File

@ -0,0 +1,156 @@
"""
This schematics file contains all designs related to tool handles
"""
from dataclasses import dataclass
import cadquery as Cq
@dataclass(frozen=True)
class Handle:
"""
Characteristic of a tool handle
"""
# Outer radius for the handle
radius: float = 38 / 2
# Inner radius
radius_inner: float = 33 / 2
# Wall thickness for the connector
insertion_thickness: float = 4
# The connector goes in the insertion
connector_thickness: float = 4
# Length for the rim on the female connector
rim_length: float = 5
insertion_length: float = 60
connector_length: float = 60
def __post_init__(self):
assert self.radius > self.radius_inner
assert self.radius_inner > self.insertion_thickness + self.connector_thickness
assert self.insertion_length > self.rim_length
@property
def _r1(self):
"""
Radius of inside of insertion
"""
return self.radius_inner - self.insertion_thickness
@property
def _r2(self):
"""
Radius of inside of connector
"""
return self._r1 - self.connector_thickness
def segment(self, length: float):
result = (
Cq.Workplane()
.cylinder(radius=self.radius, height=length)
)
result.faces("<Z").tag("mate1")
result.faces(">Z").tag("mate2")
return result
def insertion(self):
"""
This type of joint is used to connect two handlebar pieces. Each handlebar
piece is a tube which cannot be machined, so the joint connects to the
handle by glue.
Tags:
* lip: Co-planar Mates to the rod
* mate: Mates to the connector
"""
result = (
Cq.Workplane('XY')
.cylinder(
radius=self.radius_inner,
height=self.insertion_length - self.rim_length,
centered=[True, True, False])
)
result.faces(">Z").tag("lip")
result = (
result.faces(">Z")
.workplane()
.circle(self.radius)
.extrude(self.rim_length)
.faces(">Z")
.hole(2 * self._r1)
)
result.faces(">Z").tag("mate")
return result
def connector(self, solid: bool = False):
"""
Tags:
* mate{1,2}: Mates to the connector
"""
result = (
Cq.Workplane('XY')
.cylinder(
radius=self.radius,
height=self.connector_length,
)
)
for (tag, selector) in [("mate1", "<Z"), ("mate2", ">Z")]:
result.faces(selector).tag(tag)
r1 = self.radius_inner
result = (
result
.faces(selector)
.workplane()
.circle(self._r1)
.extrude(self.insertion_length)
)
if not solid:
result = result.faces(">Z").hole(2 * self._r2)
return result
def one_side_connector(self):
result = (
Cq.Workplane('XY')
.cylinder(
radius=self.radius,
height=self.rim_length,
)
)
result.faces("<Z").tag("mate")
result.faces(">Z").tag("base")
result = (
result
.faces("<Z")
.workplane()
.circle(self._r1)
.extrude(self.insertion_length)
)
return result
def connector_insertion_assembly(self):
connector_color = Cq.Color(0.8,0.8,0.5,0.3)
insertion_color = Cq.Color(0.7,0.7,0.7,0.3)
result = (
Cq.Assembly()
.add(self.connector(), name="c", color=connector_color)
.add(self.insertion(), name="i1", color=insertion_color)
.add(self.insertion(), name="i2", color=insertion_color)
.constrain("c?mate1", "i1?mate", "Plane")
.constrain("c?mate2", "i2?mate", "Plane")
.solve()
)
return result
def connector_one_side_insertion_assembly(self):
connector_color = Cq.Color(0.8,0.8,0.5,0.3)
insertion_color = Cq.Color(0.7,0.7,0.7,0.3)
result = (
Cq.Assembly()
.add(self.insertion(), name="i", color=connector_color)
.add(self.one_side_connector(), name="c", color=insertion_color)
.constrain("i?mate", "c?mate", "Plane")
.solve()
)
return result

View File

@ -12,6 +12,7 @@ class Material(Enum):
WOOD_BIRCH = 0.8, _color('bisque', 0.9)
PLASTIC_PLA = 0.5, _color('azure3', 0.6)
ACRYLIC_BLACK = 0.5, _color('gray50', 0.6)
def __init__(self, density: float, color: Cq.Color):
self.density = density

View File

@ -1,6 +1,7 @@
import unittest
import cadquery as Cq
import nhf.joints
import nhf.handle
class TestJoints(unittest.TestCase):
@ -13,5 +14,13 @@ class TestJoints(unittest.TestCase):
def test_joints_comma_assembly(self):
nhf.joints.comma_assembly()
class TestHandle(unittest.TestCase):
def test_handle_assembly(self):
h = nhf.handle.Handle()
h.connector_insertion_assembly()
h.connector_one_side_insertion_assembly()
if __name__ == '__main__':
unittest.main()

View File

@ -20,15 +20,18 @@ the wing which demands transluscency are created from 1/16" acrylic panels.
These panels serve double duty as the exoskeleton.
The wings are labeled r1,r2,r3,l1,l2,l3. The segments of the wings are labeled
from root to tip s0 (root), s1, s2, s3. The joints are named (from root to tip)
from root to tip s0 (root),
s1, s2, s3. The joints are named (from root to tip)
shoulder, elbow, wrist in analogy with human anatomy.
"""
from dataclasses import dataclass
import unittest
import cadquery as Cq
import nhf.joints
import nhf.handle
from nhf import Material
import nhf.touhou.houjuu_nue.wing as MW
import nhf.touhou.houjuu_nue.trident as MT
@dataclass(frozen=True)
class Parameters:
@ -86,6 +89,8 @@ class Parameters:
wing_r2_height = 100
wing_r3_height = 100
trident_handle: nhf.handle.Handle = nhf.handle.Handle()
def __post_init__(self):
assert self.wing_root_radius > self.hs_joint_radius,\
"Wing root must be large enough to accomodate joint"
@ -301,3 +306,6 @@ class Parameters:
result.constrain(f"harness?{name}_{i}", f"hs_{name}p?h{i}", "Point")
result.solve()
return result
def trident_assembly(self):
return MT.trident_assembly(self.trident_handle)

View File

@ -18,6 +18,9 @@ class Test(unittest.TestCase):
def test_harness(self):
p = M.Parameters()
p.harness_assembly()
def test_trident():
p = M.Parameters()
p.trident_assembly()
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,30 @@
import math
import cadquery as Cq
from nhf import Material
from nhf.handle import Handle
def trident_assembly(
handle: Handle,
handle_segment_length: float = 24*25.4):
def segment():
return handle.segment(handle_segment_length)
mat_i = Material.PLASTIC_PLA
mat_s = Material.ACRYLIC_BLACK
assembly = (
Cq.Assembly()
.add(handle.insertion(), name="i0", color=mat_i.color)
.constrain("i0", "Fixed")
.add(segment(), name="s1", color=mat_s.color)
.constrain("i0?lip", "s1?mate1", "Plane", param=0)
.add(handle.insertion(), name="i1", color=mat_i.color)
.add(handle.connector(), name="c1", color=mat_i.color)
.add(handle.insertion(), name="i2", color=mat_i.color)
.constrain("s1?mate2", "i1?lip", "Plane", param=0)
.constrain("i1?mate", "c1?mate1", "Plane")
.constrain("i2?mate", "c1?mate2", "Plane")
.add(segment(), name="s2", color=mat_s.color)
.constrain("i2?lip", "s2?mate1", "Plane", param=0)
.add(handle.insertion(), name="i3", color=mat_i.color)
.constrain("s2?mate2", "i3?lip", "Plane", param=0)
)
return assembly.solve()