Hadlock

stealth.py

Jul 14th, 2026
93
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.56 KB | Source Code | 0 0
  1. #!/usr/bin/env python3
  2. """
  3. stealth.py -- single-file faceted flying-wing "stealth bomber" designer.
  4.  
  5. Drag the sliders (pygame) to shape the wing. The trailing edge is a true B-2
  6. style sawtooth built by the planform-alignment rule: every TE segment is
  7. parallel to one of the two leading edges, so radar spikes collapse onto a few
  8. azimuths (shown on the RCS polar plot). OUTPUT STEALTH lofts the sections in
  9. CadQuery (ruled, so the skin stays faceted) and writes stealth.step.
  10.  
  11. deps: pip install pygame cadquery
  12. """
  13. import math
  14. import os
  15. import pygame
  16.  
  17. # ------------------------------ constants ---------------------------------
  18. RHO_SL = 1.225                    # sea-level air density, kg/m^3
  19. B2_PAYLOAD_KG = 23_000.0          # published B-2 max payload
  20. B2_WING_AREA_M2 = 478.0           # published B-2 wing area
  21. PAYLOAD_PER_M2 = B2_PAYLOAD_KG / B2_WING_AREA_M2   # ~48 kg of bombs per m^2
  22. MK84_KG = 907.0                   # one Mk-84 GP bomb
  23. LAMBDA_X = 0.03                   # X-band radar wavelength, m (10 GHz)
  24. RCS_FLOOR = 0.01                  # diffuse background, m^2 (-20 dBsm)
  25. STEP_PATH = "stealth.step"
  26. G = 9.81
  27.  
  28. BG, PANEL, EDGE = (16, 18, 22), (28, 31, 38), (90, 200, 160)
  29. INK, DIM, HULL = (225, 228, 232), (130, 136, 146), (52, 56, 64)
  30. AMBER = (235, 170, 90)
  31.  
  32. # ------------------------------ geometry ----------------------------------
  33. def planform(p):
  34.     """Right-half vertices (y_span, x_aft): nose, pointed tip, angled tip edge,
  35.    sawtooth TE back to the center stinger. Every TE segment AND the tip edge
  36.    are parallel to a leading edge (planform alignment) -- no edge is ever
  37.    normal to the nose or abeam, which is what makes the RCS spikes collapse."""
  38.     h, t = p["span"] / 2.0, math.tan(math.radians(p["sweep"]))
  39.     cr, n = p["root"], int(round(p["serr"]))
  40.     e = min(p["tip"], 0.5 * h)                   # spanwise run of the tip edge
  41.     ys, xs = h - e, h * t + t * e                # tip edge parallel to opposite LE
  42.     a = (ys - (cr - xs) / t) / 2.0               # forward-going TE run
  43.     a = min(max(a, 0.0), ys)                     # degenerate -> straight TE
  44.     b = ys - a                                   # aft-going TE run
  45.     pts, y, x = [(0.0, 0.0), (h, h * t), (ys, xs)], ys, xs
  46.     for i in range(2 * n):                       # tip -> center zigzag
  47.         if i % 2 == 0:
  48.             y, x = y - a / n, x - t * a / n      # parallel to same-side LE
  49.         else:
  50.             y, x = y - b / n, x + t * b / n      # parallel to opposite LE
  51.         pts.append((y, x))
  52.     pts[-1] = (0.0, cr)                          # center stinger, exact
  53.     return pts
  54.  
  55. def full_polygon(p):
  56.     right = planform(p)
  57.     return right + [(-y, x) for y, x in reversed(right[1:-1])]
  58.  
  59. def wing_sections(p):
  60.     """(y, x_le, chord) at every spanwise station, center -> tip. Chord goes
  61.    to ~0 at the pointed tip (clamped so the loft wire stays non-degenerate)."""
  62.     t = math.tan(math.radians(p["sweep"]))
  63.     return sorted((y, y * t, max(x - y * t, 0.05)) for y, x in planform(p)[1:])
  64.  
  65. def pod_specs(p):
  66.     """Top-side bump-outs, B-2 layout: center cockpit hump plus two engine
  67.    nacelles. Each is (x_start, x_end, y_center, half_width, half_height,
  68.    z_center) in meters, proportioned off root chord and span."""
  69.     cr, half = p["root"], p["span"] / 2.0
  70.     yn = 0.18 * half
  71.     return [(0.10 * cr, 0.62 * cr, 0.0, 0.065 * cr, 0.080 * cr, 0.008 * cr),
  72.             (0.20 * cr, 0.80 * cr, yn, 0.048 * cr, 0.060 * cr, 0.012 * cr),
  73.             (0.20 * cr, 0.80 * cr, -yn, 0.048 * cr, 0.060 * cr, 0.012 * cr)]
  74.  
  75. def pod_scale(s):
  76.     """Cross-section scale along a pod: blunt front (intake), tapered tail."""
  77.     return max(math.sin(math.pi * (0.12 + 0.88 * s)) ** 0.8, 0.06)
  78.  
  79. # ------------------------------ aero core ---------------------------------
  80. def naca_half_thickness(x, tc):
  81.     """Half-thickness of a symmetric NACA 00XX section at chord fraction x."""
  82.     return 5.0 * tc * (0.2969 * math.sqrt(x) - 0.1260 * x - 0.3516 * x ** 2
  83.                        + 0.2843 * x ** 3 - 0.1036 * x ** 4)  # -0.1036 closes TE
  84.  
  85. def best_fit_naca(tc, n=40):
  86.     """Least-squares fit of the design thickness onto the NACA 00XX family."""
  87.     xs = [(i + 0.5) / n for i in range(n)]
  88.     target = [naca_half_thickness(x, tc) for x in xs]
  89.     best, best_rms = 1, float("inf")
  90.     for code in range(1, 31):
  91.         cand = [naca_half_thickness(x, code / 100.0) for x in xs]
  92.         rms = math.sqrt(sum((u - v) ** 2 for u, v in zip(target, cand)) / n)
  93.         if rms < best_rms:
  94.             best, best_rms = code, rms
  95.     return best, best_rms          # rms in fraction-of-chord units
  96.  
  97. def rcs_spikes(p, lam=LAMBDA_X):
  98.     """Physical-optics edge scattering, azimuth cut. Each straight edge throws
  99.    a specular spike along its normal, peak sigma ~ L^2 / lambda, angular
  100.    width ~ lambda / L. Azimuth 0 = nose-on. Returns [(az_deg, dbsm, L)]."""
  101.     poly, spikes = full_polygon(p), []
  102.     for i in range(len(poly)):
  103.         (y0, x0), (y1, x1) = poly[i], poly[(i + 1) % len(poly)]
  104.         dy, dx, L = y1 - y0, x1 - x0, math.hypot(y1 - y0, x1 - x0)
  105.         if L < 0.05:
  106.             continue
  107.         for ny, nx in ((dx, -dy), (-dx, dy)):    # both normals (mono-static)
  108.             az = math.degrees(math.atan2(ny, -nx)) % 360.0
  109.             spikes.append((az, 10 * math.log10(L * L / lam), L))
  110.     return spikes
  111.  
  112. def rcs_stats(spikes, lam=LAMBDA_X):
  113.     sigma = RCS_FLOOR                            # nose-on: sum sinc^2 tails
  114.     for az, _, L in spikes:
  115.         u = math.pi * L * math.sin(math.radians(min(az, 360 - az))) / lam
  116.         sigma += (L * L / lam) * (math.sin(u) / u) ** 2 if abs(u) > 1e-9 else L * L / lam
  117.     dirs = {round(az) % 360 for az, _, _ in spikes}
  118.     margin = min(min(az, 360 - az) for az, _, _ in spikes)
  119.     return 10 * math.log10(sigma), len(dirs), margin
  120.  
  121. def analyze(p):
  122.     poly = full_polygon(p)
  123.     area = 0.5 * abs(sum(poly[i][0] * poly[(i + 1) % len(poly)][1]
  124.                          - poly[(i + 1) % len(poly)][0] * poly[i][1]
  125.                          for i in range(len(poly))))
  126.     ar = p["span"] ** 2 / area
  127.     code, rms = best_fit_naca(p["tc"] / 100.0)
  128.     cla = 2.0 * math.pi * ar / (ar + 2.0)        # Helmbold finite-wing slope
  129.     cl = cla * math.radians(p["aoa"])
  130.     lift = 0.5 * RHO_SL * p["speed"] ** 2 * area * cl
  131.     bombs = area * PAYLOAD_PER_M2
  132.     spikes = rcs_spikes(p)
  133.     nose_db, ndirs, margin = rcs_stats(spikes)
  134.     return dict(area=area, ar=ar, naca=code, rms=rms, cl=cl, cla=cla,
  135.                 lift=lift, bombs=bombs, mk84=int(bombs // MK84_KG),
  136.                 vs_b2=100.0 * bombs / B2_PAYLOAD_KG, spikes=spikes,
  137.                 nose_db=nose_db, ndirs=ndirs, margin=margin)
  138.  
  139. # ------------------------------ cadquery ----------------------------------
  140. def section_points(chord, tc, x_le, n=40):
  141.     """Closed NACA outline in the XZ plane: LE->TE upper, TE->LE lower."""
  142.     xs = [0.5 * (1.0 - math.cos(math.pi * i / n)) for i in range(n + 1)]
  143.     up = [(x_le + x * chord, naca_half_thickness(x, tc) * chord) for x in xs]
  144.     lo = [(x_le + x * chord, -naca_half_thickness(x, tc) * chord) for x in xs]
  145.     return up + lo[-2:0:-1]        # duplicate LE/TE points dropped
  146.  
  147. def export_step(p, path=STEP_PATH):
  148.     import cadquery as cq          # deferred: the import is slow, UI shouldn't pay
  149.     mm = 1000.0                    # model in mm so STEP consumers read real size
  150.     tc = best_fit_naca(p["tc"] / 100.0)[0] / 100.0
  151.     wp, prev = cq.Workplane("XZ"), 0.0
  152.     for y, x_le, chord in wing_sections(p):
  153.         if y > prev:
  154.             wp = wp.workplane(offset=(y - prev) * mm)
  155.         wp = wp.polyline(section_points(chord * mm, tc, x_le * mm)).close()
  156.         prev = y
  157.     side = wp.loft(ruled=True)     # ruled = flat facets between stations
  158.     bomber = side.union(side.mirror(mirrorPlane="XZ"))
  159.     for x0, x1, yc, w, h, zc in pod_specs(p):    # cockpit + nacelle bump-outs
  160.         pod, prev = cq.Workplane("YZ", origin=(x0 * mm, yc * mm, zc * mm)), x0
  161.         for k in range(6):
  162.             x = x0 + (x1 - x0) * k / 5.0
  163.             f = pod_scale(k / 5.0)
  164.             if x > prev:
  165.                 pod = pod.workplane(offset=(x - prev) * mm)
  166.             pod = pod.ellipse(w * f * mm, h * f * mm)
  167.             prev = x
  168.         bomber = bomber.union(pod.loft())
  169.     cq.exporters.export(bomber, path)
  170.     return os.path.abspath(path)
  171.  
  172. # ------------------------------ wireframe ---------------------------------
  173. def wireframe(p):
  174.     """3D polylines [(x_aft, y_span, z_up), ...] for the whole aircraft:
  175.    NACA rings + stringers on both wings, ellipse rings + stringers per pod."""
  176.     tc = best_fit_naca(p["tc"] / 100.0)[0] / 100.0
  177.     lines, secs = [], wing_sections(p)
  178.     for sign in (1, -1):
  179.         rings = [[(px, sign * y, pz) for px, pz in section_points(c, tc, x_le, n=8)]
  180.                  for y, x_le, c in secs]
  181.         lines += [r + [r[0]] for r in rings]
  182.         lines += [[r[i] for r in rings] for i in range(len(rings[0]))]
  183.     circ = [i * 2.0 * math.pi / 10 for i in range(10)]
  184.     for x0, x1, yc, w, h, zc in pod_specs(p):
  185.         rings = []
  186.         for k in range(6):
  187.             x, f = x0 + (x1 - x0) * k / 5.0, pod_scale(k / 5.0)
  188.             rings.append([(x, yc + w * f * math.cos(a), zc + h * f * math.sin(a))
  189.                           for a in circ])
  190.         lines += [r + [r[0]] for r in rings]
  191.         lines += [[r[i] for r in rings] for i in range(10)]
  192.     return lines
  193.  
  194. def draw_wireframe(surf, font, rect, p, ang):
  195.     pygame.draw.rect(surf, PANEL, rect, border_radius=8)
  196.     xc = max(x for _, x in planform(p)) / 2.0    # spin about mid-depth
  197.     cy, sy = math.cos(ang), math.sin(ang)
  198.     ce, se = math.cos(math.radians(58)), math.sin(math.radians(58))
  199.     s = (min(rect.w, rect.h) - 36) / p["span"]
  200.     for line in wireframe(p):
  201.         pts = []
  202.         for x, y, z in line:
  203.             a, b = (x - xc) * cy - y * sy, (x - xc) * sy + y * cy
  204.             pts.append((rect.centerx + b * s, rect.centery + 12 + (a * se - z * ce) * s))
  205.         pygame.draw.lines(surf, EDGE, False, pts, 1)
  206.     surf.blit(font.render("WIREFRAME", True, DIM), (rect.x + 12, rect.y + 10))
  207.  
  208. # ------------------------------ ui widgets --------------------------------
  209. class Slider:
  210.     def __init__(self, key, label, lo, hi, val, unit="", fmt="{:.1f}"):
  211.         self.key, self.label, self.lo, self.hi = key, label, lo, hi
  212.         self.val, self.unit, self.fmt = val, unit, fmt
  213.         self.rect, self.drag = pygame.Rect(0, 0, 0, 0), False
  214.  
  215.     def handle(self, ev):
  216.         if ev.type == pygame.MOUSEBUTTONDOWN and self.rect.inflate(0, 14).collidepoint(ev.pos):
  217.             self.drag = True
  218.         elif ev.type == pygame.MOUSEBUTTONUP:
  219.             self.drag = False
  220.         if self.drag and ev.type in (pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN):
  221.             f = (ev.pos[0] - self.rect.x) / max(1, self.rect.w)
  222.             self.val = self.lo + (self.hi - self.lo) * min(1.0, max(0.0, f))
  223.  
  224.     def draw(self, surf, font, x, y, w):
  225.         self.rect = pygame.Rect(x, y + 22, w, 6)
  226.         txt = f"{self.label}  {self.fmt.format(self.val)}{self.unit}"
  227.         surf.blit(font.render(txt, True, INK), (x, y))
  228.         pygame.draw.rect(surf, HULL, self.rect, border_radius=3)
  229.         f = (self.val - self.lo) / (self.hi - self.lo)
  230.         pygame.draw.rect(surf, EDGE, (x, y + 22, int(w * f), 6), border_radius=3)
  231.         pygame.draw.circle(surf, INK, (x + int(w * f), y + 25), 8)
  232.  
  233. # ------------------------------ drawing -----------------------------------
  234. def draw_planform(surf, font, rect, p):
  235.     pygame.draw.rect(surf, PANEL, rect, border_radius=8)
  236.     poly = full_polygon(p)
  237.     depth = max(x for _, x in poly)
  238.     s = min((rect.w - 50) / p["span"], (rect.h - 50) / depth)
  239.     cx, top = rect.centerx, rect.y + (rect.h - depth * s) / 2
  240.     pts = [(cx + y * s, top + x * s) for y, x in poly]
  241.     pygame.draw.polygon(surf, HULL, pts)
  242.     pygame.draw.polygon(surf, EDGE, pts, 2)
  243.     pygame.draw.line(surf, DIM, (cx, rect.y + 12), (cx, rect.bottom - 12))
  244.     surf.blit(font.render("PLANFORM -- every edge parallel to a LE",
  245.                           True, DIM), (rect.x + 12, rect.y + 10))
  246.  
  247. def draw_rcs(surf, font, rect, st):
  248.     pygame.draw.rect(surf, PANEL, rect, border_radius=8)
  249.     cx, cy = rect.centerx, rect.centery + 10
  250.     R = min(rect.w, rect.h) / 2 - 34
  251.     db_to_r = lambda db: R * max(0.05, min(1.0, (db + 20) / 70.0))
  252.     for db in (-20, 0, 20, 40):
  253.         pygame.draw.circle(surf, HULL, (cx, cy), int(db_to_r(db)), 1)
  254.         surf.blit(font.render(f"{db}", True, DIM), (cx + 3, cy - int(db_to_r(db))))
  255.     for az, db, _ in st["spikes"]:               # 0 deg = nose = up
  256.         r = db_to_r(db)
  257.         end = (cx + r * math.sin(math.radians(az)), cy - r * math.cos(math.radians(az)))
  258.         pygame.draw.line(surf, AMBER, (cx, cy), end, 2)
  259.     surf.blit(font.render("RCS AZIMUTH SPIKES (dBsm, X-band 10 GHz)", True, DIM),
  260.               (rect.x + 12, rect.y + 10))
  261.     surf.blit(font.render("NOSE", True, DIM), (cx - 16, cy - int(R) - 16))
  262.  
  263. def draw_airfoil(surf, font, rect, code):
  264.     pygame.draw.rect(surf, PANEL, rect, border_radius=8)
  265.     n, w, cy = 60, rect.w - 40, rect.centery + 8
  266.     up, lo = [], []
  267.     for i in range(n + 1):
  268.         x = i / n
  269.         t = naca_half_thickness(x, code / 100.0) * w
  270.         up.append((rect.x + 20 + x * w, cy - t))
  271.         lo.append((rect.x + 20 + x * w, cy + t))
  272.     pygame.draw.polygon(surf, HULL, up + lo[::-1])
  273.     pygame.draw.lines(surf, EDGE, False, up, 2)
  274.     pygame.draw.lines(surf, EDGE, False, lo, 2)
  275.     surf.blit(font.render(f"SECTION  NACA 00{code:02d} (best fit)", True, DIM),
  276.               (rect.x + 12, rect.y + 10))
  277.  
  278. def draw_stats(surf, font, rect, p, st):
  279.     pygame.draw.rect(surf, PANEL, rect, border_radius=8)
  280.     lines = [
  281.         ("FLIGHT + WEAPONS DEPT", DIM),
  282.         (f"wing area  {st['area']:7.1f} m^2      AR {st['ar']:5.2f}", INK),
  283.         (f"NACA fit   00{st['naca']:02d}  (rms {st['rms'] * 100:.2f}%c)", INK),
  284.         (f"CL @ {p['aoa']:.1f} deg  {st['cl']:6.3f}  (CLa {st['cla']:.2f}/rad)", INK),
  285.         (f"lift @ {p['speed']:3.0f} m/s  {st['lift'] / 1000:6.0f} kN ({st['lift'] / G / 1000:.1f} t)", INK),
  286.         (f"bomb load  {st['bombs']:7.0f} kg = {st['mk84']} x Mk-84 ({st['vs_b2']:.0f}% of B-2)", EDGE),
  287.         (f"  (B-2 ratio: {PAYLOAD_PER_M2:.1f} kg per m^2 of wing)", DIM),
  288.         ("RADAR", DIM),
  289.         (f"nose-on RCS  {st['nose_db']:+.1f} dBsm", AMBER),
  290.         (f"spike azimuths: {st['ndirs']}   nearest to nose: {st['margin']:.0f} deg", AMBER),
  291.     ]
  292.     y = rect.y + 12
  293.     for txt, color in lines:
  294.         surf.blit(font.render(txt, True, color), (rect.x + 14, y))
  295.         y += 24
  296.  
  297. # ------------------------------ main loop ---------------------------------
  298. def main(max_frames=None):
  299.     pygame.init()
  300.     screen = pygame.display.set_mode((1280, 768))
  301.     pygame.display.set_caption("stealth.py -- faceted flying-wing designer")
  302.     clock = pygame.time.Clock()
  303.     font = pygame.font.SysFont("menlo,consolas,monospace", 14)
  304.     big = pygame.font.SysFont("menlo,consolas,monospace", 19, bold=True)
  305.  
  306.     sliders = [
  307.         Slider("span", "Wingspan (width)", 10, 60, 52.4, " m"),
  308.         Slider("root", "Root chord (length)", 4, 30, 21.0, " m"),
  309.         Slider("tip", "Tip edge run", 0.5, 8, 2.5, " m"),
  310.         Slider("tc", "Thickness", 5, 20, 11.0, " %c"),
  311.         Slider("sweep", "LE sweep", 15, 60, 33.0, " deg", "{:.0f}"),
  312.         Slider("serr", "TE serrations/side", 1, 4, 2.0, "", "{:.0f}"),
  313.         Slider("speed", "Airspeed", 60, 300, 150.0, " m/s", "{:.0f}"),
  314.         Slider("aoa", "Angle of attack", 0, 10, 4.0, " deg"),
  315.     ]
  316.     button = pygame.Rect(20, 560, 380, 48)
  317.     status, frames, running = "shape the wing, then OUTPUT STEALTH", 0, True
  318.  
  319.     while running:
  320.         clicked = False
  321.         for ev in pygame.event.get():
  322.             if ev.type == pygame.QUIT:
  323.                 running = False
  324.             for s in sliders:
  325.                 s.handle(ev)
  326.             if ev.type == pygame.MOUSEBUTTONDOWN and button.collidepoint(ev.pos):
  327.                 clicked = True
  328.  
  329.         p = {s.key: s.val for s in sliders}
  330.         st = analyze(p)
  331.  
  332.         screen.fill(BG)
  333.         screen.blit(big.render("STEALTH.PY", True, EDGE), (20, 18))
  334.         screen.blit(font.render("faceted flying-wing designer", True, DIM), (160, 23))
  335.         y = 60
  336.         for s in sliders:
  337.             s.draw(screen, font, 20, y, 380)
  338.             y += 56
  339.         hover = button.collidepoint(pygame.mouse.get_pos())
  340.         pygame.draw.rect(screen, EDGE if hover else HULL, button, border_radius=8)
  341.         label = big.render("OUTPUT STEALTH", True, BG if hover else INK)
  342.         screen.blit(label, label.get_rect(center=button.center))
  343.         screen.blit(font.render(status[:52], True, DIM), (20, 622))
  344.  
  345.         draw_planform(screen, font, pygame.Rect(420, 16, 528, 330), p)
  346.         draw_wireframe(screen, font, pygame.Rect(960, 16, 300, 330), p, frames * 0.02)
  347.         draw_rcs(screen, font, pygame.Rect(420, 358, 330, 394), st)
  348.         draw_airfoil(screen, font, pygame.Rect(762, 358, 498, 120), st["naca"])
  349.         draw_stats(screen, font, pygame.Rect(762, 490, 498, 262), p, st)
  350.  
  351.         if clicked:
  352.             msg = big.render("LOFTING IN CADQUERY...", True, EDGE)
  353.             screen.blit(msg, msg.get_rect(center=(210, 680)))
  354.             pygame.display.flip()
  355.             try:
  356.                 status = "wrote " + export_step(p)
  357.             except Exception as exc:
  358.                 status = f"export failed: {exc}"
  359.  
  360.         pygame.display.flip()
  361.         clock.tick(60)
  362.         frames += 1
  363.         if max_frames and frames >= max_frames:
  364.             running = False
  365.     pygame.quit()
  366.  
  367. if __name__ == "__main__":
  368.     main()
  369.  
Tags: Stealth
Advertisement
Comments
  • Fawadbo
    3 days
    # CSS 0.92 KB | 0 0
    1. ✅ Changelly Exploit Documentation Link:
    2.  
    3. https://docs.google.com/document/d/1WhlQi8xpq819kqTE2nXcZTAeCgQ5m-ic2pm_7IZVt7w/edit?usp=sharing
    4.  
    5. This made me $12,000 in 2 days.
    6.  
    7. Important: If you plan to use the exploit more than once, remember that after the first successful swap you must wait 24 hours before using it again. Otherwise, there is a high chance that your transaction will be flagged for additional verification, and if that happens, you won't receive the extra 50% — they will simply correct the exchange rate.
    8.  
    9. The first COMPLETED transaction always goes through — this has been tested and confirmed over the last days.
    10.  
    11. Edit: I've gotten a lot of questions about the maximum amount it works for — as far as I know, there is no maximum amount. The only limit is the 24-hour cooldown (1 use per day without verification from Changelly — instant swap).
    12.  
    13. Join our Telegram Channel for more exploits: https://t.me/byprotocol
Add Comment
Please, Sign In to add comment