cosplay: Touhou/Houjuu Nue #4
|
@ -33,6 +33,7 @@ class Material(Enum):
|
|||
PLASTIC_PLA = 0.5, _color('azure3', 0.6)
|
||||
RESIN_TRANSPARENT = 1.1, _color('cadetblue2', 0.6)
|
||||
ACRYLIC_BLACK = 0.5, _color('gray50', 0.6)
|
||||
ACRYLIC_TRANSPARENT = 0.5, _color('ghostwhite', 0.5)
|
||||
|
||||
def __init__(self, density: float, color: Cq.Color):
|
||||
self.density = density
|
||||
|
|
|
@ -332,6 +332,7 @@ class TorsionJoint:
|
|||
)
|
||||
|
||||
def track(self):
|
||||
# TODO: Cover outer part of track only. Can we do this?
|
||||
groove_profile = (
|
||||
Cq.Sketch()
|
||||
.circle(self.radius)
|
||||
|
|
|
@ -104,12 +104,16 @@ class Parameters(Model):
|
|||
shoulder_attach_diam: float = 8
|
||||
|
||||
"""
|
||||
Heights for various wing joints, where the numbers start from the first joint.
|
||||
Heights for various wing joints, where the numbers start from the first
|
||||
joint.
|
||||
"""
|
||||
wing_s0_thickness: float = 40
|
||||
wing_s0_height: float = 100
|
||||
wing_r1_height: float = 100
|
||||
wing_r1_width: float = 400
|
||||
|
||||
# Length of the spacer
|
||||
wing_s1_thickness: float = 20
|
||||
wing_s1_spacer_thickness: float = 25.4 / 8
|
||||
wing_s1_spacer_width: float = 20
|
||||
|
||||
trident_handle: Handle = field(default_factory=lambda: Handle(
|
||||
diam=38,
|
||||
|
@ -121,6 +125,9 @@ class Parameters(Model):
|
|||
simplify_geometry=False,
|
||||
))
|
||||
|
||||
material_panel: Material = Material.ACRYLIC_TRANSPARENT
|
||||
material_bracket: Material = Material.ACRYLIC_TRANSPARENT
|
||||
|
||||
def __post_init__(self):
|
||||
super().__init__(name="houjuu-nue")
|
||||
assert self.wing_root_radius > self.hs_hirth_joint.radius,\
|
||||
|
@ -258,6 +265,10 @@ class Parameters(Model):
|
|||
result.faces("<Z").tag("base")
|
||||
return result
|
||||
|
||||
#@target(name="wing/joining-plate", kind=TargetKind.DXF)
|
||||
#def joining_plate(self) -> Cq.Workplane:
|
||||
# return self.wing_joining_plate.plate()
|
||||
|
||||
@target(name="wing_root")
|
||||
def wing_root(self) -> Cq.Assembly:
|
||||
"""
|
||||
|
@ -312,43 +323,42 @@ class Parameters(Model):
|
|||
)
|
||||
return result
|
||||
|
||||
def wing_r1_profile(self) -> Cq.Sketch:
|
||||
"""
|
||||
Generates the first wing segment profile, with the wing root pointing in
|
||||
the positive x axis.
|
||||
"""
|
||||
# Depression of the wing middle
|
||||
bend = 200
|
||||
factor = 0.7
|
||||
@target(name="wing/s1-spacer", kind=TargetKind.DXF)
|
||||
def wing_s1_spacer(self) -> Cq.Workplane:
|
||||
result = (
|
||||
Cq.Sketch()
|
||||
.segment((0, 0), (0, self.wing_r1_height))
|
||||
.spline([
|
||||
(0, self.wing_r1_height),
|
||||
(0.5 * self.wing_r1_width, self.wing_r1_height - factor * bend),
|
||||
(self.wing_r1_width, self.wing_r1_height - bend),
|
||||
])
|
||||
.segment(
|
||||
(self.wing_r1_width, self.wing_r1_height - bend),
|
||||
(self.wing_r1_width, -bend),
|
||||
)
|
||||
.spline([
|
||||
(self.wing_r1_width, - bend),
|
||||
(0.5 * self.wing_r1_width, - factor * bend),
|
||||
(0, 0),
|
||||
])
|
||||
.assemble()
|
||||
Cq.Workplane('XZ')
|
||||
.sketch()
|
||||
.rect(self.wing_s1_spacer_width, self.wing_s1_thickness)
|
||||
.finalize()
|
||||
.extrude(self.wing_s1_spacer_thickness)
|
||||
)
|
||||
result.faces("<Z").tag("mate1")
|
||||
result.faces(">Z").tag("mate2")
|
||||
result.faces(">Y").tag("dir")
|
||||
return result
|
||||
|
||||
def wing_r1(self) -> Cq.Solid:
|
||||
profile = self.wing_r1_profile()
|
||||
@target(name="wing/r1s1", kind=TargetKind.DXF)
|
||||
def wing_r1s1_profile(self) -> Cq.Sketch:
|
||||
return MW.wing_r1s1_profile()
|
||||
|
||||
def wing_r1s1_panel(self, front=True) -> Cq.Workplane:
|
||||
profile = self.wing_r1s1_profile()
|
||||
anchors = [
|
||||
("shoulder_bot", 10, 10),
|
||||
("middle", 50, -20),
|
||||
("tip", 390, -150),
|
||||
]
|
||||
result = (
|
||||
Cq.Workplane("XY")
|
||||
.placeSketch(profile)
|
||||
.extrude(self.panel_thickness)
|
||||
.val()
|
||||
)
|
||||
plane = result.faces(">Z" if front else "<Z").workplane()
|
||||
sign = 1 if front else -1
|
||||
tag_direction = "+Y" if front else "-Y"
|
||||
for name, px, py in anchors:
|
||||
plane.moveTo(px, sign * py).tagPlane(name)
|
||||
#plane.moveTo(px, sign * py).tagPlane(f"{name}_dir", direction=tag_direction)
|
||||
return result
|
||||
|
||||
######################
|
||||
|
@ -405,6 +415,29 @@ class Parameters(Model):
|
|||
)
|
||||
return result
|
||||
|
||||
def wing_r1s1_assembly(self) -> Cq.Assembly:
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
.add(self.wing_r1s1_panel(front=True), name="panel_front",
|
||||
color=self.material_panel.color)
|
||||
.add(self.wing_r1s1_panel(front=False), name="panel_back",
|
||||
color=self.material_panel.color)
|
||||
.constrain("panel_front@faces@>Z", "panel_back@faces@<Z", "Point",
|
||||
param=self.wing_s1_thickness)
|
||||
)
|
||||
for tag in ["shoulder_bot", "middle", "tip"]:
|
||||
(
|
||||
result
|
||||
.add(self.wing_s1_spacer(), name=f"{tag}_spacer",
|
||||
color=self.material_bracket.color)
|
||||
.constrain(f"panel_front?{tag}", f"{tag}_spacer?mate1", "Plane")
|
||||
.constrain(f"panel_back?{tag}", f"{tag}_spacer?mate2", "Plane")
|
||||
)
|
||||
|
||||
result.solve()
|
||||
return result
|
||||
|
||||
|
||||
def wing_r1_assembly(self) -> Cq.Assembly:
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
|
|
|
@ -226,3 +226,34 @@ def wing_root(joint: HirthJoint,
|
|||
loc=Cq.Location((0, 0, -joint.total_height)))
|
||||
)
|
||||
return result
|
||||
|
||||
def wing_r1s1_profile() -> Cq.Sketch:
|
||||
"""
|
||||
Generates the first wing segment profile, with the wing root pointing in
|
||||
the positive x axis.
|
||||
"""
|
||||
# Depression of the wing middle
|
||||
h = 100
|
||||
w = 400
|
||||
bend = 200
|
||||
factor = 0.7
|
||||
result = (
|
||||
Cq.Sketch()
|
||||
.segment((0, 0), (0, h))
|
||||
.spline([
|
||||
(0, h),
|
||||
(0.5 * w, h - factor * bend),
|
||||
(w, h - bend),
|
||||
])
|
||||
.segment(
|
||||
(w, h - bend),
|
||||
(w, -bend),
|
||||
)
|
||||
.spline([
|
||||
(w, - bend),
|
||||
(0.5 * w, - factor * bend),
|
||||
(0, 0),
|
||||
])
|
||||
.assemble()
|
||||
)
|
||||
return result
|
||||
|
|
34
nhf/utils.py
34
nhf/utils.py
|
@ -6,6 +6,7 @@ Adds the functions to `Cq.Workplane`:
|
|||
2. `tagPlane`
|
||||
"""
|
||||
import cadquery as Cq
|
||||
from typing import Union, Tuple
|
||||
|
||||
|
||||
def tagPoint(self, tag: str):
|
||||
|
@ -18,21 +19,34 @@ def tagPoint(self, tag: str):
|
|||
Cq.Workplane.tagPoint = tagPoint
|
||||
|
||||
|
||||
def tagPlane(self, tag: str, axis='Z'):
|
||||
def tagPlane(self, tag: str,
|
||||
direction: Union[str, Cq.Vector, Tuple[float, float, float]] = '+Z'):
|
||||
"""
|
||||
Adds a phantom `Cq.Edge` in the given location which can be referenced in a
|
||||
`Axis`, `Point`, or `Plane` constraint.
|
||||
"""
|
||||
x, y, z = 0, 0, 0
|
||||
if axis in ('z', 'Z'):
|
||||
z = 1
|
||||
elif axis in ('y', 'Y'):
|
||||
y = 1
|
||||
elif axis in ('x', 'X'):
|
||||
x = 1
|
||||
if isinstance(direction, str):
|
||||
x, y, z = 0, 0, 0
|
||||
assert len(direction) == 2
|
||||
sign, axis = direction
|
||||
if axis in ('z', 'Z'):
|
||||
z = 1
|
||||
elif axis in ('y', 'Y'):
|
||||
y = 1
|
||||
elif axis in ('x', 'X'):
|
||||
x = 1
|
||||
else:
|
||||
assert False, "Axis must be one of x,y,z"
|
||||
if sign == '+':
|
||||
sign = 1
|
||||
elif sign == '-':
|
||||
sign = -1
|
||||
else:
|
||||
assert False, "Sign must be one of +/-"
|
||||
v = Cq.Vector(x, y, z) * sign
|
||||
else:
|
||||
assert False, "Axis must be one of x,y,z"
|
||||
edge = Cq.Edge.makeLine((-x, -y, -z), (x, y, z))
|
||||
v = Cq.Vector(direction)
|
||||
edge = Cq.Edge.makeLine(v * (-1), v)
|
||||
self.eachpoint(edge.moved, useLocalCoordinates=True).tag(tag)
|
||||
|
||||
Cq.Workplane.tagPlane = tagPlane
|
||||
|
|
Loading…
Reference in New Issue