""" This file describes the shapes of the wing shells. The joints are defined in `__init__.py`. """ import math import cadquery as Cq from nhf import Material, Role from nhf.parts.joints import HirthJoint import nhf.utils def wing_root_profiles( base_sweep=150, wall_thickness=8, base_radius=40, middle_offset=30, middle_height=80, conn_thickness=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_thickness / 2, middle_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_thickness / 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(joint: HirthJoint, bolt_diam: int = 12, union_tol=1e-4, shoulder_attach_diam=8, shoulder_attach_dist=25, conn_thickness=40, conn_height=100, wall_thickness=8) -> Cq.Assembly: """ Generate the contiguous components of the root wing segment """ tip_centre = Cq.Vector((-150, 0, -80)) attach_theta = math.radians(5) c, s = math.cos(attach_theta), math.sin(attach_theta) attach_points = [ (15, 4), (15 + shoulder_attach_dist * c, 4 + shoulder_attach_dist * s), ] root_profile, middle_profile, tip_profile = wing_root_profiles( conn_thickness=conn_thickness, conn_height=conn_height, wall_thickness=8, ) middle_profile = middle_profile.located(Cq.Location( (-40, 0, -40), (0, 1, 0), 30 )) antetip_profile = tip_profile.located(Cq.Location( (-95, 0, -75), (0, 1, 0), 60 )) tip_profile = tip_profile.located(Cq.Location( tip_centre, (0, 1, 0), 90 )) profiles = [ root_profile, middle_profile, antetip_profile, tip_profile, ] result = None for p1, p2 in zip(profiles[:-1], profiles[1:]): seg = ( Cq.Workplane('XY') .add(p1) .toPending() .workplane() # This call is necessary .add(p2) .toPending() .loft() ) if result: result = result.union(seg, tol=union_tol) else: result = seg result = ( result # Create connector holes .copyWorkplane( Cq.Workplane('bottom', origin=tip_centre + Cq.Vector((0, -50, 0))) ) .pushPoints(attach_points) .hole(shoulder_attach_diam) ) # Generate attach point tags for sign in [False, True]: y = conn_height / 2 - wall_thickness side = "bottom" if sign else "top" y = y if sign else -y plane = ( result # Create connector holes .copyWorkplane( Cq.Workplane(side, origin=tip_centre + Cq.Vector((0, y, 0))) ) ) if side == "bottom": side = "bot" for i, (px, py) in enumerate(attach_points): tag = f"conn_{side}{i}" plane.moveTo(px, -py if side == "top" else py).tagPlane(tag) result.faces("X").tag("conn") j = ( joint.generate(is_mated=True) .faces(" 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