Geometry of mirror and rotor spacer

This commit is contained in:
Leni Aniva 2025-05-13 14:29:10 -07:00
parent 5b5ccee94e
commit a684996475
Signed by: aniva
GPG Key ID: D5F96287843E8DFB
2 changed files with 224 additions and 68 deletions

View File

@ -1,6 +1,7 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
import cadquery as Cq import cadquery as Cq
from nhf.build import Model, TargetKind, target, assembly, submodel 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.touhou.yasaka_kanako.onbashira as MO
import nhf.utils import nhf.utils
@ -8,36 +9,178 @@ import nhf.utils
class Mirror(Model): class Mirror(Model):
""" """
Kanako's mirror, made of three levels. 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 = 50.0 width: float = 50.0
height: float = 70.0 height: float = 60.0
wall_thickness: float = 10.0 inner_gap: float = 3.0
outer_gap: float = 3.0
core_thickness: float = 25.4 / 8
casing_thickness: float = 25.4 / 16
flange_r0: float = 5.0
flange_r1: float = 15.0
flange_y1: float = 12.0
flange_y2: float = 25.0
flange_hole_r: float = 8.0
wing_x1: float = 12.0
wing_x2: float = 20.0
wing_r1: float = 6.0
wing_r2: float = 12.0
tail_r0: float = 5.0
tail_r1: float = 10.0
tail_y1: float = 12.0
tail_y2: float = 25.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) @target(name="core", kind=TargetKind.DXF)
def profile_core(self) -> Cq.Sketch: def profile_core(self) -> Cq.Sketch:
return Cq.Sketch().ellipse(self.width/2, self.height/2) 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) @target(name="casing-bot", kind=TargetKind.DXF)
def profile_casing(self) -> Cq.Sketch: def profile_casing_bot(self) -> Cq.Sketch:
""" """
Base of the casing with no holes carved out Base of the casing with no holes carved out
""" """
dx = self.wall_thickness
return ( return (
Cq.Sketch() Cq.Sketch()
.ellipse(self.width/2+dx, self.height/2+dx) .ellipse(self.width/2, self.height/2)
)
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:
yt = self.height / 2 - self.outer_gap
rx = self.width/2 - self.outer_gap
ry = 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 (
self.profile_casing_bot()
.ellipse(rx, ry, mode="s")
.boolean(flange, mode="a")
.boolean(tail, mode="a")
.boolean(self.profile_wing(-1), mode="a")
.boolean(self.profile_wing(1), mode="a")
)
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) @target(name="casing-top", kind=TargetKind.DXF)
def profile_casing_top(self) -> Cq.Sketch: def profile_casing_top(self) -> Cq.Sketch:
""" rx = self.width/2 - self.outer_gap - self.inner_gap
Base of the casing with no holes carved out ry = self.height/2 - self.outer_gap - self.inner_gap
"""
return ( return (
self.profile_casing() self.profile_casing_bot()
.ellipse(self.width/2, self.height/2, mode="s") .ellipse(rx, ry, mode="s")
)
def casing_top(self) -> Cq.Workplane:
return (
Cq.Workplane()
.placeSketch(self.profile_casing_top())
.extrude(self.casing_thickness)
) )
@assembly() @assembly()
def assembly(self) -> Cq.Assembly: def assembly(self) -> Cq.Assembly:
pass 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)
)
)

View File

