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_base(): 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 = 35, ) tip_profile = tip_profile.rotate( startVector=rotate_centre, endVector=rotate_centre + rotate_axis, angleDegrees = 70, ) 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(tip_profile) .toPending() .loft() ) seg1 = seg1.union(seg2) return seg1