""" 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 def wing_root_profiles( base_sweep=150, wall_thickness=8, base_radius=40, middle_offset=30, middle_height=80, 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, 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_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(joint: HirthJoint, bolt_diam: int = 12, union_tol=1e-4, attach_diam=8, conn_width=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_points = [ (15, 0), (40, 0), ] root_profile, middle_profile, tip_profile = wing_root_profiles( conn_width=conn_width, conn_height=conn_height, wall_thickness=8, ) middle_profile = middle_profile.located(Cq.Location( (-40, 0, -40), (0, 30, 0) )) antetip_profile = tip_profile.located(Cq.Location( (-95, 0, -75), (0, 60, 0) )) tip_profile = tip_profile.located(Cq.Location( tip_centre, (0, 90, 0) )) 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(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))) ) ) for i, (px, py) in enumerate(attach_points): ( plane .moveTo(px, py) .eachpoint(Cq.Vertex.makeVertex(0, 0, 0)) .tag(f"conn_{side}{i}") ) result.faces("X").tag("conn") j = ( joint.generate(is_mated=True) .faces("