cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
6 changed files with 126 additions and 5 deletions
Showing only changes of commit 8efea1d038 - Show all commits

View File

@ -40,7 +40,7 @@ class Hole:
assert self.tag is not None assert self.tag is not None
return self.tag + "_rev" return self.tag + "_rev"
def cutting_geometry(self, default_diam: Optional[float]=None) -> Cq.Face: def cutting_geometry(self, default_diam: Optional[float] = None) -> Cq.Face:
if self.face is not None: if self.face is not None:
return self.face return self.face
diam = self.diam if self.diam is not None else default_diam diam = self.diam if self.diam is not None else default_diam

72
nhf/parts/electronics.py Normal file
View File

@ -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

View File

@ -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,

View File

@ -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:

View File

@ -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:
""" """

View File

@ -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: