# Grouper: everything on the hull but the collar and the doors (exec'd by build_grouper.py after gr_clamp.py). Numbers are the # sheet's pixels from gr_measure.py unless in metres; SIDE rules every x and height, TOP gives widths, FRONT/BACK what SIDE # cannot show. Quads only (lofts and revolves with Coons caps, tubes, plates over outlines); parts that sink into the hull are # pruned by the finish pass (faces wholly deeper than 6 cm under the skin go). att = MB("_attached", [GUN, BAND, FRAME, STRIP, LAMPW, SCAN, RECESS, STEEL, DUCTG]) SG = (1, -1) # port, starboard # ================================================================== stern: stator, hub, rotor (the duct's walls are the grid's, gr_hull.py) DUCT_AX = Vector((0.0, 0.0, Zi(DUCT["y_c"]))) R_HUB = Lp(DUCT["r_hub"]) * DUCT["r_out"] / 41.7 R_HUBCAP = Lp(DUCT["r_hub_cap"]) * DUCT["r_out"] / 41.7 Y_STATOR = Yi(DUCT["stator_x"]) Y_ROTOR = Yi(DUCT["rotor_x"]) ROT_C = Vector((0.0, Y_ROTOR, DUCT_AX.z)) # the static hub: from the stern plate to the rotor, and from the rotor through the stator to the cap revolve_y(att, [(R_HUB, Y_PLATE - 0.01), (R_HUB, Y_ROTOR - 0.045)], FRAME, DUCT_AX, segs=32, cap0=False, cap1=True) revolve_y(att, [(R_HUB, Y_ROTOR + 0.045), (R_HUB, Y_STATOR + 0.03), (R_HUB * 0.97, Y_STATOR + 0.06), (R_HUBCAP, Y_LIP - 0.02), (R_HUBCAP * 0.6, Y_LIP + 0.005)], [FRAME, FRAME, FRAME, FRAME], DUCT_AX, segs=32) # six stator spokes (BACK: 30, 90, 150, 210, 270, 330 deg; vertical at the top and bottom), the hub to the inner wall for deg in DUCT["stator_deg"]: a = math.radians(deg) d = Vector((math.cos(a), 0, math.sin(a))) sec = rrect2d(0.035, Lp(DUCT["stator_width"]) * 0.5, 0.012, 2) tvec = Vector((0, 1, 0)).cross(d) rings = [] for rr in (R_HUB - 0.02, R_IN + 0.012): c = DUCT_AX + d * rr + Vector((0, Y_STATOR, 0)) rings.append([c + Vector((0, u, 0)) + tvec * v for u, v in sec]) loft_rings(att, rings, FRAME) # the rotor (SM_Grouper_Rotor, moving: spins about Y): five twisted blades on a ring, behind the stator (my addition: the # sheet draws the duct, the stator and the hub) rotor = MB("SM_Grouper_Rotor", [FRAME, STEEL]) revolve_y(rotor, [(R_HUB - 0.004, Y_ROTOR - 0.040), (R_HUB - 0.004, Y_ROTOR + 0.040)], STEEL, DUCT_AX, segs=40) R_BLADE = Lp(DUCT["rotor_r"]) * DUCT["r_out"] / 41.7 for kb in range(DUCT["rotor_blades"]): a0 = 2 * math.pi * kb / DUCT["rotor_blades"] secs = [] for q in range(6): t = q / 5 rr = (R_HUB - 0.01) + (R_BLADE - R_HUB + 0.01) * t d = Vector((math.cos(a0), 0, math.sin(a0))) tang = Vector((0, 1, 0)).cross(d) pitch = math.radians(48 - 22 * t) cd = (Vector((0, 1, 0)) * math.cos(pitch) + tang * math.sin(pitch)).normalized() nd = d.cross(cd).normalized() secs.append((ROT_C + d * rr, cd, nd, 0.12 - 0.03 * t, 0.018 - 0.008 * t)) foil(rotor, secs, FRAME) # ================================================================== the ventral skeg (SIDE "Stabilizer Fin", lower aft) def keel_z(x): return BOTf(Yi(x)) skeg_rings = [] SK = SKEG["profile"] for q in range(len(SK)): x, yb = SK[q] zr = keel_z(x) + 0.06 # the root inside the hull zb = Zi(yb) if zb > keel_z(x) - 0.02: zb = keel_z(x) - 0.02 t = Lp(SKEG["blade_thick_px"]) * 0.5 wr = Lp(SKEG["root_ridge_half_px"]) * (1.0 - 0.5 * smoothstep(600.0, 614.0, x)) pts = [] for v in (0.0, 0.35, 0.62, 0.84, 0.97): # one side down (a concave root fillet), round bottom, the other side up w = t + (wr - t) * (1 - v) ** 3 pts.append((w, zr + (zb - zr) * v)) pts += [(t * 0.45, zb - t * 0.35), (-t * 0.45, zb - t * 0.35)] pts += [(-w_, z_) for w_, z_ in reversed(pts[:5])] pts = pts[:5] + pts[5:7] + pts[7:] skeg_rings.append([Vector((w_, Yi(x), z_)) for w_, z_ in pts]) # 12 points per ring, symmetric (one side down, round bottom, the other side up): ladder caps close both ends loft_rings(att, skeg_rings, GUN, ladder=True) # the skeg's root ridge forward of the blade (BOTTOM's teardrop to x 645): a low fairing on the keel rr = [] for x in (612.0, 620.0, 630.0, 640.0, 645.0): f = 1.0 - smoothstep(612.0, 645.0, x) w = Lp(SKEG["root_ridge_half_px"]) * (0.5 + 0.5 * f) zk = keel_z(x) h = 0.02 + 0.05 * f sec = [(w, zk + 0.05), (w * 0.8, zk - h * 0.4), (w * 0.4, zk - h * 0.9), (0.0, zk - h), (-w * 0.4, zk - h * 0.9), (-w * 0.8, zk - h * 0.4), (-w, zk + 0.05), (0.0, zk + 0.08)] rr.append([Vector((a, Yi(x), b)) for a, b in sec]) loft_rings(att, rr, GUN) # ================================================================== stern fins and their mount plates (SIDE: the "T" in the waist band) FIN = STERN_FIN x_m0, x_m1 = STERN_FIN_MOUNT["x"] for sg in SG: # the mount plate: a strip across the band from the upper rim to the lower rim, 2 px proud of the band xm = 0.5 * (x_m0 + x_m1) zs = [Zi(STERN_FIN_MOUNT["y"][0] + (STERN_FIN_MOUNT["y"][1] - STERN_FIN_MOUNT["y"][0]) * q / 10) for q in range(11)] surface_bar(att, sg, [(Yi(xm), z) for z in zs], Lp(x_m1 - x_m0) * 0.5, Lp(STERN_FIN_MOUNT["proud_px"]) + 0.01, GUN, level=LV_BAND) # the fin: sections out from the band; root chord x 574.5 (the mount) .. 606, 16 px thick, rounded front (SIDE); tip at # 64 px lateral (TOP) with a shorter chord at x 594 .. 604 rings = [] for q in range(6): t = q / 5 lat_out = Lp(FIN["tip_lateral_px"]) x_a = 574.5 + (594.0 - 574.5) * t ** 0.8 x_f = 606.0 - 2.0 * t h = Lp(16.0) * 0.5 * (1.0 - 0.55 * t) zc = Zi(FIN["y_c"]) yc = Yi(0.5 * (x_a + x_f)) if q == 0: lat = hull_x_at(zc, yc) - 0.10 else: lat = hull_x_at(zc, yc) + LV_BAND + (lat_out - hull_x_at(zc, yc) - LV_BAND) * t st2 = stadium2d(min(Yi(x_a), Yi(x_f)), max(Yi(x_a), Yi(x_f)), h, 3) rings.append([Vector((sg * lat, u, zc + v)) for u, v in st2]) loft_rings(att, rings, GUN) # ================================================================== the dorsal ridge (TOP's teardrop x 551-630 on the upper aft body) DR = DORSAL_RIDGE hw_f = lambda x: Lp(lin_px([(x_, h_) for x_, h_ in DR["top_planform"]], x)) rings = [] for x in (548.6, 551.0, 556.0, 565.0, 575.0, 590.0, 600.0, 610.0, 618.0, 624.0, 628.0): y = Yi(x) zt = TOPDRAWNf(y) hw = max(0.03, hw_f(min(max(x, 551.0), 630.0))) zh = TOPf(y) hgt = max(0.006, zt - zh) sec = [(hw + 0.03, zh - 0.03), (hw + 0.012, zh + 0.004), (hw, zh + 0.35 * hgt), (hw * 0.93, zh + 0.75 * hgt), (hw * 0.75, zt - 0.08 * hgt), (hw * 0.3, zt)] sec = sec + [(-a, b) for a, b in reversed(sec)] rings.append([Vector((a, y, b)) for a, b in sec]) loft_rings(att, rings, GUN, ladder=True) # its aft face (BACK's "tombstone") carries a slot: a dark plate in a low rim yA = Yi(548.6) + 0.004 zA = TOPf(Yi(548.6)) + 0.06 rrect_bar(att, Vector((0, yA - 0.006, zA)), Vector((0, yA + 0.004, zA)), 0.11, 0.035, 0.02, RECESS, up=Vector((0, 0, 1))) # ================================================================== bilge strakes (BACK: two slanted plates under the aft lower body) BS = BILGE_STRAKE for sg in SG: secs = [] for q in range(6): x = BS["x"][0] + (BS["x"][1] - BS["x"][0]) * q / 5 y = Yi(x) e = math.sin(math.pi * q / 5) ** 0.5 lat0 = Lp(BS["lateral_px"]) - 0.17 rp = Vector((sg * lat0, y, hull_z_at(sg * lat0, y, top=False) + 0.04)) tipd = Vector((sg * math.cos(math.radians(BS["angle_deg"])), 0, -math.sin(math.radians(BS["angle_deg"])))).normalized() span = Lp(BS["span_px"]) * (0.35 + 0.65 * e) mid = rp + tipd * (span * 0.5) secs.append((mid, tipd, Vector((0, 1, 0)).cross(tipd).normalized(), span, Lp(BS["thick_px"]))) # a foil whose chord is the span (from the root inside the hull out to the tip), lofted along the hull's length foil(att, secs, GUN) # ================================================================== the deck: the two lamp heads on pylons, the sensor box, the antenna EM = EMITTER EM_LAT = Lp(EM["lateral_px"]) EM_Z = Zi(EM["lens_y"]) Y_EM0, Y_EM1 = Yi(EM["x"][0] + 2.0), Yi(EM["lens_x"]) LAMP_HEADS = [] for sg in SG: c = Vector((sg * EM_LAT, 0.0, EM_Z)) R_H = Lp(EM["half_width_px"]) * 0.95 R_LN = R_H - 0.024 prof = [(0.03, Y_EM0 + 0.004), (0.075, Y_EM0 - 0.006), (0.105, Y_EM0 - 0.03), (R_H, Y_EM0 - 0.075), (R_H, Y_EM1 + 0.02), (R_H - 0.004, Y_EM1 + 0.004), (R_H - 0.016, Y_EM1), (R_LN + 0.002, Y_EM1), (R_LN, Y_EM1 + 0.012)] revolve_y(att, prof, [GUN, GUN, GUN, GUN, FRAME, FRAME, FRAME, FRAME], c, segs=24, cap0=True, cap1=False) revolve_y(att, [(R_LN, Y_EM1 + 0.012), (R_LN * 0.6, Y_EM1 + 0.004)], LAMPW, c, segs=24, cap0=False, cap1=True) LAMP_HEADS.append((c + Vector((0, Y_EM1 - 0.02, 0)), sg)) # the pylon: a tapered neck from the shoulder up under the head, leaning out (FRONT) ym = 0.5 * (Y_EM0 + Y_EM1) + 0.03 zbase = hull_z_at(sg * (EM_LAT - 0.06), ym) rings = [] for t in (0.0, 0.45, 1.0): lat = EM_LAT - 0.07 * (1 - t) z = (zbase - 0.06) + (EM_Z - 0.03 - (zbase - 0.06)) * t w, h = 0.085 - 0.02 * t, 0.10 - 0.02 * t rings.append([Vector((sg * lat + u, ym + v, z)) for u, v in rrect2d(w * 0.5, h, 0.02, 2)]) loft_rings(att, rings, GUN) SB = SENSOR_BOX y_sb = Yi(0.5 * (SB["x"][0] + SB["x"][1])) hs = Lp(SB["half_px"]) z_top_sb = Zi(SB["top_y"]) z_bot_sb = z_top_sb - SB["box_h_m"] z_deck = hull_z_at(0.0, y_sb) qcyl(att, SB["stalk_dia_m"] * 0.5, z_bot_sb - z_deck + 0.06, Vector((0, y_sb, 0.5 * (z_bot_sb + z_deck - 0.06) + 0.01)), FRAME, segs=12) qbox(att, (2 * hs, 2 * hs, SB["box_h_m"]), Vector((0, y_sb, 0.5 * (z_top_sb + z_bot_sb))), GUN, bevel=0.012) qbox(att, (0.12, 0.012, 0.06), Vector((0, y_sb - hs - 0.004, 0.5 * (z_top_sb + z_bot_sb) + 0.01)), RECESS, bevel=0.003) qbox(att, (0.20, 0.13, 0.03), Vector((0, y_sb, z_deck + 0.005)), GUN, bevel=0.008) # the antenna (SM_Grouper_Antenna, moving: folds aft about X at its foot): its foot bracket stays on the hull AN = ANTENNA A_FOOT = Vector((0.0, Yi(AN["foot"][0]), Zi(AN["foot"][1]))) A_TIP = Vector((0.0, Yi(AN["tip"][0]), Zi(AN["tip"][1]))) antenna = MB("SM_Grouper_Antenna", [FRAME, STEEL, LAMPW]) d_ax = (A_TIP - A_FOOT).normalized() tube(antenna, [A_FOOT - d_ax * 0.012, A_FOOT + d_ax * 0.05, A_TIP - d_ax * 0.02, A_TIP], [0.019, Lp(AN["dia_px"]) * 0.5, 0.012, 0.010], FRAME, sides=8) qsphere(antenna, A_TIP, (0.016, 0.016, 0.016), LAMPW, cuts=1) qcyl(antenna, 0.026, 0.042, A_FOOT, STEEL, rot=(0, math.pi / 2, 0), segs=12) # the hinge's knuckle on the mast fb0, fb1 = Yi(AN["foot_bracket"][0]), Yi(AN["foot_bracket"][2]) zdk = hull_z_at(0.0, A_FOOT.y) for sx in (-1, 1): # a clevis: two cheeks either side rrect_bar(att, Vector((sx * 0.033, A_FOOT.y, zdk - 0.03)), Vector((sx * 0.033, A_FOOT.y, A_FOOT.z + 0.012)), 0.006, 0.032, 0.005, FRAME, up=Vector((0, 1, 0))) qbox(att, (0.10, abs(fb0 - fb1), 0.035), Vector((0, 0.5 * (fb0 + fb1), zdk + 0.006)), FRAME, bevel=0.008) qcyl(att, 0.008, 0.088, A_FOOT, STEEL, rot=(0, math.pi / 2, 0), segs=8) # the pin # ================================================================== the scanner drum on its cradle under the nose SD = SENSOR_DRUM DR_AX = Vector((0.0, 0.0, Zi(SD["y_c"]))) R_DR = Lp(SD["r"]) R_FL = Lp(SD["flange_r"]) E_DR = Lp(SD["edge_r"]) Yf0, Yf1 = Yi(SD["flange_x"][0]), Yi(SD["flange_x"][1]) Yd0, Yd1 = Yi(SD["x_body"][0]), Yi(SD["x_face"]) REC = Lp(SD["face_recess"]) prof = [(0.12, Yf0 + 0.01), (R_FL - 0.03, Yf0), (R_FL, Yf0 - 0.02), (R_FL, Yf1 + 0.005), (R_DR - E_DR, Yd0)] for q in range(1, 4): a = math.pi / 2 * q / 4 prof.append((R_DR - E_DR + E_DR * math.sin(a), Yd0 - E_DR + E_DR * math.cos(a))) prof += [(R_DR, Yd0 - E_DR), (R_DR, Yd1 + E_DR)] for q in range(1, 4): a = math.pi / 2 * q / 4 prof.append((R_DR - E_DR + E_DR * math.cos(a), Yd1 + E_DR - E_DR * math.sin(a))) R_LENS = R_DR - E_DR - 0.012 prof += [(R_DR - E_DR, Yd1), (R_LENS, Yd1), (R_LENS, Yd1 + REC)] revolve_y(att, prof, [FRAME] * 4 + [GUN] * (len(prof) - 7) + [FRAME, FRAME], DR_AX, segs=48, cap0=True, cap1=True, ang0=math.pi / 48) for xr in SD["flange_ribs_x"]: # the flange's ribs yr = Yi(xr) revolve_y(att, [(R_FL - 0.01, yr + 0.008), (R_FL + 0.014, yr + 0.006), (R_FL + 0.014, yr - 0.006), (R_FL - 0.01, yr - 0.008)], FRAME, DR_AX, segs=48, cap0=False, cap1=False) # the lens (the concept: a glowing faceted crystal; the sheet's layout: six sectors round a hexagonal hub, dividers at # 30/90/../330 deg): a raised hexagon of six facets and twelve faceted cells, dark ribs between the sectors Y_LF = Yd1 + REC # the lens's base plane def lp(r, a, dz=0.0): return Vector((DR_AX.x + r * math.cos(a), Y_LF - dz, DR_AX.z + r * math.sin(a))) lens = bmesh.new() R_HEX = Lp(SD["face_r"]["hub_hex"]) * KF * R_DR / (Lp(SD["face_r"]["rim"][1]) * KF) R_MID = 0.5 * (R_HEX + R_LENS) C0 = lens.verts.new(lp(0.0, 0.0, 0.065)) HV = [lens.verts.new(lp(R_HEX, math.radians(30 + 60 * i), 0.020)) for i in range(6)] HM = [lens.verts.new(lp(R_HEX * 0.866 * 1.0, math.radians(60 + 60 * i), 0.026)) for i in range(6)] for i in range(6): lens.faces.new((C0, HM[i - 1], HV[i], HM[i])) for i in range(6): # a sector between hex vertices i and i+1: an inner and an outer cell a0, a1 = math.radians(30 + 60 * i), math.radians(90 + 60 * i) am = 0.5 * (a0 + a1) g = 0.010 inner = [HV[i], HM[i], HV[(i + 1) % 6]] mid0, mid1 = lens.verts.new(lp(R_MID, a0 + 0.04, 0.018)), lens.verts.new(lp(R_MID, a1 - 0.04, 0.018)) midc = lens.verts.new(lp(R_MID * 1.02, am, 0.030)) out0, out1 = lens.verts.new(lp(R_LENS - g, a0 + 0.03, 0.004)), lens.verts.new(lp(R_LENS - g, a1 - 0.03, 0.004)) outc = lens.verts.new(lp(R_LENS - g, am, 0.006)) lens.faces.new((HV[i], mid0, midc, HM[i])) lens.faces.new((HM[i], midc, mid1, HV[(i + 1) % 6])) lens.faces.new((mid0, out0, outc, midc)) lens.faces.new((midc, outc, out1, mid1)) lens.normal_update() for f in lens.faces: if f.normal.y > 0: f.normal_flip() lay_cr = lens.faces.layers.int.new("crisp") # faceted crystal: every edge sharp for f in lens.faces: f.material_index = att.mi(SCAN) f[lay_cr] = 1 att.merge(lens, None) for i in range(6): # a dark rib round the hexagon rrect_bar(att, lp(R_HEX * 1.02, math.radians(30 + 60 * i), 0.024), lp(R_HEX * 1.02, math.radians(90 + 60 * i), 0.024), 0.010, 0.012, 0.004, FRAME, up=Vector((0, -1, 0))) for i in range(6): # the dark ribs on the sector lines and round the hexagon a = math.radians(30 + 60 * i) rrect_bar(att, lp(R_HEX, a, 0.026), lp(R_LENS, a, 0.008), 0.012, 0.012, 0.005, FRAME, up=Vector((0, -1, 0))) SCANNER_C = Vector((DR_AX.x, Y_LF - 0.05, DR_AX.z)) # the cradle: in each side plane (lateral 0.576, just outside the drum's rim) a hanger under the nose carrying the upper arm and # the bent U tube, the lower rail from the cheek to the flange's clevis; a cross tube under the drum, a top bar behind it, # clamps from the rails to the flange CR = CRADLE L_CR = Lp(CR["lateral_px"]) def cr(x, y, sg): return Vector((sg * L_CR, Yi(x), Zi(y))) R_TUBE = Lp(6.0) * 0.5 for sg in SG: # the hanger: a plate from the nose's underside down to the arm and the tube's top ends yh0, yh1 = Yi(887.0), Yi(900.0) zt_ = min(hull_z_at(sg * L_CR + dx_, y_, top=False) for dx_ in (-0.03, 0.03) for y_ in (yh0, yh1)) loft_rings(att, [[Vector((sg * L_CR + u, y_, z_)) for u, (y_, z_) in zip([0.03, 0.03, -0.03, -0.03], [(yh0, zt_ + 0.022), (yh1, zt_ + 0.022), (yh1, zt_ + 0.022), (yh0, zt_ + 0.022)])], [Vector((sg * L_CR + u, y_, z_)) for u, (y_, z_) in zip([0.03, 0.03, -0.03, -0.03], [(yh0, Zi(404.0)), (yh1, Zi(410.0)), (yh1, Zi(410.0)), (yh0, Zi(404.0))])]], FRAME) a0, a1 = cr(*CR["upper_arm"][0], sg), cr(*CR["upper_arm"][1], sg) rrect_bar(att, a0, a1, 0.025, Lp(4.0) * 0.5 + 0.01, 0.01, FRAME) tube(att, catmull([tuple(cr(x, y, sg)) for x, y in CR["u_tube"]], 4), R_TUBE, FRAME, sides=8) l0, l1 = cr(*CR["lower_rail"][0], sg), cr(*CR["lower_rail"][1], sg) tube(att, [l0 + Vector((0, 0.02, 0)), l1], Lp(5.0) * 0.5, FRAME, sides=8) # the rail's aft end braced up onto the cheek zc_ = hull_z_at(sg * L_CR, l0.y + 0.05, top=False) rrect_bar(att, l0 + Vector((0, 0.02, -0.02)), Vector((l0.x, l0.y + 0.05, zc_ + 0.05)), 0.03, 0.035, 0.01, FRAME, up=Vector((0, -1, 0))) x0c, y0c, x1c, y1c = CR["clevis"] qbox(att, (0.07, Lp(x1c - x0c), Lp(y1c - y0c)), cr(0.5 * (x0c + x1c), 0.5 * (y0c + y1c), sg), FRAME, bevel=0.01) for yc_ in CR["flange_clamps_y"]: # clamps from the side frame to the flange zc2 = Zi(yc_) dz = zc2 - DR_AX.z rin = math.sqrt(max(0.0, R_FL ** 2 - dz * dz)) - 0.02 rrect_bar(att, Vector((sg * rin, Yi(922.0), zc2)), Vector((sg * (L_CR + 0.02), Yi(922.0), zc2)), 0.03, 0.025, 0.008, FRAME, up=Vector((0, 0, 1))) tube(att, [cr(CR["cross_tube_x"], CR["cross_tube_y"], 1), cr(CR["cross_tube_x"], CR["cross_tube_y"], -1)], R_TUBE, FRAME, sides=8) tube(att, [cr(CR["top_bar"][0], CR["top_bar"][1], 1), cr(CR["top_bar"][0], CR["top_bar"][1], -1)], Lp(4.0) * 0.5, FRAME, sides=8) # ================================================================== cheek fins (FRONT's small slanted fins on the lower nose's cheeks) CF = CHEEK_FIN for sg in SG: secs = [] for q in range(5): x = CF["x"][0] + (CF["x"][1] - CF["x"][0]) * q / 4 y = Yi(x) lat = Lp(CF["lateral_px"]) zr = None for zz in [-0.5 - 0.02 * k for k in range(60)]: if hull_x_at(zz, y) < lat - 0.05: zr = zz break if zr is None: zr = -1.0 e = math.sin(math.pi * q / 4) ** 0.6 a = math.radians(CF["angle_deg"]) d = Vector((sg * math.sin(a), 0, -math.cos(a))) span = 0.25 * (0.4 + 0.6 * e) + 0.06 rp = Vector((sg * (lat - 0.05), y, zr + 0.04)) secs.append((rp + d * span * 0.5, d, Vector((0, 1, 0)).cross(d).normalized(), span, Lp(CF["thick_px"]))) foil(att, secs, GUN) # ================================================================== the pods' lamps (FRONT's rings; the concept's bright round lamps) POD_LAMPS = [] R_BEZ = Lp(POD["face"]["r_duct"]) * KF R_PL = Lp(POD["face"]["r_ring"]) * KF for sg in SG: a = Vector((sg * POD_L, -6.0, POD_Z)) hit, nrm, fi, dist = SKIN.ray_cast(a, Vector((0, 1, 0))) yh = hit.y if hit is not None else Yi(843.0) c = Vector((sg * POD_L, 0.0, POD_Z)) prof = [(R_BEZ, yh + 0.10), (R_BEZ, yh - 0.035), (R_BEZ - 0.012, yh - 0.05), (R_BEZ - 0.035, yh - 0.05), (R_PL + 0.01, yh - 0.03), (R_PL, yh - 0.025)] revolve_y(att, prof, [GUN, FRAME, FRAME, FRAME, FRAME], c, segs=32, cap0=False, cap1=False) revolve_y(att, [(R_PL, yh - 0.025), (R_PL * 0.6, yh - 0.035)], LAMPW, c, segs=32, cap0=False, cap1=True) POD_LAMPS.append((c + Vector((0, yh - 0.04, 0)), sg)) # ================================================================== flanks: the vents and panels aft of the door, the trace panel FD = FLANK_DETAILS for sg in SG: # the fill port and the long slot touch on the sheet (x 676): each drawn in 1.2 px so their rims stay apart x0, y0, x1, y1 = FD["fill_port"] slot_trim(att, rrect_px(x0, y0, x1 - 1.2, y1, 3.0), sg, LV_BAND, FRAME, RECESS, rim=0.009) for nm in ("slot_1", "slot_2", "slot_3"): x0, y0, x1, y1 = FD[nm] if nm == "slot_1": x0 += 1.2 slot_trim(att, rrect_px(x0, y0, x1, y1, 0.5 * (y1 - y0)), sg, LV_BAND, FRAME, RECESS, rim=0.009) x0, y0, x1, y1 = FD["panel_right"] slot_trim(att, rrect_px(x0, y0, x1, y1, 2.5), sg, LV_BAND, FRAME, RECESS) for xd, yd in FD["dots"]: p, n = on_side(sg, Yi(xd), Zi(yd), LV_BAND) qsphere(att, p, (0.022, 0.022, 0.022), STEEL, cuts=1) x0, y0, x1, y1 = FD["trace_panel"] chart_plate(att, [(Yi(x), Zi(y)) for x, y in rrect_px(x0, y0, x1, y1, 3.0)], lambda u, v, sg=sg: on_side(sg, u, v, LV_BAND), FRAME, thick=0.004, cell=0.04) # the pod's flank slot (SIDE x 790-808, y 439-443) x0, y0, x1, y1 = POD["slot"] slot_trim(att, rrect_px(x0, y0, x1, y1, 2.0), sg, LV_BAND, FRAME, RECESS) # the slot on the duct's flank (SIDE x 530, y 368-388) xs, ys0, ys1 = DUCT["flank_slot"] pa = Vector((sg * (R_OUT + 0.003), Yi(xs), Zi(ys0))) pb = Vector((sg * (R_OUT + 0.003), Yi(xs), Zi(ys1))) rrect_bar(att, pa, pb, 0.012, 0.010, 0.006, RECESS, up=Vector((sg, 0, 0))) # ================================================================== the belly: the ventral port's rings and the transducer VP = VENTRAL_PORT def belly_ring(r0, r1, mat, lift=0.004, segs=72): bm2 = bmesh.new() rows_ = [] for r_ in (r0, r1): row = [] for k in range(segs): a = 2 * math.pi * k / segs x, y = r_ * math.cos(a), Y_CL + r_ * math.sin(a) z = hull_z_at(x, y, top=False) row.append(bm2.verts.new(Vector((x, y, z - lift)))) rows_.append(row) for k in range(segs): k2 = (k + 1) % segs bm2.faces.new((rows_[0][k], rows_[1][k], rows_[1][k2], rows_[0][k2])) bm2.normal_update() if sum(f.normal.z for f in bm2.faces) > 0: bmesh.ops.reverse_faces(bm2, faces=bm2.faces[:]) for f in bm2.faces: f.material_index = att.mi(mat) att.merge(bm2, None) for r0, r1 in ((Lp(VP["r_groove"][0]) - 0.008, Lp(VP["r_groove"][0]) + 0.008), (Lp(VP["r_groove"][1]) - 0.008, Lp(VP["r_groove"][1]) + 0.008)): belly_ring(r0, r1, FRAME, lift=0.003) VT = VENTRAL_TRANSDUCER yt = Yi(0.5 * (VT["x"][0] + VT["x"][1])) qsphere(att, Vector((0.0, yt, hull_z_at(0.0, yt, top=False) + 0.01)), (Lp(VT["half_px"]) * 1.6, Lp(VT["x"][1] - VT["x"][0]) * 0.5, Lp(VT["proud_px"]) + 0.01), FRAME, cuts=2) REPORT["stage"] = "fittings"