@ -54,9 +54,9 @@ class Onbashira(Model):
bearing_disk_thickness: float = 25.4 / 8 bearing_disk_thickness: float = 25.4 / 8
rotor_inner_radius: float = 40.0 rotor_inner_radius: float = 40.0
rotor_bind_bolt_diam: float = 8.0 rotor_bind_bolt_diam: float = 8.0
rotor_bind_radius: float = 85.0 rotor_bind_radius: float = 85.0
rotor_spacer_outer_diam: float = 15.0
stator_bind_radius: float = 140.0 stator_bind_radius: float = 140.0
material_side: Material = Material.WOOD_BIRCH material_side: Material = Material.WOOD_BIRCH
@ -194,6 +194,74 @@ class Onbashira(Model):
""" """
pass pass
def bearing_ball(self) -> Cq.Solid:
return Cq.Solid.makeSphere(radius=self.bearing_ball_diam/2, angleDegrees1=-90)
@target(name="rotor-spacer")
def rotor_spacer(self) -> Cq.Solid:
outer = Cq.Solid.makeCylinder(
radius=self.rotor_spacer_outer_diam/2,
height=self.bearing_disk_gap,
)
inner = Cq.Solid.makeCylinder(
radius=self.rotor_bind_bolt_diam/2,
height=self.bearing_disk_gap
)
return outer - inner
def assembly_rotor(self) -> Cq.Assembly:
z_lower = -self.bearing_disk_gap/2 - self.bearing_disk_thickness
a = (
Cq.Assembly()
.addS(
self.bearing_stator(),
name="stator1",
material=self.material_bearing,
role=Role.STATOR,
loc=Cq.Location(0, 0, self.bearing_disk_gap/2)
)
.addS(
self.bearing_rotor(),
name="rotor1",
material=self.material_bearing,
role=Role.ROTOR,
loc=Cq.Location(0, 0, self.bearing_disk_gap/2)
)
.addS(
self.bearing_stator(),
name="stator2",
material=self.material_bearing,
role=Role.STATOR,
loc=Cq.Location(0, 0, z_lower)
)
.addS(
self.bearing_rotor(),
name="rotor2",
material=self.material_bearing,
role=Role.ROTOR,
loc=Cq.Location(0, 0, z_lower)
)
.addS(
self.bearing_gasket(),
name="gasket",
material=self.material_bearing,
role=Role.ROTOR,
loc=Cq.Location(0, 0, -self.bearing_disk_thickness/2)
)
)
for i in range(self.n_bearing_balls):
ball = self.bearing_ball()
loc = Cq.Location.rot2d(i * 360/self.n_bearing_balls) * Cq.Location(self.bearing_track_radius, 0, 0)
a = a.addS(
ball,
name=f"bearing_ball{i}",
material=self.material_bearing_ball,
role=Role.BEARING,
loc=loc,
)
return a
def profile_side_panel( def profile_side_panel(
self, self,
length: float, length: float,
@ -394,61 +462,6 @@ class Onbashira(Model):
result.tagAbsolute("holeStatorR", (ri * math.cos(th), ri * math.sin(th), -h/2), direction="-Z") result.tagAbsolute("holeStatorR", (ri * math.cos(th), ri * math.sin(th), -h/2), direction="-Z")
return result return result
def bearing_ball(self) -> Cq.Solid:
return Cq.Solid.makeSphere(radius=self.bearing_ball_diam/2, angleDegrees1=-90)
def assembly_rotor(self) -> Cq.Assembly:
z_lower = -self.bearing_disk_gap/2 - self.bearing_disk_thickness
a = (
Cq.Assembly()
.addS(
self.bearing_stator(),
name="stator1",
material=self.material_bearing,
role=Role.STATOR,
loc=Cq.Location(0, 0, self.bearing_disk_gap/2)
)
.addS(
self.bearing_rotor(),
name="rotor1",
material=self.material_bearing,
role=Role.ROTOR,
loc=Cq.Location(0, 0, self.bearing_disk_gap/2)
)
.addS(
self.bearing_stator(),
name="stator2",
material=self.material_bearing,
role=Role.STATOR,
loc=Cq.Location(0, 0, z_lower)
)
.addS(
self.bearing_rotor(),
name="rotor2",
material=self.material_bearing,
role=Role.ROTOR,
loc=Cq.Location(0, 0, z_lower)
)
.addS(
self.bearing_gasket(),
name="gasket",
material=self.material_bearing,
role=Role.ROTOR,
loc=Cq.Location(0, 0, -self.bearing_disk_thickness/2)
)
)
for i in range(self.n_bearing_balls):
ball = self.bearing_ball()
loc = Cq.Location.rot2d(i * 360/self.n_bearing_balls) * Cq.Location(self.bearing_track_radius, 0, 0)
a = a.addS(
ball,
name=f"bearing_ball{i}",
material=self.material_bearing_ball,
role=Role.BEARING,
loc=loc,
)
return a
def assembly_section(self, **kwargs) -> Cq.Assembly: def assembly_section(self, **kwargs) -> Cq.Assembly:
a = Cq.Assembly() a = Cq.Assembly()
side = self.side_panel(**kwargs) side = self.side_panel(**kwargs)