cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
2 changed files with 90 additions and 14 deletions
Showing only changes of commit 1f5a65c43f - Show all commits

View File

@ -238,9 +238,13 @@ class WingRoot:
""" """
panel_thickness: float = 25.4 / 16 panel_thickness: float = 25.4 / 16
spacer_thickness: float = 25.4 / 8
height: float = 100.0 height: float = 100.0
shoulder_width: float = 20.0 shoulder_width: float = 30.0
root_width: float = 60.0 root_width: float = 80.0
tip_x: float = -200.0
tip_y: float = 160.0
def outer_spline(self) -> list[Tuple[float, float]]: def outer_spline(self) -> list[Tuple[float, float]]:
""" """
@ -248,34 +252,81 @@ class WingRoot:
""" """
def profile(self) -> Cq.Sketch: def profile(self) -> Cq.Sketch:
tip_x, tip_y = -100.0, 70.0
sketch = ( sketch = (
Cq.Sketch() Cq.Sketch()
.segment((-self.root_width, 0), (0, 0)) .segment((-self.root_width, 0), (0, 0))
.spline([ .spline([
(0, 0), (0, 0),
(-30.0, 50.0), (-30.0, 80.0),
(tip_x, tip_y) (self.tip_x, self.tip_y)
]) ])
.segment( .segment(
(tip_x, tip_y), (self.tip_x, self.tip_y),
(tip_x, tip_y - self.shoulder_width) (self.tip_x, self.tip_y - self.shoulder_width)
) )
.segment( .segment(
(tip_x, tip_y - self.shoulder_width), (self.tip_x, self.tip_y - self.shoulder_width),
(-self.root_width, 0) (-self.root_width, 0)
) )
.assemble() .assemble()
) )
return sketch return sketch
def xy_surface(self) -> Cq.Workplane: def spacer(self) -> Cq.Workplane:
"""
Creates a rectangular spacer. This could be cut from acrylic.
return ( There are two holes on the top of the spacer. With the holes
Cq.Workplane() """
.placeSketch(self.profile()) length = self.height
.extrude(self.panel_thickness) width = 10.0
h = self.spacer_thickness
result = (
Cq.Workplane('XY')
.sketch()
.rect(length, width)
.finalize()
.extrude(h)
) )
# Tag the mating surfaces to be glued
result.faces("<X").workplane().tagPlane("left")
result.faces(">X").workplane().tagPlane("right")
# Tag the directrix
result.faces(">Z").tag("dir")
return result
def surface(self, top: bool = False) -> Cq.Workplane:
tags = [
("shoulder", (self.tip_x, self.tip_y + 30), 0),
("base", (-self.root_width, 0), 90),
]
return nhf.utils.extrude_with_markers(
self.profile(),
self.panel_thickness,
tags,
reverse=not top,
)
def assembly(self) -> Cq.Assembly:
result = (
Cq.Assembly()
.add(self.surface(top=True), name="bot")
.add(self.surface(top=False), name="top")
.constrain("bot@faces@>Z", "top@faces@<Z", "Point",
param=self.height)
)
for t in ["shoulder", "base"]:
name = f"{t}_spacer"
(
result
.add(self.spacer(), name=name)
.constrain(f"{name}?left", f"bot?{t}", "Plane")
.constrain(f"{name}?right", f"top?{t}", "Plane")
.constrain(f"{name}?dir", f"top?{t}_dir", "Axis")
)
return result.solve()
@dataclass @dataclass
class WingProfile: class WingProfile:

View File

@ -6,9 +6,34 @@ Adds the functions to `Cq.Workplane`:
2. `tagPlane` 2. `tagPlane`
""" """
import math import math
import functools
import cadquery as Cq import cadquery as Cq
from nhf import Role from nhf import Role
from typing import Union, Tuple from typing import Union, Tuple, cast
# Bug fixes
def _subloc(self, name: str) -> Tuple[Cq.Location, str]:
"""
Calculate relative location of an object in a subassembly.
Returns the relative positions as well as the name of the top assembly.
"""
rv = Cq.Location()
obj = self.objects[name]
name_out = name
if obj not in self.children and obj is not self:
locs = []
while not obj.parent is self:
locs.append(obj.loc)
obj = cast(Cq.Assembly, obj.parent)
name_out = obj.name
rv = functools.reduce(lambda l1, l2: l2 * l1, locs)
return (rv, name_out)
Cq.Assembly._subloc = _subloc
def tagPoint(self, tag: str): def tagPoint(self, tag: str):