Hadlock

planet(-oid) barycenter calculator v4

May 1st, 2026
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.42 KB | Source Code | 0 0
  1. import pygame, sys, math
  2. # bary v4
  3. pygame.init()
  4. W, H = 980, 580
  5. S = pygame.display.set_mode((W, H)); pygame.display.set_caption("Solar Barycenter Calculator")
  6. F  = pygame.font.SysFont("monospace", 13)
  7. FB = pygame.font.SysFont("monospace", 14, bold=True)
  8. FT = pygame.font.SysFont("monospace", 17, bold=True)
  9.  
  10. MS = 1.989e30   # Sun mass (kg)
  11. RS = 696000     # Sun radius (km)
  12. AU = 1.496e8    # 1 AU in km
  13. ME = 5.972e24   # Earth mass (kg)
  14.  
  15. # (name, distance in AU, mass in Earth masses)
  16. PL = [("Mercury",0.387,0.0553),("Venus",0.723,0.815),("Earth",1.000,1.0),
  17.       ("Mars",1.524,0.107),("Jupiter",5.204,317.8),("Saturn",9.582,95.16),
  18.       ("Uranus",19.20,14.54),("Neptune",30.05,17.15),("Pluto",39.48,0.0022),
  19.       ("Eris",67.78,0.00277),("Ceres",2.768,0.000158)]
  20. PR = [("Eris",67.78,0.00277),("Pluto",39.48,0.0022),("Haumea",43.13,6.71e-4),
  21.       ("Makemake",45.79,5.19e-4),("Gonggong",67.49,2.93e-4),("Charon",39.48,2.66e-4),
  22.       ("Quaoar",43.69,2.34e-4),("Sedna",506.0,1.67e-4),("Orcus",39.42,1.06e-4),
  23.       ("Salacia",42.18,8.24e-5),("Ceres",2.768,1.58e-4)]
  24. # (name, mass in solar masses, radius in solar radii). Clicking sets the primary.
  25. ST = [("Sol",1.0,1.0),("Alpha Cen A",1.079,1.223),("Proxima Cen",0.1221,0.1542),
  26.       ("Sirius A",2.063,1.711),("Vega",2.135,2.362),("Polaris",5.4,37.5),
  27.       ("Betelgeuse",16.5,640.0),("Rigel",21.0,78.9),("Antares",12.0,680.0),
  28.       ("Arcturus",1.08,25.4),("Aldebaran",1.16,44.13)]
  29. SL = {"d": 5.204, "m": 317.8}   # custom-row defaults: Jupiter
  30. PRIMARY = {"name":"Sol", "M":MS, "R":RS}
  31. DR = None
  32.  
  33. def bc(au, me):
  34.     M, Rp = PRIMARY["M"], PRIMARY["R"]
  35.     r = au * AU * me * ME / (M + me * ME)
  36.     return r, r - Rp, r < Rp
  37.  
  38. def panel(rect, col=(25,25,45), r=6):
  39.     pygame.draw.rect(S, col, rect, border_radius=r)
  40.     pygame.draw.rect(S, (50,50,80), rect, 1, border_radius=r)
  41.  
  42. def sldr(lbl, k, x, y, mn, mx_, w=270):
  43.     v = SL[k]
  44.     pct = max(0, min(1, (math.log10(v)-math.log10(mn)) / (math.log10(mx_)-math.log10(mn))))
  45.     kx = x + int(pct*w)
  46.     panel(pygame.Rect(x-8, y-22, w+16, 44), (20,20,38))
  47.     pygame.draw.rect(S, (40,40,65),  (x, y+5, w, 5), border_radius=3)
  48.     pygame.draw.rect(S, (80,170,255),(x, y+5, kx-x, 5), border_radius=3)
  49.     pygame.draw.circle(S, (30,30,50),  (kx, y+7), 9)
  50.     pygame.draw.circle(S, (80,170,255),(kx, y+7), 7)
  51.     pygame.draw.circle(S, (180,220,255),(kx-2, y+5), 2)
  52.     S.blit(F.render(f"{lbl}: {v:.4g}", 1, (170,190,220)), (x, y-18))
  53.     return pygame.Rect(x-4, y+1, w+8, 14)
  54.  
  55. BG=(10,10,20); CI=(60,220,100); CO=(255,70,70); CH=(80,180,255); CK=(140,100,255); CY=(255,230,60)
  56. COLS = [("Body",120),("Dist AU",80),("Mass/ME",88),("Bary (km)",105),("Diff (km)",110),("Inside?",72)]
  57. TW = sum(w for _, w in COLS)
  58. SR = {}
  59. clock = pygame.time.Clock()
  60. D_MIN, D_MAX = 0.1, 1000.0    # AU (1000 covers Sedna's 506 AU semi-major)
  61. M_MIN, M_MAX = 1e-6, 400.0    # Earth masses
  62.  
  63. # Pluto-Charon two-body system: the only known dwarf-planet pair whose barycenter
  64. # lies outside the primary. Computed independently of the Sun-relative bc().
  65. PC_D, PC_MP, PC_MC, PC_RP = 19591, 1.303e22, 1.586e21, 1188.3   # km, kg, kg, km
  66. PC_R = PC_D * PC_MC / (PC_MP + PC_MC)   # bary distance from Pluto center (km)
  67. PC_OUT = PC_R > PC_RP
  68.  
  69. def stars():
  70.     t = pygame.time.get_ticks()
  71.     for i in range(45):
  72.         b = 80 + (i*37 + t//80) % 120
  73.         pygame.draw.circle(S, (b,b,b+20), ((i*173+11)%W, (i*97+7)%110), 1 if i%3 else 0)
  74.  
  75. while True:
  76.     S.fill(BG); stars()
  77.     mx, my = pygame.mouse.get_pos()
  78.     for e in pygame.event.get():
  79.         if e.type == pygame.QUIT: sys.exit()
  80.         if e.type == pygame.MOUSEBUTTONUP: DR = None
  81.         if e.type == pygame.MOUSEBUTTONDOWN:
  82.             if SR.get("d") and SR["d"].collidepoint(e.pos): DR = "d"
  83.             if SR.get("m") and SR["m"].collidepoint(e.pos): DR = "m"
  84.             for i, (n, a, m) in enumerate(PR):
  85.                 if pygame.Rect(W-316, 130+i*32, 144, 26).collidepoint(e.pos):
  86.                     SL["d"] = a; SL["m"] = m
  87.             for i, (n, mm, rr) in enumerate(ST):
  88.                 if pygame.Rect(W-162, 130+i*32, 144, 26).collidepoint(e.pos):
  89.                     PRIMARY["name"] = n; PRIMARY["M"] = mm * MS; PRIMARY["R"] = rr * RS
  90.         if e.type == pygame.MOUSEMOTION and DR:
  91.             for k, x, mn, mx_, w in [("d",30,D_MIN,D_MAX,270), ("m",340,M_MIN,M_MAX,270)]:
  92.                 if DR == k:
  93.                     SL[k] = mn * (mx_/mn) ** max(0, min(1, (mx-x)/w))
  94.  
  95.     S.blit(FT.render("SOLAR BARYCENTER CALCULATOR", 1, CH), (30, 12))
  96.     S.blit(F.render(f"Primary: {PRIMARY['name']}   M={PRIMARY['M']:.3e} kg   R={PRIMARY['R']:,.0f} km   |  Inside = within primary's surface", 1, (80,80,110)), (30, 34))
  97.     SR["d"] = sldr("Distance (AU)",  "d",  30, 75, D_MIN, D_MAX)
  98.     SR["m"] = sldr("Mass (x Earth)", "m", 340, 75, M_MIN, M_MAX)
  99.  
  100.     panel(pygame.Rect(W-322, 108, 312, 22 + len(PR)*32 + 8), (18,18,36))
  101.     S.blit(F.render("BODIES",            1, (130,110,180)), (W-316, 113))
  102.     S.blit(F.render("STARS (set primary)", 1, (110,180,130)), (W-162, 113))
  103.     for i, (n, a, m) in enumerate(PR):
  104.         br = pygame.Rect(W-316, 130+i*32, 144, 24); hot = br.collidepoint(mx, my)
  105.         panel(br, (45,35,80) if hot else (30,25,55))
  106.         S.blit(FB.render(n, 1, CK if hot else (150,130,220)), (W-309, 134+i*32))
  107.     for i, (n, mm, rr) in enumerate(ST):
  108.         br = pygame.Rect(W-162, 130+i*32, 144, 24); hot = br.collidepoint(mx, my)
  109.         cur = PRIMARY["name"] == n
  110.         panel(br, (40,90,50) if hot else ((30,70,40) if cur else (22,45,28)))
  111.         S.blit(FB.render(n, 1, CI if (hot or cur) else (130,200,130)), (W-155, 134+i*32))
  112.  
  113.     hx, hy = 18, 140
  114.     panel(pygame.Rect(hx-2, hy, TW+4, 22), (20,30,65))
  115.     for h, w in COLS:
  116.         S.blit(FB.render(h, 1, CH), (hx+3, hy+4)); hx += w
  117.  
  118.     for i, (nm, au, me) in enumerate(PL + [("Custom", SL["d"], SL["m"])]):
  119.         r, df, ins = bc(au, me)
  120.         ry, rx = 164 + i*20, 18
  121.         custom = nm == "Custom"
  122.         pygame.draw.rect(S, (30,22,55) if custom else ((24,24,42) if i%2 else (18,18,32)), (rx-2, ry, TW+4, 19))
  123.         if custom: pygame.draw.rect(S, (70,50,120), (rx-2, ry, TW+4, 19), 1)
  124.         vs = [nm, f"{au:.3f}", f"{me:.5g}", f"{r:,.0f}", f"{abs(df):,.0f}", "YES" if ins else "NO"]
  125.         for j, ((h, w), v) in enumerate(zip(COLS, vs)):
  126.             tc = CI if (j==5 and ins) else CO if j==5 else CY if custom else (200,200,210)
  127.             S.blit(F.render(v, 1, tc), (rx+2, ry+3)); rx += w
  128.  
  129.     by = 410
  130.     panel(pygame.Rect(16, by, TW+4, 118), (28,18,38))
  131.     S.blit(FB.render("BINARY: Pluto-Charon  (only known case where the barycenter sits outside the primary)", 1, CK), (24, by+6))
  132.     S.blit(F.render(f"Separation: {PC_D:,} km     Pluto radius: {PC_RP:,.0f} km     Charon/Pluto mass ratio: {PC_MC/PC_MP*100:.2f}%", 1, (190,190,210)), (24, by+28))
  133.     S.blit(F.render(f"Pluto mass:  {PC_MP:.3e} kg     Charon mass: {PC_MC:.3e} kg", 1, (190,190,210)), (24, by+46))
  134.     S.blit(F.render(f"Barycenter: {PC_R:,.0f} km from Pluto center", 1, (200,200,220)), (24, by+64))
  135.     pc_col = CO if PC_OUT else CI
  136.     pc_msg = f"OUTSIDE Pluto by {PC_R-PC_RP:,.0f} km" if PC_OUT else f"INSIDE Pluto by {PC_RP-PC_R:,.0f} km"
  137.     S.blit(FB.render(pc_msg, 1, pc_col), (24, by+86))
  138.  
  139.     pygame.draw.line(S, (40,40,70), (18, H-28), (W-18, H-28))
  140.     S.blit(F.render("Green=inside primary  Red=outside  Yellow=custom  |  Click STARS column to change primary  |  Sliders are log-scale", 1, (65,65,95)), (18, H-20))
  141.     pygame.display.flip(); clock.tick(60)
Advertisement
Add Comment
Please, Sign In to add comment