cosplay: Touhou/Houjuu Nue #4
|
@ -0,0 +1,72 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Tuple
|
||||||
|
import cadquery as Cq
|
||||||
|
from nhf import Item, Role
|
||||||
|
from nhf.parts.box import Hole, MountingBox
|
||||||
|
import nhf.utils
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ArduinoUnoR3(Item):
|
||||||
|
# From datasheet
|
||||||
|
mass: float = 25.0
|
||||||
|
length: float = 68.6
|
||||||
|
width: float = 53.4
|
||||||
|
|
||||||
|
# with clearance
|
||||||
|
base_height: float = 5.0
|
||||||
|
# aesthetic only for illustrating clearance
|
||||||
|
total_height: float = 50.0
|
||||||
|
roof_height: float = 20.0
|
||||||
|
|
||||||
|
# This is labeled in mirrored coordinates from top down (i.e. unmirrored from bottom up)
|
||||||
|
holes: list[Tuple[float, float]] = field(default_factory=lambda: [
|
||||||
|
(15.24, 2.54),
|
||||||
|
(13.74, 50.80), # x coordinate not labeled on schematic
|
||||||
|
(66.04, 17.78),
|
||||||
|
(66.04, 45.72),
|
||||||
|
])
|
||||||
|
hole_diam: float = 3.0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "Arduino Uno R3"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def role(self) -> Role:
|
||||||
|
return Role.ELECTRONIC
|
||||||
|
|
||||||
|
def generate(self) -> Cq.Assembly:
|
||||||
|
sketch = (
|
||||||
|
Cq.Sketch()
|
||||||
|
.polygon([
|
||||||
|
(0,0),
|
||||||
|
(self.length, 0),
|
||||||
|
(self.length, self.width),
|
||||||
|
(0,self.width)
|
||||||
|
])
|
||||||
|
.push([(x, self.width - y) for x,y in self.holes])
|
||||||
|
.circle(self.hole_diam / 2, mode='s')
|
||||||
|
)
|
||||||
|
# pillar thickness
|
||||||
|
t = 3.0
|
||||||
|
pillar_height = self.total_height - self.base_height
|
||||||
|
pillar = Cq.Solid.makeBox(
|
||||||
|
t, t, pillar_height
|
||||||
|
)
|
||||||
|
roof = Cq.Solid.makeBox(
|
||||||
|
self.length, self.width, t
|
||||||
|
)
|
||||||
|
result = (
|
||||||
|
Cq.Workplane('XY')
|
||||||
|
.placeSketch(sketch)
|
||||||
|
.extrude(self.base_height)
|
||||||
|
.union(pillar.located(Cq.Location((0, 0, self.base_height))))
|
||||||
|
.union(pillar.located(Cq.Location((self.length - t, 0, self.base_height))))
|
||||||
|
.union(pillar.located(Cq.Location((self.length - t, self.width - t, self.base_height))))
|
||||||
|
.union(pillar.located(Cq.Location((0, self.width - t, self.base_height))))
|
||||||
|
.union(roof.located(Cq.Location((0, 0, self.total_height - t))))
|
||||||
|
)
|
||||||
|
plane = result.copyWorkplane(Cq.Workplane('XY'))
|
||||||
|
for i, (x, y) in enumerate(self.holes):
|
||||||
|
plane.moveTo(x, self.width - y).tagPlane(f"conn{i}", direction='-Z')
|
||||||
|
return result
|
|
@ -37,6 +37,7 @@ import nhf.touhou.houjuu_nue.wing as MW
|
||||||
import nhf.touhou.houjuu_nue.trident as MT
|
import nhf.touhou.houjuu_nue.trident as MT
|
||||||
import nhf.touhou.houjuu_nue.joints as MJ
|
import nhf.touhou.houjuu_nue.joints as MJ
|
||||||
import nhf.touhou.houjuu_nue.harness as MH
|
import nhf.touhou.houjuu_nue.harness as MH
|
||||||
|
import nhf.touhou.houjuu_nue.electronics as ME
|
||||||
from nhf.parts.item import Item
|
from nhf.parts.item import Item
|
||||||
import nhf.utils
|
import nhf.utils
|
||||||
|
|
||||||
|
@ -67,6 +68,7 @@ class Parameters(Model):
|
||||||
parent_substrate_cull_corners=(1,1,1,1),
|
parent_substrate_cull_corners=(1,1,1,1),
|
||||||
parent_substrate_cull_edges=(0,0,1,0),
|
parent_substrate_cull_edges=(0,0,1,0),
|
||||||
),
|
),
|
||||||
|
electronic_board=ME.ElectronicBoardControl(),
|
||||||
shoulder_angle_bias=WING_DEFLECT_EVEN,
|
shoulder_angle_bias=WING_DEFLECT_EVEN,
|
||||||
s0_top_hole=True,
|
s0_top_hole=True,
|
||||||
s0_bot_hole=True,
|
s0_bot_hole=True,
|
||||||
|
|
|
@ -11,6 +11,7 @@ from nhf.parts.box import MountingBox, Hole
|
||||||
from nhf.parts.fibre import tension_fibre
|
from nhf.parts.fibre import tension_fibre
|
||||||
from nhf.parts.item import Item
|
from nhf.parts.item import Item
|
||||||
from nhf.parts.fasteners import FlatHeadBolt, HexNut
|
from nhf.parts.fasteners import FlatHeadBolt, HexNut
|
||||||
|
from nhf.parts.electronics import ArduinoUnoR3
|
||||||
from nhf.touhou.houjuu_nue.common import NUT_COMMON, BOLT_COMMON
|
from nhf.touhou.houjuu_nue.common import NUT_COMMON, BOLT_COMMON
|
||||||
import nhf.utils
|
import nhf.utils
|
||||||
|
|
||||||
|
@ -509,7 +510,6 @@ class ElectronicBoard(Model):
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
super().__init__(name=self.name)
|
super().__init__(name=self.name)
|
||||||
|
|
||||||
@submodel(name="panel")
|
|
||||||
def panel(self) -> MountingBox:
|
def panel(self) -> MountingBox:
|
||||||
return MountingBox(
|
return MountingBox(
|
||||||
holes=self.mount_holes,
|
holes=self.mount_holes,
|
||||||
|
@ -528,7 +528,7 @@ class ElectronicBoard(Model):
|
||||||
.addS(panel.generate(), name="panel",
|
.addS(panel.generate(), name="panel",
|
||||||
role=Role.ELECTRONIC | Role.STRUCTURE, material=self.material)
|
role=Role.ELECTRONIC | Role.STRUCTURE, material=self.material)
|
||||||
)
|
)
|
||||||
for hole in panel.holes:
|
for hole in self.mount_holes:
|
||||||
spacer_name = f"{hole.tag}_spacer"
|
spacer_name = f"{hole.tag}_spacer"
|
||||||
bolt_name = f"{hole.tag}_bolt"
|
bolt_name = f"{hole.tag}_bolt"
|
||||||
(
|
(
|
||||||
|
@ -548,6 +548,49 @@ class ElectronicBoard(Model):
|
||||||
)
|
)
|
||||||
return result.solve()
|
return result.solve()
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ElectronicBoardBattery(ElectronicBoard):
|
||||||
|
name: str = "electronic-board-battery"
|
||||||
|
|
||||||
|
@submodel(name="panel")
|
||||||
|
def panel_out(self) -> MountingBox:
|
||||||
|
return self.panel()
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ElectronicBoardControl(ElectronicBoard):
|
||||||
|
name: str = "electronic-board-control"
|
||||||
|
|
||||||
|
controller_datum: Cq.Location = Cq.Location.from2d(-25,10, -90)
|
||||||
|
|
||||||
|
controller: ArduinoUnoR3 = ArduinoUnoR3()
|
||||||
|
|
||||||
|
def panel(self) -> MountingBox:
|
||||||
|
box = super().panel()
|
||||||
|
def transform(i, x, y):
|
||||||
|
pos = self.controller_datum * Cq.Location.from2d(x, self.controller.width - y)
|
||||||
|
x, y = pos.to2d_pos()
|
||||||
|
return Hole(
|
||||||
|
x=x, y=y,
|
||||||
|
diam=self.controller.hole_diam,
|
||||||
|
tag=f"controller_conn{i}",
|
||||||
|
)
|
||||||
|
box.holes = box.holes.copy() + [
|
||||||
|
transform(i, x, y)
|
||||||
|
for i, (x, y) in enumerate(self.controller.holes)
|
||||||
|
]
|
||||||
|
return box
|
||||||
|
|
||||||
|
@submodel(name="panel")
|
||||||
|
def panel_out(self) -> MountingBox:
|
||||||
|
return self.panel()
|
||||||
|
|
||||||
|
def assembly(self) -> Cq.Assembly:
|
||||||
|
result = super().assembly()
|
||||||
|
result.add(self.controller.assembly(), name="controller")
|
||||||
|
for i in range(len(self.controller.holes)):
|
||||||
|
result.constrain(f"controller?conn{i}", f"panel?controller_conn{i}", "Plane")
|
||||||
|
return result.solve()
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class LightStrip:
|
class LightStrip:
|
||||||
|
|
||||||
|
|
|
@ -455,7 +455,7 @@ class ShoulderJoint(Model):
|
||||||
"""
|
"""
|
||||||
Position of the middle of the spool measured from the middle
|
Position of the middle of the spool measured from the middle
|
||||||
"""
|
"""
|
||||||
return self.height / 2 - self.torsion_joint.total_height - self.spool_base_height / 2
|
return 0
|
||||||
|
|
||||||
def parent_lip_loc(self, left: bool=True) -> Cq.Location:
|
def parent_lip_loc(self, left: bool=True) -> Cq.Location:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -19,6 +19,7 @@ from nhf.touhou.houjuu_nue.electronics import (
|
||||||
LINEAR_ACTUATOR_21,
|
LINEAR_ACTUATOR_21,
|
||||||
LINEAR_ACTUATOR_50,
|
LINEAR_ACTUATOR_50,
|
||||||
ElectronicBoard,
|
ElectronicBoard,
|
||||||
|
ElectronicBoardBattery,
|
||||||
LightStrip,
|
LightStrip,
|
||||||
ELECTRONIC_MOUNT_HEXNUT,
|
ELECTRONIC_MOUNT_HEXNUT,
|
||||||
)
|
)
|
||||||
|
@ -76,7 +77,7 @@ class WingProfile(Model):
|
||||||
s0_top_hole: bool = False
|
s0_top_hole: bool = False
|
||||||
s0_bot_hole: bool = True
|
s0_bot_hole: bool = True
|
||||||
|
|
||||||
electronic_board: ElectronicBoard = field(default_factory=lambda: ElectronicBoard())
|
electronic_board: ElectronicBoard = field(default_factory=lambda: ElectronicBoardBattery())
|
||||||
|
|
||||||
s1_thickness: float = 25.0
|
s1_thickness: float = 25.0
|
||||||
|
|
||||||
|
@ -156,6 +157,9 @@ class WingProfile(Model):
|
||||||
@submodel(name="wrist-joint")
|
@submodel(name="wrist-joint")
|
||||||
def submodel_wrist_joint(self) -> Model:
|
def submodel_wrist_joint(self) -> Model:
|
||||||
return self.wrist_joint
|
return self.wrist_joint
|
||||||
|
@submodel(name="electronic-board")
|
||||||
|
def submodel_electronic_board(self) -> Model:
|
||||||
|
return self.electronic_board
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def root_height(self) -> float:
|
def root_height(self) -> float:
|
||||||
|
|
Loading…
Reference in New Issue