Cosplay/nhf/touhou/houjuu_nue/wing.py

179 lines
4.6 KiB
Python

import math
import cadquery as Cq
def wing_root_profiles(
base_sweep=150,
wall_thickness=8,
base_radius=60,
middle_offset=30,
conn_width=40,
conn_height=100) -> tuple[Cq.Wire, Cq.Wire]:
assert base_sweep < 180
assert middle_offset > 0
theta = math.pi * base_sweep / 180
c, s = math.cos(theta), math.sin(theta)
c_1, s_1 = math.cos(theta * 0.75), math.sin(theta * 0.75)
c_2, s_2 = math.cos(theta / 2), math.sin(theta / 2)
r1 = base_radius
r2 = base_radius - wall_thickness
base = (
Cq.Sketch()
.arc(
(c * r1, s * r1),
(c_1 * r1, s_1 * r1),
(c_2 * r1, s_2 * r1),
)
.arc(
(c_2 * r1, s_2 * r1),
(r1, 0),
(c_2 * r1, -s_2 * r1),
)
.arc(
(c_2 * r1, -s_2 * r1),
(c_1 * r1, -s_1 * r1),
(c * r1, -s * r1),
)
.segment(
(c * r1, -s * r1),
(c * r2, -s * r2),
)
.arc(
(c * r2, -s * r2),
(c_1 * r2, -s_1 * r2),
(c_2 * r2, -s_2 * r2),
)
.arc(
(c_2 * r2, -s_2 * r2),
(r2, 0),
(c_2 * r2, s_2 * r2),
)
.arc(
(c_2 * r2, s_2 * r2),
(c_1 * r2, s_1 * r2),
(c * r2, s * r2),
)
.segment(
(c * r2, s * r2),
(c * r1, s * r1),
)
.assemble(tag="wire")
.wires().val()
)
assert isinstance(base, Cq.Wire)
# The interior sweep is given by theta, but the exterior sweep exceeds the
# interior sweep so the wall does not become thinner towards the edges.
# If the exterior sweep is theta', it has to satisfy
#
# sin(theta) * r2 + wall_thickness = sin(theta') * r1
x, y = conn_width / 2, conn_height / 2
t = wall_thickness
dx = middle_offset
middle = (
Cq.Sketch()
# Interior arc, top point
.arc(
(x - t, y - t),
(x - t + dx, 0),
(x - t, -y + t),
)
.segment(
(x - t, -y + t),
(-x, -y+t)
)
.segment((-x, -y))
.segment((x, -y))
# Outer arc, bottom point
.arc(
(x, -y),
(x + dx, 0),
(x, y),
)
.segment(
(x, y),
(-x, y)
)
.segment((-x, y-t))
#.segment((x2, a))
.close()
.assemble(tag="wire")
.wires().val()
)
assert isinstance(middle, Cq.Wire)
x, y = conn_width / 2, conn_height / 2
t = wall_thickness
tip = (
Cq.Sketch()
.segment((-x, y), (x, y))
.segment((x, -y))
.segment((-x, -y))
.segment((-x, -y+t))
.segment((x-t, -y+t))
.segment((x-t, y-t))
.segment((-x, y-t))
.close()
.assemble(tag="wire")
.wires().val()
)
return base, middle, tip
def wing_root():
root_profile, middle_profile, tip_profile = wing_root_profiles()
rotate_centre = Cq.Vector(-200, 0, -25)
rotate_axis = Cq.Vector(0, 1, 0)
terminal_offset = Cq.Vector(-80, 0, 80)
terminal_rotate = Cq.Vector(0, -45, 0)
#middle_profile = middle_profile.moved(Cq.Location((0, 0, -100)))
#tip_profile = tip_profile.moved(Cq.Location((0, 0, -200)))
middle_profile = middle_profile.rotate(
startVector=rotate_centre,
endVector=rotate_centre + rotate_axis,
angleDegrees = 30,
)
antetip_profile = tip_profile.rotate(
startVector=rotate_centre,
endVector=rotate_centre + rotate_axis,
angleDegrees = 60,
)
tip_profile = tip_profile.rotate(
startVector=rotate_centre,
endVector=rotate_centre + rotate_axis,
angleDegrees = 90,
)
seg1 = (
Cq.Workplane('XY')
.add(root_profile)
.toPending()
.transformed(
offset=terminal_offset,
rotate=terminal_rotate)
#.add(middle_profile.moved(Cq.Location((-15, 0, 15))))
.add(middle_profile)
.toPending()
.loft()
)
seg2 = (
Cq.Workplane('XY')
.add(middle_profile)
.toPending()
.workplane()
.add(antetip_profile)
.toPending()
.loft()
)
seg3 = (
Cq.Workplane('XY')
.add(antetip_profile)
.toPending()
.workplane()
.add(tip_profile)
.toPending()
.loft()
)
result = seg1.union(seg2).union(seg3)
result.faces("<Z").tag("base")
result.faces(">X").tag("conn")
return result