Compare commits

..

26 Commits

Author SHA1 Message Date
Leni Aniva bd7e8677c7
Make all angle joints flanged 2025-05-16 07:15:30 -07:00
Leni Aniva c5f9e570a6
Fit onbashira profile in 12x12 in panel 2025-05-15 23:16:39 -07:00
Leni Aniva 63c2c74e02
Barrel position solver 2025-05-15 21:20:22 -07:00
Leni Aniva 0fb88a97d3
Chamber connectors 2025-05-15 20:55:17 -07:00
Leni Aniva 4edad88299
Larger mirror dimensions 2025-05-15 13:23:52 -07:00
Leni Aniva 83d4232ad7
Add holes for gohei 2025-05-14 23:21:13 -07:00
Leni Aniva 4d4e4c7eab
Mating structure for angle joint 2025-05-14 23:08:47 -07:00
Leni Aniva b83bf5a57d
Use only one bolt for angle bracket 2025-05-14 22:40:53 -07:00
Leni Aniva 670d4a8c21
Improve geometry of angle joint 2025-05-14 13:13:44 -07:00
Leni Aniva 22a4f4ceec
Mirror wing geometry 2025-05-13 17:20:51 -07:00
Leni Aniva a684996475
Geometry of mirror and rotor spacer 2025-05-13 14:29:10 -07:00
Leni Aniva 5b5ccee94e
Optimize angle joint geometry; Mirror stub 2025-05-13 09:11:28 -07:00
Leni Aniva b88d52f4be
Use 8mm bolt 2025-05-13 00:02:43 -07:00
Leni Aniva ca606c6bc1
Fix rotation radius 2025-05-12 23:59:34 -07:00
Leni Aniva 916ccee260
Centre holes 2025-05-12 23:52:45 -07:00
Leni Aniva 44cd6ee960
Onbashira dimension update and flanges 2025-05-12 23:33:20 -07:00
Leni Aniva 4dcd97613b
Section bracing 2025-05-12 22:08:44 -07:00
Leni Aniva 97675a2fc8
Add angle joint stub, hole in rotor 2025-05-12 14:50:59 -07:00
Leni Aniva 74145f88d2
Add bolts on rotor 2025-05-12 12:24:33 -07:00
Leni Aniva 878d532890
Use rotor-stator configuration for bearing 2025-05-09 16:58:14 -04:00
Leni Aniva 7511efa9ee
Add Kanako set class 2025-04-22 11:16:44 -07:00
Leni Aniva 3e0eab0cec
Merge branch 'main' into touhou/yasaka-kanako 2025-04-20 12:49:17 -07:00
Leni Aniva b15db172a0
Merge branch 'main' into touhou/yasaka-kanako 2025-04-08 23:28:30 -07:00
Leni Aniva a74f919a5b
Onbashira rotor-stator mechanism 2025-02-25 21:04:25 -08:00
Leni Aniva f4704b9ad6
fix: Remove shebang in init 2025-02-24 00:54:39 -08:00
Leni Aniva 590033e492
Kanako onbashira barrel 2025-02-24 00:21:31 -08:00
6 changed files with 1316 additions and 0 deletions

View File

@ -3,6 +3,9 @@
This is the design repository for NorCal Hakkero Factory No. 1, where we use
parametric CAD to make cosplay props.
> NorCal Hakkero Factory № 1
> 北加国営八卦炉第一工場
## Development
Most cosplay schematics are created with Blender, CadQuery, and Inkscape. To

View File

