cosplay: Touhou/Houjuu Nue #4
|
@ -40,7 +40,7 @@ class Hole:
|
|||
assert self.tag is not None
|
||||
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:
|
||||
return self.face
|
||||
diam = self.diam if self.diam is not None else default_diam
|
||||
|
|
|
@ -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.joints as MJ
|
||||
import nhf.touhou.houjuu_nue.harness as MH
|
||||
import nhf.touhou.houjuu_nue.electronics as ME
|
||||
from nhf.parts.item import Item
|
||||
import nhf.utils
|
||||
|
||||
|
@ -67,6 +68,7 @@ class Parameters(Model):
|
|||
parent_substrate_cull_corners=(1,1,1,1),
|
||||
parent_substrate_cull_edges=(0,0,1,0),
|
||||
),
|
||||
electronic_board=ME.ElectronicBoardControl(),
|
||||
shoulder_angle_bias=WING_DEFLECT_EVEN,
|
||||
s0_top_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.item import Item
|
||||
from nhf.parts.fasteners import FlatHeadBolt, HexNut
|
||||
from nhf.parts.electronics import ArduinoUnoR3
|
||||
from nhf.touhou.houjuu_nue.common import NUT_COMMON, BOLT_COMMON
|
||||
import nhf.utils
|
||||
|
||||
|
@ -509,7 +510,6 @@ class ElectronicBoard(Model):
|
|||
def __post_init__(self):
|
||||
super().__init__(name=self.name)
|
||||
|
||||
@submodel(name="panel")
|
||||
def panel(self) -> MountingBox:
|
||||
return MountingBox(
|
||||
holes=self.mount_holes,
|
||||
|
@ -528,7 +528,7 @@ class ElectronicBoard(Model):
|
|||
.addS(panel.generate(), name="panel",
|
||||
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"
|
||||
bolt_name = f"{hole.tag}_bolt"
|
||||
(
|
||||
|
@ -548,6 +548,49 @@ class ElectronicBoard(Model):
|
|||
)
|
||||
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)
|
||||
class LightStrip:
|
||||
|
||||
|
|
|
@ -455,7 +455,7 @@ class ShoulderJoint(Model):
|
|||
"""
|
||||
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:
|
||||
"""
|
||||
|
|
|
@ -19,6 +19,7 @@ from nhf.touhou.houjuu_nue.electronics import (
|
|||
LINEAR_ACTUATOR_21,
|
||||
LINEAR_ACTUATOR_50,
|
||||
ElectronicBoard,
|
||||
ElectronicBoardBattery,
|
||||
LightStrip,
|
||||
ELECTRONIC_MOUNT_HEXNUT,
|
||||
)
|
||||
|
@ -76,7 +77,7 @@ class WingProfile(Model):
|
|||
s0_top_hole: bool = False
|
||||
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
|
||||
|
||||
|
@ -156,6 +157,9 @@ class WingProfile(Model):
|
|||
@submodel(name="wrist-joint")
|
||||
def submodel_wrist_joint(self) -> Model:
|
||||
return self.wrist_joint
|
||||
@submodel(name="electronic-board")
|
||||
def submodel_electronic_board(self) -> Model:
|
||||
return self.electronic_board
|
||||
|
||||
@property
|
||||
def root_height(self) -> float:
|
||||
|
|
Loading…
Reference in New Issue