From 6e8c85468d37e1d57bb4e73c008017cb5593fcab Mon Sep 17 00:00:00 2001 From: Soham Pawar Date: Sat, 8 Aug 2026 15:24:18 +0530 Subject: [PATCH 1/5] chore: untrack pyc files and update gitignore --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index e22667c..fc5e5ae 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,14 @@ coverage/ .tmp/ *storybook.log storybook-static + +# ──── Python ─────────────────────────────────── +__pycache__/ +*.py[cod] +*$py.class +.venv/ +venv/ +env/ +.env +.pytest_cache/ +alembic.ini From 5ee10468146223928fd7d5db4fd911cf1b69c543 Mon Sep 17 00:00:00 2001 From: Soham Pawar Date: Sat, 8 Aug 2026 15:32:00 +0530 Subject: [PATCH 2/5] feat: add Cinematic Orbit Camera Mode and cleanup cache --- .../v1/__pycache__/__init__.cpython-313.pyc | Bin 178 -> 0 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 188 -> 0 bytes .../__pycache__/agents.cpython-313.pyc | Bin 5010 -> 0 bytes .../__pycache__/auth.cpython-313.pyc | Bin 4044 -> 0 bytes .../__pycache__/catalog.cpython-313.pyc | Bin 10238 -> 0 bytes .../__pycache__/collisions.cpython-313.pyc | Bin 8995 -> 0 bytes .../__pycache__/dashboard.cpython-313.pyc | Bin 3697 -> 0 bytes .../__pycache__/satellites.cpython-313.pyc | Bin 6497 -> 0 bytes .../__pycache__/weather.cpython-313.pyc | Bin 7736 -> 0 bytes .../app/__pycache__/__init__.cpython-313.pyc | Bin 171 -> 0 bytes backend/app/__pycache__/main.cpython-313.pyc | Bin 7102 -> 0 bytes .../core/__pycache__/__init__.cpython-313.pyc | Bin 176 -> 0 bytes .../core/__pycache__/config.cpython-313.pyc | Bin 2472 -> 0 bytes .../__pycache__/scheduler.cpython-313.pyc | Bin 8951 -> 0 bytes .../core/__pycache__/security.cpython-313.pyc | Bin 2376 -> 0 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 176 -> 0 bytes .../__pycache__/session.cpython-313.pyc | Bin 1349 -> 0 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 174 -> 0 bytes .../__pycache__/db_models.cpython-313.pyc | Bin 31554 -> 0 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 175 -> 0 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 3347 -> 0 bytes .../__pycache__/base.cpython-313.pyc | Bin 4151 -> 0 bytes .../__pycache__/cache.cpython-313.pyc | Bin 5264 -> 0 bytes .../__pycache__/celestrak.cpython-313.pyc | Bin 5982 -> 0 bytes .../__pycache__/chain.cpython-313.pyc | Bin 5148 -> 0 bytes .../__pycache__/spacetrack.cpython-313.pyc | Bin 2516 -> 0 bytes .../test_backend.cpython-312-pytest-9.1.1.pyc | Bin 8018 -> 0 bytes frontend/src/components/EarthTwin.tsx | 13 +- .../src/components/layouts/MainLayout.tsx | 2 + frontend/src/components/ui/CameraControls.tsx | 48 ++++++ frontend/src/hooks/useCinematicCamera.ts | 147 ++++++++++++++++++ frontend/src/store/uiStore.ts | 6 + 32 files changed, 215 insertions(+), 1 deletion(-) delete mode 100644 backend/api/v1/__pycache__/__init__.cpython-313.pyc delete mode 100644 backend/api/v1/endpoints/__pycache__/__init__.cpython-313.pyc delete mode 100644 backend/api/v1/endpoints/__pycache__/agents.cpython-313.pyc delete mode 100644 backend/api/v1/endpoints/__pycache__/auth.cpython-313.pyc delete mode 100644 backend/api/v1/endpoints/__pycache__/catalog.cpython-313.pyc delete mode 100644 backend/api/v1/endpoints/__pycache__/collisions.cpython-313.pyc delete mode 100644 backend/api/v1/endpoints/__pycache__/dashboard.cpython-313.pyc delete mode 100644 backend/api/v1/endpoints/__pycache__/satellites.cpython-313.pyc delete mode 100644 backend/api/v1/endpoints/__pycache__/weather.cpython-313.pyc delete mode 100644 backend/app/__pycache__/__init__.cpython-313.pyc delete mode 100644 backend/app/__pycache__/main.cpython-313.pyc delete mode 100644 backend/app/core/__pycache__/__init__.cpython-313.pyc delete mode 100644 backend/app/core/__pycache__/config.cpython-313.pyc delete mode 100644 backend/app/core/__pycache__/scheduler.cpython-313.pyc delete mode 100644 backend/app/core/__pycache__/security.cpython-313.pyc delete mode 100644 backend/database/__pycache__/__init__.cpython-313.pyc delete mode 100644 backend/database/__pycache__/session.cpython-313.pyc delete mode 100644 backend/models/__pycache__/__init__.cpython-313.pyc delete mode 100644 backend/models/__pycache__/db_models.cpython-313.pyc delete mode 100644 backend/orbital/__pycache__/__init__.cpython-313.pyc delete mode 100644 backend/orbital/providers/__pycache__/__init__.cpython-313.pyc delete mode 100644 backend/orbital/providers/__pycache__/base.cpython-313.pyc delete mode 100644 backend/orbital/providers/__pycache__/cache.cpython-313.pyc delete mode 100644 backend/orbital/providers/__pycache__/celestrak.cpython-313.pyc delete mode 100644 backend/orbital/providers/__pycache__/chain.cpython-313.pyc delete mode 100644 backend/orbital/providers/__pycache__/spacetrack.cpython-313.pyc delete mode 100644 backend/tests/__pycache__/test_backend.cpython-312-pytest-9.1.1.pyc create mode 100644 frontend/src/components/ui/CameraControls.tsx create mode 100644 frontend/src/hooks/useCinematicCamera.ts diff --git a/backend/api/v1/__pycache__/__init__.cpython-313.pyc b/backend/api/v1/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 3059b5c9b58d8611eedf27d58a07bd9394df8823..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178 zcmey&%ge<81e+v+vOx4>5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa|LgvsFxJacWU< zjCYV{ut$u4UaCt`W?5>COKNd;Nq#|$e?e-VLUDd+QF3aGcWOaSYEev5VsdtBUP?@2 zK>-kF#*`Vx#K&jmWtPOp>lIYq;;_lhPbtkwwJTx;+5mDvF^KVznURsPh#ANN0IZoV AHvj+t diff --git a/backend/api/v1/endpoints/__pycache__/__init__.cpython-313.pyc b/backend/api/v1/endpoints/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 7df6f832bec13013eb8c8f4949185c98935096fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 188 zcmey&%ge<81e+v+vOx4>5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa|L!vsFxJacWU< zjCYV{ut$u4UaCt`W?5>COKNd;Nq#|$e?e-VLUDd+QF3aGcWOaSYEev5VsdtBUP?@2 zK>-kF#*`Vx0J#PEnRz9}G4b)4d6^~g@p=W7w>WHa^HWN5QtgUZfp&r1QVe2zWM*V! IEMf+-0O|xXaR2}S diff --git a/backend/api/v1/endpoints/__pycache__/agents.cpython-313.pyc b/backend/api/v1/endpoints/__pycache__/agents.cpython-313.pyc deleted file mode 100644 index f6c61fa3a9ddf6e4aa095042b791e0e83c0d2cc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5010 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 lM8(j9GrjjaK8tN4H^nXrgHjjilDbK^ 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 UH^LqIdW!GJcMxm}9u@ohKO%4#p8x;= diff --git a/backend/api/v1/endpoints/__pycache__/collisions.cpython-313.pyc b/backend/api/v1/endpoints/__pycache__/collisions.cpython-313.pyc deleted file mode 100644 index e7a3898935c6cc4be21683fb4b45da034e70ed7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8995 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 U(sJJVoR%oTS$0i_ee~%3H*D&J_W%F@ diff --git a/backend/api/v1/endpoints/__pycache__/dashboard.cpython-313.pyc b/backend/api/v1/endpoints/__pycache__/dashboard.cpython-313.pyc deleted file mode 100644 index f6217d62cd8de9a3411639f3ba1901aa6c0a2bb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3697 zcmai1UrZax8K3pqYkU1CkN`eR0voveF(HQBK_FZb$pn%E6O(xByGrF85)PfgVq`o?3WHq|}ap%T97oi1vh?u`i4R=rB?H)}5jTH3+8 zJKs0+{h9e@e&5GVARr=WKaD2ujRz3=3nT32u6K4`Lg!;7BZ6d3cEq_U2XRdCgqIl2 z$DLCy;+hhOVA0OFd&)yRR^Ju(PKiXc`a;|{xzZ8_h36xZnkO(=Sdo-@C9fGK=#!+|x;C$aLdL*~D(Q2QxqziC z(HF5M%_MJ2_lZ(0VghW%WVTe`fRNNMCTYqNHlmh)H<(#NG87XR3V9P;oFn>@)ZJio z@00A4RGcMwds2tib#rsmo@TbQKq z*lpqA*|`Jnk5MN|p(NuUYBI-8V3N%|)CQ1!8a3_+S?dJK;P!0u zN5~6D%DZLHK6&R6^4|X;FYc3f9U=kD_01=L-1i6VR52806n)?Wu6LQOtqozdj%nWp*S&ywUX`!V})xHgM z(!5~7d%%MCzj}4RLW)Z{Qhdspa;4lUkK87Qq8?404cbO5B=5*Z3|p z#~qpnJFNNk`EWBM6gnj9%7;!()|Sh zO-+|n#Z;sP#gMWX7{-d01K3=_W>lnN0|}{613W4y1x!qX9?KgUCASEmUcl-+R&P>& zPS@_0Gz-%WDj23>mJI5?Gm#jdOx&WPG7sRMAteo%$y6f2n0^Z;->sn}7pEQ@S*ehK3=o^9j`#p|CuVL>CSubO z4?S)Ya1+$*TT8IEmW-ag2U*6TClqjc8E0xs%UJ1%hKmHNfRrH0wldG~)u@jhw*}ae zj7&jSRC?kd;#)4#wu8()X9LCxFw7_VW7M5W#1k{`CTMVcER#ya-ib|4$Ht~7 zzV&eQ(7}Veh?lTQR@gZ!G99%HStPnzvf^x=o~#0?hNo_$l*>ULqHf5r21J`W+1ZNt z2ozR`9y^rfsb>!MD5Gnb@{r32!x$ROXu6_g=9EGq3t^+)i3d4cWK{wc3i|v!jD)4{$A#{sVCp8_^_fmdyeb0ETJjY8tRpW-WwGHn|r( zX8{fi>1VP$evmU5p0N7FZZv&kG@UXqG1Bqmr2KYzM#JL-WYXiBu}mEq$QXFR)?@q=^7SLM z?SI21R@+|t1V6!R)0MW!R@;?I+m(&(&9-Z+-fF1*lhTvY+P9y)zZJS%30+<%o1xKF z|9|kyBa@#Em51+chLVlZ?^Hs4TcN9!(AABF&%EVp$<2_wD%!H?N~m`$G+GIbZoK{5 zeEF?Ao1wd_;;j_+V-OhgVo;4)(<-H8?N?_R(s!k)$EnTRp)LS@*exh_kph(j691x>sX)K z`2L2rjkw!fN44Y3THn*f^^5CW+dLN-<92!8EAGCI{Gn=7OSNgb8fbpycKU^F(5$FRym@^k%fCnz1zu7&H{H;64=Mum01V8eB;4g;;*X8wza_i_HJ#W42 zy-+>y>jwwHP4KZv{E73MZ5Igs!?+5k;72zuO+?Y}2Ky(%==VK+(0?8dPF&!hpLei+ zFUR^9c-D`KH_!0TM|y5b{2wG2^dqg*onhs8CPUqG3Sd+*Pgp5OobaM8a73f*X0v-A zHENY7VO0eY*m#JI8@UC%q!>{U&D8tWWu+&rQEU~XMznFaEJh6k1aqaB*XD_rsbmEQ zb+fwa{0i*=jYy1fJy)TUEfw%}G6N*5Weox^ zh!n@Ya(FoB-&zoN?khC(CF=hYjeLbp{s{$NdYiVq;TPWU+R&!=^dsSO@9_^i9tm)W z9iAVDe;8h!diuTf!3};x-the9dga{Mqi~tK@%hEU^>==qd4;ZV!dhXMeRg={7zaDh IB?&P5Z&kbL^#A|> diff --git a/backend/api/v1/endpoints/__pycache__/satellites.cpython-313.pyc b/backend/api/v1/endpoints/__pycache__/satellites.cpython-313.pyc deleted file mode 100644 index 4c95a7e61bc091bdece32e86cf4d0b1b720e0475..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6497 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 diff --git a/backend/app/__pycache__/__init__.cpython-313.pyc b/backend/app/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 2d0bc5a43f426f5a4578714e6e8e51baef6f479f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmey&%ge<81P!wzvq1D?5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~hXvQCOKNd;Nq#|$e?e-VLUDd+QF3aGcWOaSYEev5VsdtBUP?@2 uK|xG>d}dx|NqoFsLFFwDo80`A(wtPgB37W$AghZ(jE~HWjEqIhKo$Up4lEA< diff --git a/backend/app/__pycache__/main.cpython-313.pyc b/backend/app/__pycache__/main.cpython-313.pyc deleted file mode 100644 index 4673fac8b8af9589fe2d8646f92fcdb61e0a62de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7102 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 YyzN{6<9jnd>)v3_7HPyZnK{@01K#8X#sB~S diff --git a/backend/app/core/__pycache__/__init__.cpython-313.pyc b/backend/app/core/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index ba0e05efca595f76d053a719e9976b6a6bd4f2f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmey&%ge<81P!wzvq1D?5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~hCOKNd;Nq#|$e?e-VLUDd+QF3aGcWOaSYEev5VsdtBUP?@2 zK|xG%eo<;ne0*kJW=VX!UP0w84x8Nkl+v73yCPPg`5^m?L5z>gjEsy$%s>_Zk`XQ$ diff --git a/backend/app/core/__pycache__/config.cpython-313.pyc b/backend/app/core/__pycache__/config.cpython-313.pyc deleted file mode 100644 index f2d61a7da0cb31d595870919e2e93824d49603c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2472 zcmaJ@&2QVt6(35}haK69zwOwu6>Ec7*+h~*vKst8v_JbS{fKkYpy;h{D9Lf!MS1w~{ocGc z^XAQ)N1hK1^dop)zTErtA3=ov&Pea^b%pah2wx%@$y^TYaD+#?jEWW@8yUj&3&Wn1}Lt-F9fO z)O2*q)Qn=a>*c=qE;*m)SUd_MoQ#M^=7{$u#6x(+r}$;>EijXL5^$N%WquNLS-@pM z(x>!;WuMFX$rYDfaao9jT+0EM4Uz$ug#);$ta;na8@i z;Y+%4S2saMT(3!&O_5QTUF+J9U9z8cbu(n#wYcuGS!JTL#)?OtyM{jH$)ua{2xR<5 z%G1dYnUd$pwZ>>HN`t-BzV^ddt1l;(^P&_HvNTkdN;_h@q85dM*a{@Hrk*%lr2RrU ztKMH!<%&cQrEQAs+AoV~Nvx9i=zYNeDDi>mmboRvhikS*?3L|OmC zS70$Ci9eD=`QG=Mv_5+1I8A#qk$8lg1}2&wKg6Vpjkwlq-eF@fWLQS5 z`G2*`$z+m-g?zsBiI`DKQud>4QEq+E1dj&6_7el<=#kBy@ZqIdSC*F&7TMPw%}^T$ z8mZ};NkbW-B5Vt?sP0Pn)+E6-oyGaqZFPBVBax(il9F)u=02syR4 zPdZpCi$x);vZ{eYrooI@D7n&bQIG{SQ!3`(j`|694v1NW+k5e_3&Z-v`PTqnqFp3i z480YkZYO!Gfg8@Hy7P>n`<(4bAlq{u zkI)qqfw@iYJ;07sM=?)ptE>?lNB*mjBG^2P9BL$@n~oLHA_nxcNZlflbV-s6daY*Q z&oqMLl)Dr2k}!lreRk8(9e6oJZ?Ya}W1~(v%VzgE5}}3arm|~eVky3ExDIIBGN7W3&`V7}p zi|7sAwCPN*QN+Jz6mo#<9{?PozeR6-8(n)9UHdk=^(wmcP4&;w?bC4iRdoBg_OkKE z#!LMdeaGCd0$&7v*$2v3;qpna{O>=7%QpMW{ANP9%Kv^8pbG5b>D9ye45;4i>#VEG zUgCp(?r->Fej2_AH0|!_bpm`^d>qnV`5h|80yLiXw{glS2V0dtK_6IO}@3Ko&u3|Nv$9{}}2 zAy9}=Gfj)FY?*99mOsK~B?GPz)E60Kl+_Tq%3z4WFoO{W>>7|!24f7y8B8#Mx<`b4 zOvyY0H>fz1SY(|**``RK2khfve+Te$)SiqU<&UGMe6&42ceHctJ>}=lf@oro>yQWS zTT3VW+>4P@eyP2%a>7Smfbj~L%wX=QbS$6pwKG4O0(*Cw2SCP?N7-lgDW7OhZgsR< zpuHVG;U}I?zKH(%`YC_69lLv(p7o;zk?Ss$Y0suk`0?iuO9~us#*T`|_nymN6i)dC z;B&F=;KCX2@qNs_sC*Uu?cN*2{@cO61q0s2(n?DV@f&B-`5PWw4 diff --git a/backend/app/core/__pycache__/scheduler.cpython-313.pyc b/backend/app/core/__pycache__/scheduler.cpython-313.pyc deleted file mode 100644 index 2b5cbecbf568ee688e4f17deb3d2be9f905363d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8951 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 T2|8A#9!Ai?I6OiQ?5O@19pGbh diff --git a/backend/app/core/__pycache__/security.cpython-313.pyc b/backend/app/core/__pycache__/security.cpython-313.pyc deleted file mode 100644 index 89dc62a4d8d0b6ca596e1b1af6ade30c5ba67a9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2376 zcmdT_O>7%Q6rT02*MG4cJ3swN(}dKi;zCnVB~<-UQRJE?Hch}ALxZ;%XFW~qI=id4 z>n4^s!0&9$JlB3IYz?Qia}mvtB0wRnP+$M!WNV=FQAE zZ@zuI6AlLnwr>ySs#Px`zi^||e6F(d11h(PLKGoOG6YCQ0AZNho~$P$f|!v&a%?f{ z&B!1-x|H>0{NQ(VZ#Iw#LU5R<{t=?crv$~P_|JMEqy(T#=~F_gq=r>-!h;Zq(xnPY zSPf6eO5`x9N>u?O{}2;(W1>p88dhTXhShGI8|QwADLsxSJRvE)jwikpO!oiIkCyZ> zf4O8+yRt%AfcIruvrFg}r}PH%=Jkr9Cj}-}S8WtsDQK8>%Fu1P#%;evZM&i`SxjEk z_z;syb=$z{K1*AsE7W4aGBtr}xJQ|Jr)UJcB zc{nt;$tXp9ce(_A19@T09Vi_lq~Pj=$=97x;Md@^0!QGq zoWG;(Nkus8uATQ3LLGmeOd?v|6wkOv7Yki&3vos|+MXOCd2!z^-FqV)tq|{Mig;Gk zrM#3NNojUdV7`@#Ubm^mZw!;kL`*HZ1Et!M0Tp|BB^d;sJ|@>~O*d9SLIVQaGcc7( zr4^+(KbK9<7SmThn8~G!S7v7O^JxXwu>m;ynM`%fC{q?x(y3f}zL-rfu)x&&m*;Xb z^V3&0y%6Oqa?lv+TqhFAD7v#kZCNfNfh?ZWVcytW>YSRlC|GJXH=|6eb2_~Qm77$( zM6DaPVXAW`)rT#k4jNUn)T~iZ7fad=s+ZN0X{wq5RJEv9$1^s@%?9hyfFj3=B~7E2 zRkRHZg#_-$GjDy0-8%Wv*ZVYf=+4lsp+~Wi=CxMr^q2ClyN>${->p49Q)uBcs*Q&u|8Z!r6&k#&KMGykk}kH-*Mag(%>IHdGnSTV}=8odY?Fq9wA`U<;kQ4oaZWb8Q^d`5bIBV*4<;^)ZW=5+Ji zgX68p)OzTruAcS43&|%OX!bR&jZ|~(-q1Ed^Wh;!Y<;xwlAAB$JY;l3+)y_B_fEA# slI@U>KXF2~>P{wB#AY&>+I)f&o-%5reCLr%KNa~hCOKNd;Nq#|$e?e-VLUDd+QF3aGcWOaSYEev5VsdtBUP??# zVo73BVsUCre0*kJW=VX!UP0w84x8Nkl+v73yCPPg`5^m?L5z>gjEsy$%s>_Zj<+rd diff --git a/backend/database/__pycache__/session.cpython-313.pyc b/backend/database/__pycache__/session.cpython-313.pyc deleted file mode 100644 index d4270ea2ebb03c5a3183babb0d55524d35e6ae10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1349 zcma(ROK%%Rc=lzlH-0n;L?o)(p#_Ac%1B5NDWV`prh+)6CEFYjD~;B>V|&SZ$NgqE z)LgJQR0Ro;fZUNd@dJ=Ja)NVCL?f#5IQiDdMdHALnYFEo6MWKs^S$PK&rT~92TAzw zr+41_QAg+x#VnvWC#H8Lag1C9$kklEt?lTbV?`TnW5)z@#{z4o0EJv;wu?J9*cf^G zCFI%rD+_mzPuFq_Zqc=`n&5cmCFGXelIOV2RRfluLXuzFKyKNyHjUXGD7%#n1eIR3 zUd!%FnRAozvS8*tp|d`#<2^1a=gKl;gBdF->|= zLa!{m?y|s-an}!02BRz3PeRAKCrCUXIYy2@rT1W3j9=N z*1)(K$M`Ll^jK>fzkr9MfueE1LW=u7#~n%&40OPbXxMNjs_42VDgjVGrDT>;-4JCy zD|O%>QV_Kk4PqZ;h$Fh~b1I7SPROe^r)e4`J$`GV#yfdJc{D_2$XHB-lMJZ=_ z^Z^y+fq#d{@X(Ff``4wZ<7X)g*kBN)qNM0VCNF~lKiS8m{+OWQm|EmwClwOwK zG|dStd-3j~H)vDJ6eQ>ZIzUJ0Si|z%pRaeWnxlm!Mm_ZOf7*1>HRB@cYA+dGt*-6W zby1!#LB6=c3S!2oTnIhvaFq>)4T(izw#0BzGD^UomuP~<=$DlXUpB_u4|T&@J*}Sm z#QxQGKDR%%KXv}Pr_YGLxtjDxOIzA!=4JH7@|O0^%BJz1u_fbIMUjw@1!}VVcI?mI zBB(k=^|7GAU;wINpt=eLrLM^r(+C()iIQ&A1NHY2nFALxcsP~X{CFwJwo!B+LpGOhy4dm C`d$kF diff --git a/backend/models/__pycache__/__init__.cpython-313.pyc b/backend/models/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 8e949627a9e95e4122da59035ec3e013157c1c71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 174 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 Li9g44xTF6cR>}r! diff --git a/backend/orbital/__pycache__/__init__.cpython-313.pyc b/backend/orbital/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 85148d5754d6c0777e33f91c283f5ff97f59c06f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175 zcmey&%ge<81P!wzvq1D?5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~hfvQCOKNd;Nq#|$e?e-VLUDd+QF3aGcWOaSYEev5VsdtBUP?@U yQBr0}Vopqad}dx|NqoFsLFFwDo80`A(wtPgB37XBAnS`kjE~HWjEqIhKo$UA*Db#Q diff --git a/backend/orbital/providers/__pycache__/__init__.cpython-313.pyc b/backend/orbital/providers/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 4cfd82e4c41328d633014a40ec5a67ff2da59745..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3347 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 C99GZ( diff --git a/backend/orbital/providers/__pycache__/cache.cpython-313.pyc b/backend/orbital/providers/__pycache__/cache.cpython-313.pyc deleted file mode 100644 index 74b994c97c7536977423d6bfa9e4814cfc509c15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5264 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 Q9tOB6?u~yC|Ii{QTlSxv+dVyC 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 ZILx2kKou|Dw-}}(U(^0Iq8&@T{{{Tou8#l! diff --git a/backend/orbital/providers/__pycache__/chain.cpython-313.pyc b/backend/orbital/providers/__pycache__/chain.cpython-313.pyc deleted file mode 100644 index 3c7e1699627362088e0b94c889308c9663a19dff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5148 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 diff --git a/backend/orbital/providers/__pycache__/spacetrack.cpython-313.pyc b/backend/orbital/providers/__pycache__/spacetrack.cpython-313.pyc deleted file mode 100644 index 63f2f3a5cf93b160e8b15f805ff23c87071bc9d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2516 zcmah~-EY)J5MSTHR0{o2n^4*$f;J|I7FWix_Yw!kK4yJM zFG5wGDtM?0QX_bzPc2X7&!BIY<{@ZRm8hi;yak1-ubs7h5T&55JtKPA%|sd%yZH7=HkMu2FFmKak&!8tjW7)?TQR0VZ31*y?Aq^Uet z$1P!s3z2+F_?EOc8P+CDgfn7j{+wrckk8tq#&@@ar{trH(pi4SnL}JP*tU z#vw8oP}2ow+70SD^Nd&Wk7bn4lvR=Zz-tI~#IXXQj$@=m_G~RcJEmJ!L`I8@mJ_1= z(>U;_0DKU;G&5kX>jZ4X49wP-=7iNcF*At;AKR756**Gk|~Ysj@s!_7`4%j80*+d3iB#V_=85f{tk;LyY*~t?%TLk;Cz$c;fvlf7%4Dfv1z@^!EICe1x?pmwONv$C*@&!Dt6*xY zQ@-zp;D~G;A@R&PCbBh?^93L5vH~T16PHd?keMM04mZqs({arymx+NF#CWSfpS=A# zk|pw}xbw!yt?`@VYhC4)uJT&f@K5&7?p=4SGQ3h5UM-F^vm+pJIU`bGsfv{A%sGKb z*i+WlsLD8nh&;l@vWzCq+sA~I*fxe@zJ-R3)-JYMxzzIl1T(CZV=W*?DLc5nvLZ0W`Nb5G5|6 zi#-#Vh0t+Y^&#$g`-QT%>YFr zGO>OxBS+-!NdGR3P&Tx+K{x$*V|2k_RA?ju(K;Adr0pWOP8LE$l}kb{Pi#Y!E)f46yd5 z>H*`1ahF6-la&SXHxLrZaSO>ej+&CxtgQ2$IOYG+i!= zjA7Wmg@MSp{`55dW<@$^)Nqcx4!sHel5UC)e#JFi3%gCD;=^3%w&-sC)t~j}%9M$H zMJ5`^N)fqf7L22M1|luZFo-;VLrTc{kJ2goP6W$)a2)q!1LAVAkyI4rNg|`Dn};IK4?XNSwyVT0Nl5FojZ^4WbQtZ7^#O4pAE6(`!W8_OpZ2qqK44dCv0_qFN&_K(sr_dt^qhNVW=HFdWz$kx zn#=5&bM8I&?wvFDJm(J`9Z3P&umADdVm&Gde?!7=vWMLLB_Qt#hG2-ckP+Ek%E?(t z6mm)?BJz=FCJG~x9m~Ztan>i>iCi+1WVm9hxl|^_@QB@!>&$dAJZg94x-;Dj7ww)* z4_m!A(K$VdU)w<4xHKE(+a)1sTa zWK!q6ZJBuoC|NHS;g%|pIm^+^{DPG?f$q*0Y}=w%A>Zs9AQoNH^t^7bP>X8RB+FLT z%<_$J7=s&1=k7M_?_I(1b}0ypQkae73m~^&i2jpQcITEt+@cflS6qbm)7r{HS-j!> zXGJpPid>e;^3oH6P*KVXea}hwGZxu<_9=chfM;GbA~%Zw0?J!!^g^!|I{SR+3Sxt?@i9UXhcJ+w&RukRgKvD(ke)$5;5XNq8zbAMn#3&13C>41{NGC6*xi!9X)$?6w!$^brr*+&W7a5FoIy>UZ+mspgD|V3OJy1 zDxGqre9?{Qgy<^-&w<;U)$MG_)*VyBCG>pObdxj16$j3`rW3bE0|r|oW|3qIxuQ-? zHwDAmT*D^K01T>uX1?$zk!LdiB5tF~Yl=5$-j>p9a+XjGp!aRs;YIkqX#j;|6@ zAa0*FUm!Uh^yiJD4GQAGNIK>!#R5If;0e-&yHxQn`2(E@6URBh7nuwb(=-MXM|r#V zjq|5xUjuQ_Sz3VqnSCX1ULe-8IeWpROHQFU`%2NwkG@(ck*qoUvRSlEGCQYdm(08| zi;|!WYF^iPamC%o1Qb^OKz`&f{BsI^^hW^Jgg>ht@0_}NYP0`PT|K;Z;XZOtLw&NQ zK6!oeW`A9sUc0c}H&|5$uS~8_Tz$5o48pa=NlpZ?hP|AsH_=3owa)3A z&WD%kozJhmust|bRfevdT+d!T*HDJw+TtW90$9UdPSu;}EoEpu8}J0krZPmpv3`zD z-g}5hzz_{7@YcJ;0frpn717TxfuFBPWr_aw!7%}Nb%}X(n6tyHKQI>9pdv5+@LC)&~;VFy7pwd)Q7PS`=rhqm34*||F|9%4ed%Q>rbDCXv z-Ayp;vbAtIEfL%Z`u-EE;ecS02LzMpB*7Ta9cXGVPS)ov`%&~ZLMC7pGKm0nHSCBd z!o(EC*UOwT*WCR~b6aC(Exd`-Far$;{V{+w;p486M%PrWYwG6whuM19^J_1D+&$Xp zp00JHw!T{Les1lhZMARRs;MIl1+Oh-_C5#hWhW)ABeX_$X!xjNV4)TS|TpV zvS4(e_pXRWXC(q>EwVK0c@8^t*4mxVE_Ob-!2HI}x{7X*i@=DHBs~Ns4r09C!;RjlTJO~M@W}T5$G7*7{XM3f zjNTQLzS!E!pCyIV-l}rw%9(4=R@Fld1+OhmbE1v}M*|2Gu0dLcyfEye!7%UMRtKue z*p<`QhO6pWL&0l{)10Uy!O;N1glogWu#X1AfWWM6b#T30Q^%^xk?S%5SpK?PQ;#$h zytcd!PSqL3@lE9CXc*p7jy%AJx!i|=Jb7CLy0saBH>n6^VNq!vXI4XkC{rv;tcoO; zW%?@z&l78lZ8X~e9*8#;oYS-#M&uE^RdfB_yj7F4UA-yntsqJi8TfFwbHwv=XLWEv+0_z&3VE2AK3j7`v>Y`O%&y0bB-Ht0 zWw|6&;^jECL;u_E{ix+Qyq^@#XuhA&oOaR)Z0s(4K`dl+cvd}ObQ)ddgeRTkLrW*9 z^|nYS239>@k&W(33?$IQB*2~?Vy=gI`d;?*egE4}&wSeI&{4f;9bG15^8-@#E~g@6rlbpq23NHUT?@Q_jNJfH|U4mCrkM73BeX&n>8uD1PP90(XgMgDD!~er^pMq$cqSG0^s&K1WR-V zlouTnitgYNpbn}#I+=mt9cyqe3Tr?GG@4~4d{fwfh*Ou48sXC)LzKQ%C4MSkr#>FN-c;@=IYX@OPSZt&Uhzi_zZTUT%tTU41n}8qm z*mw_zx0J~TxW3kA~&&ba^Axjry&iHoZPzIn6q5V=~$)1**xOzY?H za(RvOoA6XzjbBCV5`xzNfO2OL|1JW^b%ckt4e4mpDvVf5?6Ds;e#xY zMGg1%sIfhgEbtw}32y?xYNe;$PuJD)|G0L80bsh49(LpYhaXnTb%!+msc4*koPp0b zl5X5wIpO~-=oQ;~crS6tx*ZT8j=!@m*~J~@p5 zlL}^S*-P0Fm?-$$i=W9AjFN4hBi{!mI938h|MZzGisD}c;Vt3Kj@v@|wy^iM@bqnA z>b7w5Bcbafq4%$ezV-8OkE})SD2lk}PD&B?-AP5n(L0@S@u@rg5pn8nrz9R^|MX9x C5eC%& diff --git a/frontend/src/components/EarthTwin.tsx b/frontend/src/components/EarthTwin.tsx index 048f33d..a60e150 100644 --- a/frontend/src/components/EarthTwin.tsx +++ b/frontend/src/components/EarthTwin.tsx @@ -1,5 +1,8 @@ import React, { useEffect, useRef, useState, useCallback, useImperativeHandle, forwardRef } from 'react'; import { prefersReducedMotion } from './SatelliteSpotlight/GlowEffect'; +import { useSatelliteSelection } from '@/hooks/useSatelliteSelection'; +import { useSpotlightEffect } from '@/hooks/useSpotlightEffect'; +import { useCinematicCamera } from '@/hooks/useCinematicCamera'; import { keplerToLatLonAlt } from '@/utils/orbitCalc'; import { useUIStore } from '@/store/uiStore'; import { logEvent } from '@/store/logbookStore'; @@ -116,7 +119,7 @@ export const EarthTwin = forwardRef((_props, ref) => { const [viewerInstance, setViewerInstance] = useState(null); const [containerEl, setContainerEl] = useState(null); const [datasetVersion, setDatasetVersion] = useState(0); - const { activeSector, setSelectedSatelliteId } = useUIStore(); + const { activeSector, setSelectedSatelliteId, selectedSatelliteId } = useUIStore(); const [useFallback, setUseFallback] = useState(true); // Set by flyToSatellite(), consumed by SpotlightManager to lock its // selection/info-card onto a satellite chosen from outside the globe @@ -349,6 +352,14 @@ export const EarthTwin = forwardRef((_props, ref) => { const [selectedBookmarkId, setSelectedBookmarkId] = useState(''); + useSpotlightEffect({ + viewer: viewerInstance, + selectedId: selectedSatelliteId, + entitiesRef, + hoveredId: hoveredObject?.catalog_number ?? null, + }); + + useCinematicCamera(viewerInstance, entitiesRef, catalogMapRef); const saveCurrentView = useCallback(() => { const viewer = viewerRef.current; diff --git a/frontend/src/components/layouts/MainLayout.tsx b/frontend/src/components/layouts/MainLayout.tsx index f08295a..d174c0c 100644 --- a/frontend/src/components/layouts/MainLayout.tsx +++ b/frontend/src/components/layouts/MainLayout.tsx @@ -7,6 +7,7 @@ import { DynamicBackground } from '@/components/DynamicBackground/DynamicBackgro import { LogbookPanel } from '@/components/Logbook/LogbookPanel'; import { useLogbookStore, logEvent } from '@/store/logbookStore'; import { NotificationCenter } from '@/components/ui/NotificationCenter'; +import { CameraControls } from '@/components/ui/CameraControls'; /** Ensures the mission-init System log fires once per browser tab session. */ let hasLoggedMissionInit = false; @@ -413,6 +414,7 @@ export const MainLayout: React.FC = () => { + ); diff --git a/frontend/src/components/ui/CameraControls.tsx b/frontend/src/components/ui/CameraControls.tsx new file mode 100644 index 0000000..e06c912 --- /dev/null +++ b/frontend/src/components/ui/CameraControls.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { useUIStore, CameraMode } from '@/store/uiStore'; +import { MaterialIcon } from '../MaterialIcon'; + +export const CameraControls: React.FC = () => { + const { selectedSatelliteId, cameraMode, setCameraMode } = useUIStore(); + + const modes: { id: CameraMode; icon: string; label: string }[] = [ + { id: 'FREE', icon: 'pan_tool', label: 'FREE' }, + { id: 'CHASE', icon: 'flight_takeoff', label: 'CHASE' }, + { id: 'COCKPIT', icon: 'visibility', label: 'COCKPIT' }, + { id: 'EARTH_OBSERVER', icon: 'public', label: 'EARTH' }, + { id: 'ORBITAL', icon: 'satellite_alt', label: 'ORBITAL' }, + ]; + + return ( + + {selectedSatelliteId && ( + + {modes.map((mode) => { + const isActive = cameraMode === mode.id; + return ( + + ); + })} + + )} + + ); +}; diff --git a/frontend/src/hooks/useCinematicCamera.ts b/frontend/src/hooks/useCinematicCamera.ts new file mode 100644 index 0000000..d63858e --- /dev/null +++ b/frontend/src/hooks/useCinematicCamera.ts @@ -0,0 +1,147 @@ +import { useEffect, useRef } from 'react'; +import * as Cesium from 'cesium'; +import { useUIStore, CameraMode } from '@/store/uiStore'; +import { CatalogObject } from '@/types/satellite'; +import { keplerToLatLonAlt } from '@/utils/orbitCalc'; + +const LERP_FACTOR = 0.08; // Controls how fast the camera catches up to the target position + +export function useCinematicCamera( + viewer: Cesium.Viewer | null, + entitiesRef: React.RefObject>, + catalogMapRef: React.RefObject> +) { + const lastMode = useRef('FREE'); + + useEffect(() => { + if (!viewer) return; + + const onPreRender = () => { + const state = useUIStore.getState(); + const mode = state.cameraMode; + const targetId = state.selectedSatelliteId; + + // Handle transitioning to FREE mode + if (mode === 'FREE' || !targetId) { + if (lastMode.current !== 'FREE') { + lastMode.current = 'FREE'; + // Release any overrides if needed, but Cesium camera allows manual control natively + // when we stop overriding it in preRender. + } + return; + } + + const entity = entitiesRef.current?.get(targetId); + const catalogData = catalogMapRef.current?.get(targetId); + + if (!entity || !catalogData) return; + + lastMode.current = mode; + + // 1. Calculate precise real-time position and velocity + const now = new Date(); + const p0_geo = keplerToLatLonAlt(catalogData, 0); + const p1_geo = keplerToLatLonAlt(catalogData, 1); // 1 second into future + + if (!p0_geo || !p1_geo) return; + + const p0 = Cesium.Cartesian3.fromDegrees(p0_geo.lon, p0_geo.lat, p0_geo.alt * 1000); + const p1 = Cesium.Cartesian3.fromDegrees(p1_geo.lon, p1_geo.lat, p1_geo.alt * 1000); + + // 2. Update physical entity position so it visibly moves! + entity.position = new Cesium.ConstantPositionProperty(p0); + + // 3. Calculate camera targets + const velocity = Cesium.Cartesian3.subtract(p1, p0, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(velocity, velocity); + + // 'up' is the normal vector from earth center to the satellite + const up = Cesium.Cartesian3.normalize(p0, new Cesium.Cartesian3()); + + let targetPos = new Cesium.Cartesian3(); + let targetDir = new Cesium.Cartesian3(); + let targetUp = new Cesium.Cartesian3(); + + switch (mode) { + case 'CHASE': { + // Behind and slightly above + const distanceBehind = 80000; // 80 km + const heightAbove = 20000; // 20 km + + const backward = Cesium.Cartesian3.multiplyByScalar(velocity, -distanceBehind, new Cesium.Cartesian3()); + const upward = Cesium.Cartesian3.multiplyByScalar(up, heightAbove, new Cesium.Cartesian3()); + + Cesium.Cartesian3.add(p0, backward, targetPos); + Cesium.Cartesian3.add(targetPos, upward, targetPos); + + // Look at the satellite + Cesium.Cartesian3.subtract(p0, targetPos, targetDir); + Cesium.Cartesian3.normalize(targetDir, targetDir); + targetUp = up; + break; + } + case 'COCKPIT': { + // Exactly at the satellite, looking forward along velocity + // Slightly offset forward so we don't clip inside the point primitive + const offset = Cesium.Cartesian3.multiplyByScalar(velocity, 1000, new Cesium.Cartesian3()); + Cesium.Cartesian3.add(p0, offset, targetPos); + + targetDir = velocity; + targetUp = up; + break; + } + case 'EARTH_OBSERVER': { + // From the satellite, looking down at Earth (0,0,0) + const offset = Cesium.Cartesian3.multiplyByScalar(up, 1000, new Cesium.Cartesian3()); + Cesium.Cartesian3.add(p0, offset, targetPos); + + Cesium.Cartesian3.negate(up, targetDir); + // When looking straight down, 'up' for the camera needs to be the velocity vector + targetUp = velocity; + break; + } + case 'ORBITAL': { + // Far above, looking down at the satellite + const heightAbove = 500000; // 500 km + const upward = Cesium.Cartesian3.multiplyByScalar(up, heightAbove, new Cesium.Cartesian3()); + Cesium.Cartesian3.add(p0, upward, targetPos); + + Cesium.Cartesian3.negate(up, targetDir); + targetUp = velocity; + break; + } + } + + // 4. Smoothly interpolate current camera towards target + const currentPos = viewer.camera.position; + const currentDir = viewer.camera.direction; + const currentUp = viewer.camera.up; + + const nextPos = new Cesium.Cartesian3(); + const nextDir = new Cesium.Cartesian3(); + const nextUp = new Cesium.Cartesian3(); + + Cesium.Cartesian3.lerp(currentPos, targetPos, LERP_FACTOR, nextPos); + Cesium.Cartesian3.lerp(currentDir, targetDir, LERP_FACTOR, nextDir); + Cesium.Cartesian3.lerp(currentUp, targetUp, LERP_FACTOR, nextUp); + + Cesium.Cartesian3.normalize(nextDir, nextDir); + Cesium.Cartesian3.normalize(nextUp, nextUp); + + // Force the camera view + viewer.camera.setView({ + destination: nextPos, + orientation: { + direction: nextDir, + up: nextUp, + } + }); + }; + + viewer.scene.preRender.addEventListener(onPreRender); + + return () => { + viewer.scene.preRender.removeEventListener(onPreRender); + }; + }, [viewer, entitiesRef, catalogMapRef]); +} diff --git a/frontend/src/store/uiStore.ts b/frontend/src/store/uiStore.ts index ccdeafd..59dd3e9 100644 --- a/frontend/src/store/uiStore.ts +++ b/frontend/src/store/uiStore.ts @@ -1,6 +1,8 @@ import { create } from 'zustand'; import { logEvent } from './logbookStore'; +export type CameraMode = 'FREE' | 'CHASE' | 'COCKPIT' | 'EARTH_OBSERVER' | 'ORBITAL'; + interface UIState { sidebarCollapsed: boolean; rightDrawerOpen: boolean; @@ -10,6 +12,7 @@ interface UIState { activeSector: string; globalSearchOpen: boolean; isFlybyHistoryOpen: boolean; + cameraMode: CameraMode; toggleSidebar: () => void; setSidebarCollapsed: (collapsed: boolean) => void; toggleRightDrawer: () => void; @@ -21,6 +24,7 @@ interface UIState { setActiveSector: (sector: string) => void; setGlobalSearchOpen: (open: boolean) => void; toggleFlybyHistory: () => void; + setCameraMode: (mode: CameraMode) => void; } export const useUIStore = create((set) => ({ @@ -32,6 +36,7 @@ export const useUIStore = create((set) => ({ activeSector: '', globalSearchOpen: false, isFlybyHistoryOpen: false, + cameraMode: 'FREE', toggleSidebar: () => set((state) => ({ sidebarCollapsed: !state.sidebarCollapsed })), setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }), toggleRightDrawer: () => set((state) => ({ rightDrawerOpen: !state.rightDrawerOpen })), @@ -62,4 +67,5 @@ export const useUIStore = create((set) => ({ setActiveSector: (sector) => set({ activeSector: sector }), setGlobalSearchOpen: (open) => set({ globalSearchOpen: open }), toggleFlybyHistory: () => set((state) => ({ isFlybyHistoryOpen: !state.isFlybyHistoryOpen })), + setCameraMode: (mode) => set({ cameraMode: mode }), })); From 6de27f019d5a8d626d0ef8248db1e66c0872e0c4 Mon Sep 17 00:00:00 2001 From: Soham Pawar Date: Sat, 8 Aug 2026 16:16:36 +0530 Subject: [PATCH 3/5] feat: add procedural space background and fix type imports --- frontend/src/components/EarthTwin.tsx | 27 ++- frontend/src/components/ui/CameraControls.tsx | 3 +- .../ui/ProceduralSpaceBackground.tsx | 159 ++++++++++++++++++ frontend/src/hooks/useCinematicCamera.ts | 5 +- 4 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 frontend/src/components/ui/ProceduralSpaceBackground.tsx diff --git a/frontend/src/components/EarthTwin.tsx b/frontend/src/components/EarthTwin.tsx index a60e150..8349bab 100644 --- a/frontend/src/components/EarthTwin.tsx +++ b/frontend/src/components/EarthTwin.tsx @@ -3,6 +3,7 @@ import { prefersReducedMotion } from './SatelliteSpotlight/GlowEffect'; import { useSatelliteSelection } from '@/hooks/useSatelliteSelection'; import { useSpotlightEffect } from '@/hooks/useSpotlightEffect'; import { useCinematicCamera } from '@/hooks/useCinematicCamera'; +import { ProceduralSpaceBackground } from '@/components/ui/ProceduralSpaceBackground'; import { keplerToLatLonAlt } from '@/utils/orbitCalc'; import { useUIStore } from '@/store/uiStore'; import { logEvent } from '@/store/logbookStore'; @@ -356,6 +357,9 @@ export const EarthTwin = forwardRef((_props, ref) => { viewer: viewerInstance, selectedId: selectedSatelliteId, entitiesRef, + catalogMapRef, + collisionSetRef, + datasetVersion, hoveredId: hoveredObject?.catalog_number ?? null, }); @@ -553,6 +557,11 @@ export const EarthTwin = forwardRef((_props, ref) => { navigationInstructionsInitiallyVisible: false, scene3DOnly: true, shouldAnimate: true, + contextOptions: { + webgl: { + alpha: true, + } + }, }); viewerRef.current = viewer; @@ -565,7 +574,10 @@ export const EarthTwin = forwardRef((_props, ref) => { viewer.scene.globe.enableLighting = true; // Enable real-time day/night lighting - viewer.scene.backgroundColor = Cesium.Color.fromCssColorString('#050710'); + viewer.scene.backgroundColor = Cesium.Color.TRANSPARENT; + viewer.scene.skyBox.show = false; + viewer.scene.sun.show = false; + viewer.scene.moon.show = false; viewer.scene.fog.enabled = false; if (viewer.scene.skyAtmosphere) { viewer.scene.skyAtmosphere.show = true; @@ -715,10 +727,15 @@ export const EarthTwin = forwardRef((_props, ref) => { useImperativeHandle(ref, () => ({ flyToSatellite }), [flyToSatellite]); return ( -
- {/* 3D Cesium Container */} -
- +
+ + {/* The Cesium container must sit cleanly in the background */} +
{/* High-Fidelity SVG Fallback */} {useFallback && (
diff --git a/frontend/src/components/ui/CameraControls.tsx b/frontend/src/components/ui/CameraControls.tsx index e06c912..c45cd83 100644 --- a/frontend/src/components/ui/CameraControls.tsx +++ b/frontend/src/components/ui/CameraControls.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { motion, AnimatePresence } from 'framer-motion'; -import { useUIStore, CameraMode } from '@/store/uiStore'; +import { useUIStore } from '@/store/uiStore'; +import type { CameraMode } from '@/store/uiStore'; import { MaterialIcon } from '../MaterialIcon'; export const CameraControls: React.FC = () => { diff --git a/frontend/src/components/ui/ProceduralSpaceBackground.tsx b/frontend/src/components/ui/ProceduralSpaceBackground.tsx new file mode 100644 index 0000000..9172437 --- /dev/null +++ b/frontend/src/components/ui/ProceduralSpaceBackground.tsx @@ -0,0 +1,159 @@ +import React, { useEffect, useRef } from 'react'; +import * as Cesium from 'cesium'; + +interface Props { + viewer: Cesium.Viewer | null; +} + +const TILE_SIZE = 2048; + +function createOffscreenCanvas(width: number, height: number): HTMLCanvasElement { + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + return canvas; +} + +function drawStars(ctx: CanvasRenderingContext2D, count: number, maxRadius: number, colors: string[]) { + for (let i = 0; i < count; i++) { + const x = Math.random() * TILE_SIZE; + const y = Math.random() * TILE_SIZE; + const radius = Math.random() * maxRadius + 0.1; + const color = colors[Math.floor(Math.random() * colors.length)]; + + ctx.beginPath(); + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.fillStyle = color; + ctx.fill(); + + // Add subtle glow for larger stars + if (radius > 1) { + ctx.shadowBlur = 10; + ctx.shadowColor = color; + ctx.fill(); + ctx.shadowBlur = 0; + } + } +} + +function drawNebula(ctx: CanvasRenderingContext2D, count: number) { + for (let i = 0; i < count; i++) { + const x = Math.random() * TILE_SIZE; + const y = Math.random() * TILE_SIZE; + const radius = Math.random() * 400 + 100; + + // Soft subtle colors + const r = Math.floor(Math.random() * 30 + 10); + const g = Math.floor(Math.random() * 20 + 5); + const b = Math.floor(Math.random() * 50 + 30); + + const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius); + gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0.15)`); + gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`); + + ctx.beginPath(); + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.fillStyle = gradient; + ctx.fill(); + } +} + +export const ProceduralSpaceBackground: React.FC = ({ viewer }) => { + const canvasRef = useRef(null); + + useEffect(() => { + if (!viewer || !canvasRef.current) return; + + const canvas = canvasRef.current; + const ctx = canvas.getContext('2d', { alpha: false }); + if (!ctx) return; + + // Resize observer to keep main canvas full screen + const resizeObserver = new ResizeObserver(() => { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + }); + resizeObserver.observe(document.body); + + // Pre-generate layers + const layer1 = createOffscreenCanvas(TILE_SIZE, TILE_SIZE); + const ctx1 = layer1.getContext('2d')!; + ctx1.fillStyle = '#030508'; + ctx1.fillRect(0, 0, TILE_SIZE, TILE_SIZE); + drawNebula(ctx1, 8); + drawStars(ctx1, 2000, 0.8, ['#ffffff', '#aaddff', '#ffddaa', '#444455']); // Background faint stars + + const layer2 = createOffscreenCanvas(TILE_SIZE, TILE_SIZE); + const ctx2 = layer2.getContext('2d')!; + drawStars(ctx2, 800, 1.2, ['#ffffff', '#aaddff', '#ffcc88']); // Midground stars + + const layer3 = createOffscreenCanvas(TILE_SIZE, TILE_SIZE); + const ctx3 = layer3.getContext('2d')!; + drawStars(ctx3, 200, 2.0, ['#ffffff', '#00e5ff', '#ffaa00']); // Foreground bright stars + + const drawTiled = (source: HTMLCanvasElement, xOffset: number, yOffset: number) => { + // Wrap offsets + let ox = xOffset % TILE_SIZE; + if (ox < 0) ox += TILE_SIZE; + let oy = yOffset % TILE_SIZE; + if (oy < 0) oy += TILE_SIZE; + + // Calculate how many tiles we need to cover the screen + const startX = -ox; + const startY = -oy; + + for (let x = startX; x < canvas.width; x += TILE_SIZE) { + for (let y = startY; y < canvas.height; y += TILE_SIZE) { + ctx.drawImage(source, x, y); + } + } + }; + + const onPreRender = () => { + // Extract camera state + const heading = viewer.camera.heading; // 0 to 2PI + const pitch = viewer.camera.pitch; // -PI/2 to PI/2 + + // We also use altitude to slightly scale or shift things + const alt = viewer.camera.positionCartographic.height; + // Normal map altitude between 1000km and 50000km + const zoomFactor = Math.max(0, Math.min(1, (alt - 1000000) / 40000000)); + + // 360 degrees = 4000 pixels of scrolling + const radToPixels = 4000 / (Math.PI * 2); + + const baseX = heading * radToPixels; + const baseY = pitch * radToPixels; + + ctx.fillStyle = '#030508'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Draw layers with parallax + // Layer 1 (slowest, background) + drawTiled(layer1, baseX * 0.1, baseY * 0.1); + + // Layer 2 (mid) + drawTiled(layer2, baseX * 0.3, baseY * 0.3 + (zoomFactor * 50)); + + // Layer 3 (fastest, foreground) + drawTiled(layer3, baseX * 0.6, baseY * 0.6 + (zoomFactor * 150)); + }; + + viewer.scene.preRender.addEventListener(onPreRender); + + return () => { + resizeObserver.disconnect(); + if (viewer && !viewer.isDestroyed()) { + viewer.scene.preRender.removeEventListener(onPreRender); + } + }; + }, [viewer]); + + return ( + + ); +}; diff --git a/frontend/src/hooks/useCinematicCamera.ts b/frontend/src/hooks/useCinematicCamera.ts index d63858e..45a0475 100644 --- a/frontend/src/hooks/useCinematicCamera.ts +++ b/frontend/src/hooks/useCinematicCamera.ts @@ -1,7 +1,8 @@ import { useEffect, useRef } from 'react'; import * as Cesium from 'cesium'; -import { useUIStore, CameraMode } from '@/store/uiStore'; -import { CatalogObject } from '@/types/satellite'; +import { useUIStore } from '@/store/uiStore'; +import type { CameraMode } from '@/store/uiStore'; +import type { CatalogObject } from '@/types/satellite'; import { keplerToLatLonAlt } from '@/utils/orbitCalc'; const LERP_FACTOR = 0.08; // Controls how fast the camera catches up to the target position From d0e205c5ece19c18f95f577cc62311baffc4e85f Mon Sep 17 00:00:00 2001 From: Soham Pawar Date: Mon, 10 Aug 2026 02:02:14 +0530 Subject: [PATCH 4/5] fix: correct space background z-index stacking and add offline globe fallback --- frontend/src/components/EarthTwin.tsx | 14 ++++++++++++-- .../components/ui/ProceduralSpaceBackground.tsx | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/EarthTwin.tsx b/frontend/src/components/EarthTwin.tsx index 8349bab..627ef78 100644 --- a/frontend/src/components/EarthTwin.tsx +++ b/frontend/src/components/EarthTwin.tsx @@ -544,7 +544,7 @@ export const EarthTwin = forwardRef((_props, ref) => { try { const viewer = new Cesium.Viewer(containerRef.current, { animation: false, - baseLayerPicker: false, + baseLayer: false, // Disable default Ion imagery to prevent 401s fullscreenButton: false, vrButton: false, geocoder: false, @@ -564,6 +564,15 @@ export const EarthTwin = forwardRef((_props, ref) => { }, }); + // Add offline Natural Earth imagery layer as fallback + Cesium.TileMapServiceImageryProvider.fromUrl( + Cesium.buildModuleUrl('Assets/Textures/NaturalEarthII') + ).then((provider) => { + if (viewerRef.current && !viewerRef.current.isDestroyed()) { + viewerRef.current.imageryLayers.addImageryProvider(provider); + } + }); + viewerRef.current = viewer; // eslint-disable-next-line react-hooks/set-state-in-effect setUseFallback(false); @@ -574,6 +583,7 @@ export const EarthTwin = forwardRef((_props, ref) => { viewer.scene.globe.enableLighting = true; // Enable real-time day/night lighting + viewer.scene.globe.baseColor = Cesium.Color.fromCssColorString('#1a1a24'); // Visible fallback color viewer.scene.backgroundColor = Cesium.Color.TRANSPARENT; viewer.scene.skyBox.show = false; viewer.scene.sun.show = false; @@ -734,7 +744,7 @@ export const EarthTwin = forwardRef((_props, ref) => { {/* The Cesium container must sit cleanly in the background */}
{/* High-Fidelity SVG Fallback */} {useFallback && ( diff --git a/frontend/src/components/ui/ProceduralSpaceBackground.tsx b/frontend/src/components/ui/ProceduralSpaceBackground.tsx index 9172437..6df0469 100644 --- a/frontend/src/components/ui/ProceduralSpaceBackground.tsx +++ b/frontend/src/components/ui/ProceduralSpaceBackground.tsx @@ -152,7 +152,7 @@ export const ProceduralSpaceBackground: React.FC = ({ viewer }) => { return ( ); From f80ccfca7af487b506b79adaff50de99fca50d74 Mon Sep 17 00:00:00 2001 From: Soham Pawar Date: Mon, 10 Aug 2026 10:25:24 +0530 Subject: [PATCH 5/5] ci: fix pytest collection by setting PYTHONPATH to backend dir --- .github/workflows/backend-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index f833a7b..2f2e3ce 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -48,6 +48,7 @@ jobs: - name: Run tests env: + PYTHONPATH: . DATABASE_URL: postgresql://postgres:postgres@localhost:5432/orbital_guardian_test SECRET_KEY: ci_test_secret_key_not_for_production ALLOWED_ORIGINS: http://localhost:5173