@ -25,6 +25,9 @@ class Role(Flag):
PARENT = auto()
CHILD = auto()
CASING = auto()
STATOR = auto()
ROTOR = auto()
BEARING = auto()
# Springs, cushions
DAMPING = auto()
# Main structural support
@ -59,6 +62,9 @@ ROLE_COLOR_MAP = {
Role.PARENT: _color('blue4', 0.6),
Role.CASING: _color('dodgerblue3', 0.6),
Role.CHILD: _color('darkorange2', 0.6),
Role.STATOR: _color('gray', 0.5),
Role.ROTOR: _color('blue3', 0.5),
Role.BEARING: _color('green3', 0.8),
Role.DAMPING: _color('springgreen', 1.0),
Role.STRUCTURE: _color('gray', 0.4),
Role.DECORATION: _color('lightseagreen', 0.4),
@ -84,6 +90,7 @@ class Material(Enum):
ACRYLIC_TRANSLUSCENT = 1.18, _color('ivory2', 0.8)
ACRYLIC_TRANSPARENT = 1.18, _color('ghostwhite', 0.5)
STEEL_SPRING = 7.8, _color('gray', 0.8)
METAL_AL = 2.7, _color('gray', 0.6)
METAL_BRASS = 8.5, _color('gold1', 0.8)
def __init__(self, density: float, color: Cq.Color):

View File

@ -0,0 +1,31 @@
from dataclasses import dataclass, field
import cadquery as Cq
from nhf.build import Model, TargetKind, target, assembly, submodel
import nhf.touhou.yasaka_kanako.onbashira as MO
import nhf.touhou.yasaka_kanako.mirror as MM
import nhf.utils
@dataclass
class Parameters(Model):
onbashira: MO.Onbashira = field(default_factory=lambda: MO.Onbashira())
mirror: MM.Mirror = field(default_factory=lambda: MM.Mirror())
def __post_init__(self):
super().__init__(name="yasaka-kanako")
@submodel(name="onbashira")
def submodel_onbashira(self) -> Model:
return self.onbashira
@submodel(name="mirror")
def submodel_mirror(self) -> Model:
return self.mirror
if __name__ == '__main__':
import sys
p = Parameters()
if len(sys.argv) == 1:
p.build_all()
sys.exit(0)

View File

@ -0,0 +1,186 @@
from dataclasses import dataclass, field
import cadquery as Cq
from nhf.build import Model, TargetKind, target, assembly, submodel
from nhf.materials import Role, Material
import nhf.touhou.yasaka_kanako.onbashira as MO
import nhf.utils
@dataclass
class Mirror(Model):
"""
Kanako's mirror, made of three levels.
The mirror suface is sandwiched between two layers of wood. As such, its
dimensions have to sit in between that of the aperature on the surface, and
the outer walls. The width/height here refers to the outer edge's width and height
"""
width: float = 100.0
height: float = 120.0
inner_gap: float = 3.0
outer_gap: float = 3.0
core_thickness: float = 25.4 / 8
casing_thickness: float = 25.4 / 8
flange_r0: float = 8.0
flange_r1: float = 20.0
flange_y1: float = 12.0
flange_y2: float = 25.0
flange_hole_r: float = 8.0
wing_x1: float = 15.0
wing_x2: float = 24.0
wing_r1: float = 10.0
wing_r2: float = 16.0
tail_r0: float = 8.0
tail_r1: float = 13.0
tail_y1: float = 16.0
tail_y2: float = 29.0
# Necklace hole
hole_diam: float = 5.0
material_mirror: Material = Material.ACRYLIC_TRANSPARENT
material_casing: Material = Material.WOOD_BIRCH
@target(name="core", kind=TargetKind.DXF)
def profile_core(self) -> Cq.Sketch:
rx = self.width/2 - self.outer_gap
ry = self.height/2 - self.outer_gap
return Cq.Sketch().ellipse(rx, ry)
def core(self) -> Cq.Workplane:
return (
Cq.Workplane()
.placeSketch(self.profile_core())
.extrude(self.core_thickness)
)
@target(name="casing-bot", kind=TargetKind.DXF)
def profile_casing_bot(self) -> Cq.Sketch:
"""
Base of the casing with no holes carved out
"""
yt = self.height / 2 - self.outer_gap
yh = (self.flange_y1 + self.flange_y2) / 2
flange = (
Cq.Sketch()
.polygon([
(self.flange_r0, yt),
(self.flange_r0, yt + self.flange_y1),
(self.flange_r1, yt + self.flange_y1),
(self.flange_r1, yt + self.flange_y2),
(-self.flange_r1, yt + self.flange_y2),
(-self.flange_r1, yt + self.flange_y1),
(-self.flange_r0, yt + self.flange_y1),
(-self.flange_r0, yt),
])
.push([
(self.flange_hole_r, yt+yh),
(-self.flange_hole_r, yt+yh),
])
.circle(self.hole_diam/2, mode="s")
)
tail = (
Cq.Sketch()
.polygon([
(+self.tail_r0, -yt),
(+self.tail_r0, -yt - self.tail_y1),
(+self.tail_r1, -yt - self.tail_y1),
(+self.tail_r1, -yt - self.tail_y2),
(-self.tail_r1, -yt - self.tail_y2),
(-self.tail_r1, -yt - self.tail_y1),
(-self.tail_r0, -yt - self.tail_y1),
(-self.tail_r0, -yt),
])
)
return (
Cq.Sketch()
.ellipse(self.width/2, self.height/2)
.boolean(flange, mode="a")
.boolean(tail, mode="a")
.boolean(self.profile_wing(-1), mode="a")
.boolean(self.profile_wing(1), mode="a")
)
def casing_bot(self) -> Cq.Workplane:
return (
Cq.Workplane()
.placeSketch(self.profile_casing_bot())
.extrude(self.casing_thickness)
)
def profile_wing(self, sign: float=1) -> Cq.Sketch:
xt = self.width / 2 - self.outer_gap
return (
Cq.Sketch()
.polygon([
(sign*xt, self.wing_r1),
(sign*(xt+self.wing_x1), self.wing_r1),
(sign*(xt+self.wing_x1), self.wing_r2),
(sign*(xt+self.wing_x2), self.wing_r2),
(sign*(xt+self.wing_x2), -self.wing_r2),
(sign*(xt+self.wing_x1), -self.wing_r2),
(sign*(xt+self.wing_x1), -self.wing_r1),
(sign*xt, -self.wing_r1),
])
)
@target(name="casing-mid", kind=TargetKind.DXF)
def profile_casing_mid(self) -> Cq.Sketch:
rx = self.width/2 - self.outer_gap
ry = self.height/2 - self.outer_gap
return (
self.profile_casing_bot()
.ellipse(rx, ry, mode="s")
)
def casing_mid(self) -> Cq.Workplane:
return (
Cq.Workplane()
.placeSketch(self.profile_casing_mid())
.extrude(self.core_thickness)
)
@target(name="casing-top", kind=TargetKind.DXF)
def profile_casing_top(self) -> Cq.Sketch:
rx = self.width/2 - self.outer_gap - self.inner_gap
ry = self.height/2 - self.outer_gap - self.inner_gap
return (
self.profile_casing_bot()
.ellipse(rx, ry, mode="s")
)
def casing_top(self) -> Cq.Workplane:
return (
Cq.Workplane()
.placeSketch(self.profile_casing_top())
.extrude(self.casing_thickness)
)
@assembly()
def assembly(self) -> Cq.Assembly:
return (
Cq.Assembly()
.addS(
self.core(),
name="core",
material=self.material_mirror,
role=Role.DECORATION,
loc=Cq.Location(0, 0, self.casing_thickness)
)
.addS(
self.casing_bot(),
name="casing_bot",
material=self.material_casing,
role=Role.CASING,
)
.addS(
self.casing_mid(),
name="casing_mid",
material=self.material_casing,
role=Role.CASING,
loc=Cq.Location(0, 0, self.casing_thickness)
)
.addS(
self.casing_top(),
name="casing_top",
material=self.material_casing,
role=Role.CASING,
loc=Cq.Location(0, 0, self.core_thickness + self.casing_thickness)
)
)

File diff suppressed because it is too large Load Diff

View File

@ -162,6 +162,15 @@ def tagPlane(self, tag: str,
Cq.Workplane.tagPlane = tagPlane
def tag_absolute(
self,
tag: str,
loc: Union[Cq.Location, Tuple[float, float, float]],
direction: Union[str, Cq.Vector, Tuple[float, float, float]] = '+Z'):
return self.pushPoints([loc]).tagPlane(tag, direction=direction)
Cq.Workplane.tagAbsolute = tag_absolute
def make_sphere(r: float = 2) -> Cq.Solid:
"""
Makes a full sphere. The default function makes a hemisphere