2024-06-28 20:07:37 -07:00
|
|
|
"""
|
|
|
|
This file describes the shapes of the wing shells. The joints are defined in
|
|
|
|
`__init__.py`.
|
|
|
|
"""
|
2024-06-24 16:13:15 -07:00
|
|
|
import math
|
|
|
|
import cadquery as Cq
|
2024-06-28 20:07:37 -07:00
|
|
|
from nhf.joints import HirthJoint
|
2024-06-24 16:13:15 -07:00
|
|
|
|
|
|
|
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
|
2024-06-28 20:07:37 -07:00
|
|
|
|
|
|
|
|
|
|
|
def wing_root(joint: HirthJoint,
|
|
|
|
bolt_diam: int = 12):
|
|
|
|
"""
|
|
|
|
Generate the contiguous components of the root wing segment
|
|
|
|
"""
|
2024-06-24 16:13:15 -07:00
|
|
|
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,
|
2024-06-27 20:22:54 -07:00
|
|
|
angleDegrees = 30,
|
|
|
|
)
|
|
|
|
antetip_profile = tip_profile.rotate(
|
|
|
|
startVector=rotate_centre,
|
|
|
|
endVector=rotate_centre + rotate_axis,
|
|
|
|
angleDegrees = 60,
|
2024-06-24 16:13:15 -07:00
|
|
|
)
|
|
|
|
tip_profile = tip_profile.rotate(
|
|
|
|
startVector=rotate_centre,
|
|
|
|
endVector=rotate_centre + rotate_axis,
|
2024-06-27 20:22:54 -07:00
|
|
|
angleDegrees = 90,
|
2024-06-24 16:13:15 -07:00
|
|
|
)
|
|
|
|
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()
|
2024-06-27 20:22:54 -07:00
|
|
|
.add(antetip_profile)
|
|
|
|
.toPending()
|
|
|
|
.loft()
|
|
|
|
)
|
|
|
|
seg3 = (
|
|
|
|
Cq.Workplane('XY')
|
|
|
|
.add(antetip_profile)
|
|
|
|
.toPending()
|
|
|
|
.workplane()
|
2024-06-24 16:13:15 -07:00
|
|
|
.add(tip_profile)
|
|
|
|
.toPending()
|
|
|
|
.loft()
|
|
|
|
)
|
2024-06-27 20:22:54 -07:00
|
|
|
result = seg1.union(seg2).union(seg3)
|
|
|
|
result.faces("<Z").tag("base")
|
|
|
|
result.faces(">X").tag("conn")
|
2024-06-28 20:07:37 -07:00
|
|
|
|
|
|
|
j = (
|
|
|
|
joint.generate(is_mated=True)
|
|
|
|
.faces("<Z")
|
|
|
|
.hole(bolt_diam)
|
|
|
|
)
|
|
|
|
result = (
|
|
|
|
result
|
|
|
|
.union(j.translate((0, 0, -10)))
|
2024-06-30 19:03:16 -07:00
|
|
|
#.union(Cq.Solid.makeCylinder(57, 5).moved(Cq.Location((0, 0, -10))))
|
|
|
|
.union(Cq.Solid.makeCylinder(20, 5).moved(Cq.Location((0, 0, -10))))
|
2024-06-28 20:07:37 -07:00
|
|
|
.clean()
|
|
|
|
)
|
2024-06-27 20:22:54 -07:00
|
|
|
return result
|