cosplay: Touhou/Houjuu Nue #4
|
@ -33,12 +33,20 @@ class Hole:
|
||||||
y: float = 0.0
|
y: float = 0.0
|
||||||
diam: Optional[float] = None
|
diam: Optional[float] = None
|
||||||
tag: Optional[str] = None
|
tag: Optional[str] = None
|
||||||
|
face: Optional[Cq.Face] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rev_tag(self) -> str:
|
def rev_tag(self) -> str:
|
||||||
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:
|
||||||
|
if self.face is not None:
|
||||||
|
return self.face
|
||||||
|
diam = self.diam if self.diam is not None else default_diam
|
||||||
|
assert diam is not None
|
||||||
|
return Cq.Face.makeFromWires(Cq.Wire.makeCircle(diam/2, Cq.Vector(), Cq.Vector(0,0,1)))
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MountingBox(Model):
|
class MountingBox(Model):
|
||||||
"""
|
"""
|
||||||
|
@ -84,8 +92,8 @@ class MountingBox(Model):
|
||||||
.rect(self.length, self.width)
|
.rect(self.length, self.width)
|
||||||
)
|
)
|
||||||
for hole in self.holes:
|
for hole in self.holes:
|
||||||
diam = hole.diam if hole.diam else self.hole_diam
|
face = hole.cutting_geometry(default_diam=self.hole_diam)
|
||||||
result.push([(hole.x, hole.y)]).circle(diam / 2, mode='s')
|
result.push([(hole.x, hole.y)]).each(lambda l:face.moved(l), mode='s')
|
||||||
if self.profile_callback:
|
if self.profile_callback:
|
||||||
result = self.profile_callback(result)
|
result = self.profile_callback(result)
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -103,12 +103,15 @@ class HexNut(Item):
|
||||||
def role(self) -> Role:
|
def role(self) -> Role:
|
||||||
return Role.CONNECTION
|
return Role.CONNECTION
|
||||||
|
|
||||||
|
@property
|
||||||
|
def radius(self) -> float:
|
||||||
|
return self.width / math.sqrt(3)
|
||||||
|
|
||||||
def generate(self) -> Cq.Workplane:
|
def generate(self) -> Cq.Workplane:
|
||||||
r = self.width / math.sqrt(3)
|
|
||||||
result = (
|
result = (
|
||||||
Cq.Workplane("XY")
|
Cq.Workplane("XY")
|
||||||
.sketch()
|
.sketch()
|
||||||
.regularPolygon(r=r, n=6)
|
.regularPolygon(r=self.radius, n=6)
|
||||||
.circle(r=self.diam_thread/2, mode='s')
|
.circle(r=self.diam_thread/2, mode='s')
|
||||||
.finalize()
|
.finalize()
|
||||||
.extrude(self.thickness)
|
.extrude(self.thickness)
|
||||||
|
@ -117,3 +120,10 @@ class HexNut(Item):
|
||||||
result.faces(">Z").tag("top")
|
result.faces(">Z").tag("top")
|
||||||
result.copyWorkplane(Cq.Workplane('XY')).tagPlane("dirX", direction="+X")
|
result.copyWorkplane(Cq.Workplane('XY')).tagPlane("dirX", direction="+X")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def cutting_face(self) -> Cq.Face:
|
||||||
|
return (
|
||||||
|
Cq.Sketch()
|
||||||
|
.regularPolygon(r=self.radius, n=6)
|
||||||
|
._faces
|
||||||
|
)
|
||||||
|
|
|
@ -335,6 +335,15 @@ LINEAR_ACTUATOR_BRACKET = MountingBracket()
|
||||||
|
|
||||||
BATTERY_BOX = BatteryBox18650()
|
BATTERY_BOX = BatteryBox18650()
|
||||||
|
|
||||||
|
# Acrylic hex nut
|
||||||
|
ELECTRONIC_MOUNT_HEXNUT = HexNut(
|
||||||
|
mass=0.8,
|
||||||
|
diam_thread=4,
|
||||||
|
pitch=0.7,
|
||||||
|
thickness=3.57,
|
||||||
|
width=6.81,
|
||||||
|
)
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class Flexor:
|
class Flexor:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -18,7 +18,8 @@ from nhf.touhou.houjuu_nue.electronics import (
|
||||||
LINEAR_ACTUATOR_10,
|
LINEAR_ACTUATOR_10,
|
||||||
LINEAR_ACTUATOR_21,
|
LINEAR_ACTUATOR_21,
|
||||||
LINEAR_ACTUATOR_50,
|
LINEAR_ACTUATOR_50,
|
||||||
ElectronicBoard
|
ElectronicBoard,
|
||||||
|
ELECTRONIC_MOUNT_HEXNUT,
|
||||||
)
|
)
|
||||||
import nhf.utils
|
import nhf.utils
|
||||||
|
|
||||||
|
@ -327,6 +328,9 @@ class WingProfile(Model):
|
||||||
)
|
)
|
||||||
@submodel(name="spacer-s0-electronic")
|
@submodel(name="spacer-s0-electronic")
|
||||||
def spacer_s0_electronic_mount(self) -> MountingBox:
|
def spacer_s0_electronic_mount(self) -> MountingBox:
|
||||||
|
"""
|
||||||
|
This one has circular holes for the screws
|
||||||
|
"""
|
||||||
return MountingBox(
|
return MountingBox(
|
||||||
holes=self.electronic_board.mount_holes,
|
holes=self.electronic_board.mount_holes,
|
||||||
hole_diam=self.electronic_board.mount_hole_diam,
|
hole_diam=self.electronic_board.mount_hole_diam,
|
||||||
|
@ -337,6 +341,26 @@ class WingProfile(Model):
|
||||||
flip_y=False,#self.flip,
|
flip_y=False,#self.flip,
|
||||||
generate_reverse_tags=True,
|
generate_reverse_tags=True,
|
||||||
)
|
)
|
||||||
|
@submodel(name="spacer-s0-electronic2")
|
||||||
|
def spacer_s0_electronic_mount2(self) -> MountingBox:
|
||||||
|
"""
|
||||||
|
This one has hexagonal holes
|
||||||
|
"""
|
||||||
|
face = ELECTRONIC_MOUNT_HEXNUT.cutting_face()
|
||||||
|
holes = [
|
||||||
|
Hole(x=h.x, y=h.y, face=face, tag=h.tag)
|
||||||
|
for h in self.electronic_board.mount_holes
|
||||||
|
]
|
||||||
|
return MountingBox(
|
||||||
|
holes=holes,
|
||||||
|
hole_diam=self.electronic_board.mount_hole_diam,
|
||||||
|
length=self.root_height,
|
||||||
|
width=self.electronic_board.width,
|
||||||
|
centred=(True, True),
|
||||||
|
thickness=self.spacer_thickness,
|
||||||
|
flip_y=False,#self.flip,
|
||||||
|
generate_reverse_tags=True,
|
||||||
|
)
|
||||||
@submodel(name="spacer-s0-shoulder-act")
|
@submodel(name="spacer-s0-shoulder-act")
|
||||||
def spacer_s0_shoulder_act(self) -> MountingBox:
|
def spacer_s0_shoulder_act(self) -> MountingBox:
|
||||||
dx = self.shoulder_joint.draft_height
|
dx = self.shoulder_joint.draft_height
|
||||||
|
@ -424,6 +448,16 @@ class WingProfile(Model):
|
||||||
.constrain(f"{tag}?{top_tag}", f"top?{tag}", "Plane")
|
.constrain(f"{tag}?{top_tag}", f"top?{tag}", "Plane")
|
||||||
.constrain(f"{tag}?dir", f"top?{tag}_dir", "Axis")
|
.constrain(f"{tag}?dir", f"top?{tag}_dir", "Axis")
|
||||||
)
|
)
|
||||||
|
result.addS(
|
||||||
|
self.spacer_s0_electronic_mount2().generate(),
|
||||||
|
name="electronic_mount2",
|
||||||
|
role=Role.STRUCTURE | Role.CONNECTION,
|
||||||
|
material=self.mat_bracket)
|
||||||
|
for hole in self.electronic_board.mount_holes:
|
||||||
|
result.constrain(
|
||||||
|
f"electronic_mount?{hole.tag}",
|
||||||
|
f"electronic_mount2?{hole.tag}_rev",
|
||||||
|
"Plane")
|
||||||
if not ignore_electronics:
|
if not ignore_electronics:
|
||||||
result.add(self.electronic_board.assembly(), name="electronic_board")
|
result.add(self.electronic_board.assembly(), name="electronic_board")
|
||||||
for hole in self.electronic_board.mount_holes:
|
for hole in self.electronic_board.mount_holes:
|
||||||
|
|
Loading…
Reference in New Issue