Create rahuketu5
Browse files
rahuketu5
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Rahu & Ketu: The Lunar Nodes</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| 9 |
+
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
|
| 10 |
+
<style>
|
| 11 |
+
body { margin: 0; overflow: hidden; background-color: #030308; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: white; }
|
| 12 |
+
#canvas-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; }
|
| 13 |
+
#ui-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10; pointer-events: none; }
|
| 14 |
+
.pointer-events-auto { pointer-events: auto; }
|
| 15 |
+
|
| 16 |
+
/* Glassmorphism panel */
|
| 17 |
+
.glass-panel {
|
| 18 |
+
background: rgba(15, 20, 35, 0.7);
|
| 19 |
+
backdrop-filter: blur(12px);
|
| 20 |
+
-webkit-backdrop-filter: blur(12px);
|
| 21 |
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 22 |
+
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
/* Custom Scrollbar for text panel */
|
| 26 |
+
::-webkit-scrollbar { width: 6px; }
|
| 27 |
+
::-webkit-scrollbar-track { background: rgba(0,0,0,0.1); }
|
| 28 |
+
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 10px; }
|
| 29 |
+
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.4); }
|
| 30 |
+
|
| 31 |
+
input[type=range] {
|
| 32 |
+
-webkit-appearance: none; background: transparent;
|
| 33 |
+
}
|
| 34 |
+
input[type=range]::-webkit-slider-thumb {
|
| 35 |
+
-webkit-appearance: none; height: 16px; width: 16px; border-radius: 50%;
|
| 36 |
+
background: #4ade80; cursor: pointer; margin-top: -6px;
|
| 37 |
+
}
|
| 38 |
+
input[type=range]::-webkit-slider-runnable-track {
|
| 39 |
+
width: 100%; height: 4px; cursor: pointer; background: rgba(255,255,255,0.2); border-radius: 2px;
|
| 40 |
+
}
|
| 41 |
+
</style>
|
| 42 |
+
</head>
|
| 43 |
+
<body>
|
| 44 |
+
|
| 45 |
+
<!-- 3D Canvas -->
|
| 46 |
+
<div id="canvas-container"></div>
|
| 47 |
+
|
| 48 |
+
<!-- UI Overlay -->
|
| 49 |
+
<div id="ui-layer" class="flex flex-col md:flex-row justify-between p-4 md:p-6 h-screen relative">
|
| 50 |
+
|
| 51 |
+
<!-- Left Info Panel -->
|
| 52 |
+
<div class="glass-panel w-full md:w-96 rounded-2xl p-6 flex flex-col pointer-events-auto h-auto max-h-[45vh] md:max-h-full overflow-y-auto mb-4 md:mb-0">
|
| 53 |
+
<h1 class="text-3xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-orange-400 to-yellow-200 mb-2">Rahu & Ketu</h1>
|
| 54 |
+
<h2 class="text-sm text-gray-300 uppercase tracking-widest mb-6 border-b border-gray-600 pb-2">The Lunar Nodes</h2>
|
| 55 |
+
|
| 56 |
+
<div class="space-y-4 text-sm leading-relaxed text-gray-200">
|
| 57 |
+
<p>
|
| 58 |
+
<strong class="text-white text-base">What are they?</strong><br>
|
| 59 |
+
In astronomy, Rahu and Ketu are not physical planets. They are mathematical intersection points known as the <strong>Lunar Nodes</strong>.
|
| 60 |
+
</p>
|
| 61 |
+
<p>
|
| 62 |
+
The Earth orbits the Sun on a flat plane called the <em>Ecliptic</em>. However, the Moon's orbit around the Earth is tilted by about 5 degrees relative to this plane.
|
| 63 |
+
</p>
|
| 64 |
+
<p>
|
| 65 |
+
Because of this tilt, the Moon's orbit crosses the Earth's Ecliptic plane at exactly two points.
|
| 66 |
+
</p>
|
| 67 |
+
|
| 68 |
+
<div class="bg-orange-500/10 border border-orange-500/30 p-3 rounded-lg mt-2">
|
| 69 |
+
<h3 class="text-orange-400 font-bold mb-1">Rahu (North Node) ☊</h3>
|
| 70 |
+
<p class="text-xs">The <em>Ascending Node</em>. This is the exact point where the Moon crosses the ecliptic going from South to North.</p>
|
| 71 |
+
</div>
|
| 72 |
+
|
| 73 |
+
<div class="bg-gray-500/10 border border-gray-500/30 p-3 rounded-lg mt-2">
|
| 74 |
+
<h3 class="text-gray-300 font-bold mb-1">Ketu (South Node) ☋</h3>
|
| 75 |
+
<p class="text-xs">The <em>Descending Node</em>. This is the exact point where the Moon crosses the ecliptic going from North to South.</p>
|
| 76 |
+
</div>
|
| 77 |
+
|
| 78 |
+
<div class="bg-purple-500/10 border border-purple-500/30 p-3 rounded-lg mt-2">
|
| 79 |
+
<h3 class="text-purple-400 font-bold mb-1">The 18.6 Year Cycle</h3>
|
| 80 |
+
<p class="text-xs">The nodes are not stationary! The Moon's orbital plane slowly wobbles, causing the nodes to drift backwards (retrograde). It takes exactly <strong>18.6 years</strong> for Rahu and Ketu to complete one full cycle around the Zodiac. This is called <em>Nodal Precession</em>.</p>
|
| 81 |
+
</div>
|
| 82 |
+
|
| 83 |
+
<p class="mt-4 border-t border-gray-600 pt-4">
|
| 84 |
+
<strong class="text-yellow-300">Eclipses:</strong> A Solar or Lunar eclipse can <em>only</em> happen when the New Moon or Full Moon aligns closely with either Rahu or Ketu. In ancient mythology, these nodes were personified as shadow demons that "swallowed" the Sun and Moon.
|
| 85 |
+
</p>
|
| 86 |
+
</div>
|
| 87 |
+
</div>
|
| 88 |
+
|
| 89 |
+
<!-- Right / Bottom Controls -->
|
| 90 |
+
<div class="flex flex-col justify-end pointer-events-auto w-full md:w-auto">
|
| 91 |
+
<div class="glass-panel rounded-2xl p-5 w-full md:w-80 space-y-5">
|
| 92 |
+
|
| 93 |
+
<div class="flex items-center justify-between mb-2">
|
| 94 |
+
<h3 class="font-semibold text-white">Controls</h3>
|
| 95 |
+
<button id="btn-pause" class="bg-white/10 hover:bg-white/20 text-white px-3 py-1 rounded text-sm transition">Pause</button>
|
| 96 |
+
</div>
|
| 97 |
+
|
| 98 |
+
<!-- Time Speed -->
|
| 99 |
+
<div>
|
| 100 |
+
<label class="flex justify-between text-xs text-gray-400 mb-2">
|
| 101 |
+
<span>Time Speed</span>
|
| 102 |
+
<span id="speed-val">1x</span>
|
| 103 |
+
</label>
|
| 104 |
+
<input type="range" id="time-speed" min="0" max="5" step="0.1" value="1" class="w-full">
|
| 105 |
+
</div>
|
| 106 |
+
|
| 107 |
+
<!-- Camera Focus -->
|
| 108 |
+
<div>
|
| 109 |
+
<label class="block text-xs text-gray-400 mb-2">Camera Focus</label>
|
| 110 |
+
<div class="flex bg-black/40 rounded-lg p-1">
|
| 111 |
+
<button id="focus-sun" class="flex-1 py-1.5 text-sm rounded-md transition text-gray-400 hover:text-white">Sun</button>
|
| 112 |
+
<button id="focus-earth" class="flex-1 py-1.5 text-sm rounded-md transition bg-blue-500/30 text-blue-200 border border-blue-500/50">Earth</button>
|
| 113 |
+
</div>
|
| 114 |
+
</div>
|
| 115 |
+
|
| 116 |
+
<!-- Visibility Toggles -->
|
| 117 |
+
<div class="pt-2 border-t border-gray-700/50 space-y-2">
|
| 118 |
+
<div class="mb-3">
|
| 119 |
+
<label class="block text-xs text-gray-400 mb-1">Show Nodes</label>
|
| 120 |
+
<select id="node-display-mode" class="w-full bg-gray-800 text-white text-sm rounded border border-gray-600 p-1.5 focus:ring-blue-500 focus:outline-none">
|
| 121 |
+
<option value="always">Always Show</option>
|
| 122 |
+
<option value="intersect">Only on Intersection</option>
|
| 123 |
+
</select>
|
| 124 |
+
</div>
|
| 125 |
+
|
| 126 |
+
<label class="flex items-center space-x-2 text-sm cursor-pointer">
|
| 127 |
+
<input type="checkbox" id="toggle-ecliptic" checked class="rounded bg-gray-800 border-gray-600 text-blue-500 focus:ring-blue-500">
|
| 128 |
+
<span class="text-gray-300">Show Earth's Plane (Ecliptic)</span>
|
| 129 |
+
</label>
|
| 130 |
+
<label class="flex items-center space-x-2 text-sm cursor-pointer">
|
| 131 |
+
<input type="checkbox" id="toggle-lunar" checked class="rounded bg-gray-800 border-gray-600 text-blue-500 focus:ring-blue-500">
|
| 132 |
+
<span class="text-gray-300">Show Moon's Tilted Plane</span>
|
| 133 |
+
</label>
|
| 134 |
+
<label class="flex items-center space-x-2 text-sm cursor-pointer">
|
| 135 |
+
<input type="checkbox" id="toggle-moon-orbit-line" checked class="rounded bg-gray-800 border-gray-600 text-blue-500 focus:ring-blue-500">
|
| 136 |
+
<span class="text-gray-300">Show Moon's Orbit Line</span>
|
| 137 |
+
</label>
|
| 138 |
+
|
| 139 |
+
<label class="flex items-center space-x-2 text-sm cursor-pointer border-t border-gray-700/50 pt-3 mt-2">
|
| 140 |
+
<input type="checkbox" id="toggle-earth-rotation" checked class="rounded bg-gray-800 border-gray-600 text-green-500 focus:ring-green-500">
|
| 141 |
+
<span class="text-gray-300">Earth's Axis Rotation</span>
|
| 142 |
+
</label>
|
| 143 |
+
</div>
|
| 144 |
+
|
| 145 |
+
<!-- Wobble Controls -->
|
| 146 |
+
<div class="pt-2 border-t border-gray-700/50 space-y-3">
|
| 147 |
+
<h4 class="text-xs font-semibold text-purple-400 uppercase tracking-wider">Lunar Wobble Controls</h4>
|
| 148 |
+
|
| 149 |
+
<div>
|
| 150 |
+
<label class="flex justify-between text-xs text-gray-400 mb-1">
|
| 151 |
+
<span>Wobble (Precession) Speed</span>
|
| 152 |
+
<span id="wobble-speed-val">1x</span>
|
| 153 |
+
</label>
|
| 154 |
+
<input type="range" id="wobble-speed" min="0" max="100" step="1" value="1" class="w-full">
|
| 155 |
+
</div>
|
| 156 |
+
|
| 157 |
+
<div>
|
| 158 |
+
<label class="flex justify-between text-xs text-gray-400 mb-1">
|
| 159 |
+
<span>Tilt Angle</span>
|
| 160 |
+
<span id="tilt-val">15°</span>
|
| 161 |
+
</label>
|
| 162 |
+
<input type="range" id="tilt-angle" min="0" max="45" step="1" value="15" class="w-full">
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
+
<label class="flex items-center space-x-2 text-sm cursor-pointer">
|
| 166 |
+
<input type="checkbox" id="toggle-axis" checked class="rounded bg-gray-800 border-gray-600 text-purple-500 focus:ring-purple-500">
|
| 167 |
+
<span class="text-gray-300">Show Orbital Axis</span>
|
| 168 |
+
</label>
|
| 169 |
+
|
| 170 |
+
<div class="mt-3 bg-purple-900/20 border border-purple-500/30 p-3 rounded-lg">
|
| 171 |
+
<label class="flex justify-between text-xs text-gray-400 mb-2">
|
| 172 |
+
<span class="flex items-center space-x-2">
|
| 173 |
+
<input type="checkbox" id="manual-precession-toggle" class="rounded bg-gray-800 border-gray-600 text-purple-500 focus:ring-purple-500">
|
| 174 |
+
<span class="text-purple-300 font-semibold">Manual 18.6 Yr Scrub</span>
|
| 175 |
+
</span>
|
| 176 |
+
<span id="precession-year-val" class="text-purple-300 font-mono">0.00 Yrs</span>
|
| 177 |
+
</label>
|
| 178 |
+
<input type="range" id="precession-slider" min="0" max="18.5996" step="0.01" value="0" class="w-full disabled:opacity-40 disabled:cursor-not-allowed" disabled>
|
| 179 |
+
</div>
|
| 180 |
+
|
| 181 |
+
<label class="flex items-center space-x-2 text-sm cursor-pointer border-t border-gray-700/50 pt-3">
|
| 182 |
+
<input type="checkbox" id="isolate-system" class="rounded bg-gray-800 border-gray-600 text-red-500 focus:ring-red-500">
|
| 183 |
+
<span class="text-gray-200 font-semibold">Isolate Earth & Moon</span>
|
| 184 |
+
</label>
|
| 185 |
+
</div>
|
| 186 |
+
|
| 187 |
+
</div>
|
| 188 |
+
</div>
|
| 189 |
+
</div>
|
| 190 |
+
|
| 191 |
+
<script>
|
| 192 |
+
// --- THREE.JS SETUP ---
|
| 193 |
+
const canvasContainer = document.getElementById('canvas-container');
|
| 194 |
+
const scene = new THREE.Scene();
|
| 195 |
+
|
| 196 |
+
// Camera setup
|
| 197 |
+
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
|
| 198 |
+
camera.position.set(0, 80, 150);
|
| 199 |
+
|
| 200 |
+
// Renderer setup
|
| 201 |
+
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
| 202 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 203 |
+
renderer.setPixelRatio(window.devicePixelRatio);
|
| 204 |
+
canvasContainer.appendChild(renderer.domElement);
|
| 205 |
+
|
| 206 |
+
// OrbitControls
|
| 207 |
+
const controls = new THREE.OrbitControls(camera, renderer.domElement);
|
| 208 |
+
controls.enableDamping = true;
|
| 209 |
+
controls.dampingFactor = 0.05;
|
| 210 |
+
controls.maxDistance = 600;
|
| 211 |
+
controls.minDistance = 20;
|
| 212 |
+
|
| 213 |
+
// --- ENVIRONMENT ---
|
| 214 |
+
// Starfield
|
| 215 |
+
const starsGeometry = new THREE.BufferGeometry();
|
| 216 |
+
const starsCount = 3000;
|
| 217 |
+
const posArray = new Float32Array(starsCount * 3);
|
| 218 |
+
for(let i = 0; i < starsCount * 3; i++) {
|
| 219 |
+
posArray[i] = (Math.random() - 0.5) * 1500;
|
| 220 |
+
}
|
| 221 |
+
starsGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
|
| 222 |
+
const starsMaterial = new THREE.PointsMaterial({ size: 1.5, color: 0xffffff, transparent: true, opacity: 0.8 });
|
| 223 |
+
const starMesh = new THREE.Points(starsGeometry, starsMaterial);
|
| 224 |
+
scene.add(starMesh);
|
| 225 |
+
|
| 226 |
+
// Ambient Light
|
| 227 |
+
scene.add(new THREE.AmbientLight(0x333344));
|
| 228 |
+
|
| 229 |
+
// --- CELESTIAL BODIES ---
|
| 230 |
+
|
| 231 |
+
// 1. SUN SYSTEM
|
| 232 |
+
const sunSystem = new THREE.Group();
|
| 233 |
+
scene.add(sunSystem);
|
| 234 |
+
|
| 235 |
+
// Sun Mesh
|
| 236 |
+
const sunGeo = new THREE.SphereGeometry(15, 32, 32);
|
| 237 |
+
const sunMat = new THREE.MeshBasicMaterial({ color: 0xffaa00 });
|
| 238 |
+
const sun = new THREE.Mesh(sunGeo, sunMat);
|
| 239 |
+
sunSystem.add(sun);
|
| 240 |
+
|
| 241 |
+
// Sun Light
|
| 242 |
+
const sunLight = new THREE.PointLight(0xffffff, 2.5, 1000);
|
| 243 |
+
sunSystem.add(sunLight);
|
| 244 |
+
|
| 245 |
+
// Sun Glow (Sprite)
|
| 246 |
+
const glowCanvas = document.createElement('canvas');
|
| 247 |
+
glowCanvas.width = 128; glowCanvas.height = 128;
|
| 248 |
+
const ctx = glowCanvas.getContext('2d');
|
| 249 |
+
const gradient = ctx.createRadialGradient(64,64,0, 64,64,64);
|
| 250 |
+
gradient.addColorStop(0, 'rgba(255, 170, 0, 0.6)');
|
| 251 |
+
gradient.addColorStop(1, 'rgba(255, 170, 0, 0)');
|
| 252 |
+
ctx.fillStyle = gradient;
|
| 253 |
+
ctx.fillRect(0,0, 128,128);
|
| 254 |
+
const glowTexture = new THREE.CanvasTexture(glowCanvas);
|
| 255 |
+
const glowMaterial = new THREE.SpriteMaterial({ map: glowTexture, blending: THREE.AdditiveBlending, transparent: true });
|
| 256 |
+
const sunGlow = new THREE.Sprite(glowMaterial);
|
| 257 |
+
sunGlow.scale.set(60, 60, 1);
|
| 258 |
+
sun.add(sunGlow);
|
| 259 |
+
|
| 260 |
+
// 2. EARTH SYSTEM
|
| 261 |
+
const earthOrbitRadius = 120;
|
| 262 |
+
const earthSystem = new THREE.Group();
|
| 263 |
+
scene.add(earthSystem);
|
| 264 |
+
|
| 265 |
+
// Earth Orbit Line
|
| 266 |
+
const earthOrbitGeo = new THREE.RingGeometry(earthOrbitRadius - 0.2, earthOrbitRadius + 0.2, 128);
|
| 267 |
+
const earthOrbitMat = new THREE.MeshBasicMaterial({ color: 0x445566, side: THREE.DoubleSide, transparent: true, opacity: 0.5 });
|
| 268 |
+
const earthOrbitLine = new THREE.Mesh(earthOrbitGeo, earthOrbitMat);
|
| 269 |
+
earthOrbitLine.rotation.x = Math.PI / 2;
|
| 270 |
+
scene.add(earthOrbitLine);
|
| 271 |
+
|
| 272 |
+
// Generate Earth Texture to make rotation visible
|
| 273 |
+
const earthCanvas = document.createElement('canvas');
|
| 274 |
+
earthCanvas.width = 512; earthCanvas.height = 256;
|
| 275 |
+
const ectx = earthCanvas.getContext('2d');
|
| 276 |
+
ectx.fillStyle = '#1a5b82'; // Ocean
|
| 277 |
+
ectx.fillRect(0, 0, 512, 256);
|
| 278 |
+
ectx.strokeStyle = '#2a7ba2'; // Grid lines
|
| 279 |
+
ectx.lineWidth = 2;
|
| 280 |
+
for(let i=0; i<=512; i+=32) { ectx.beginPath(); ectx.moveTo(i, 0); ectx.lineTo(i, 256); ectx.stroke(); }
|
| 281 |
+
for(let i=0; i<=256; i+=32) { ectx.beginPath(); ectx.moveTo(0, i); ectx.lineTo(512, i); ectx.stroke(); }
|
| 282 |
+
ectx.fillStyle = '#2d8a4e'; // Faux continents
|
| 283 |
+
[ [80, 80, 40], [180, 120, 60], [380, 90, 50], [420, 190, 40], [120, 210, 30] ].forEach(c => {
|
| 284 |
+
ectx.beginPath(); ectx.arc(c[0], c[1], c[2], 0, Math.PI*2); ectx.fill();
|
| 285 |
+
});
|
| 286 |
+
const earthTex = new THREE.CanvasTexture(earthCanvas);
|
| 287 |
+
|
| 288 |
+
// Earth Mesh
|
| 289 |
+
const earthGeo = new THREE.SphereGeometry(4, 32, 32);
|
| 290 |
+
const earthMat = new THREE.MeshStandardMaterial({ map: earthTex, roughness: 0.6 });
|
| 291 |
+
const earth = new THREE.Mesh(earthGeo, earthMat);
|
| 292 |
+
|
| 293 |
+
// Group for Earth Globe + Axis (Tilt Earth 23.5 degrees to reality)
|
| 294 |
+
const earthGlobeGroup = new THREE.Group();
|
| 295 |
+
earthGlobeGroup.rotation.z = 23.5 * (Math.PI / 180); // 23.5 degree axial tilt
|
| 296 |
+
earthGlobeGroup.add(earth);
|
| 297 |
+
|
| 298 |
+
// Earth Axis Line
|
| 299 |
+
const earthAxisGeo = new THREE.BufferGeometry().setFromPoints([
|
| 300 |
+
new THREE.Vector3(0, -6, 0),
|
| 301 |
+
new THREE.Vector3(0, 6, 0)
|
| 302 |
+
]);
|
| 303 |
+
const earthAxisMat = new THREE.LineBasicMaterial({ color: 0x88ccff, transparent: true, opacity: 0.8 });
|
| 304 |
+
const earthAxisLine = new THREE.Line(earthAxisGeo, earthAxisMat);
|
| 305 |
+
earthGlobeGroup.add(earthAxisLine);
|
| 306 |
+
|
| 307 |
+
earthSystem.add(earthGlobeGroup);
|
| 308 |
+
|
| 309 |
+
// Ecliptic Plane (Local to Earth)
|
| 310 |
+
const eclipticGeo = new THREE.PlaneGeometry(60, 60);
|
| 311 |
+
const eclipticMat = new THREE.MeshBasicMaterial({ color: 0x22ff44, transparent: true, opacity: 0.1, side: THREE.DoubleSide });
|
| 312 |
+
const eclipticPlane = new THREE.Mesh(eclipticGeo, eclipticMat);
|
| 313 |
+
eclipticPlane.rotation.x = Math.PI / 2;
|
| 314 |
+
earthSystem.add(eclipticPlane);
|
| 315 |
+
|
| 316 |
+
// Ecliptic Grid Helper to make it clearer
|
| 317 |
+
const eclipticGrid = new THREE.GridHelper(60, 10, 0x22ff44, 0x22ff44);
|
| 318 |
+
eclipticGrid.material.opacity = 0.2;
|
| 319 |
+
eclipticGrid.material.transparent = true;
|
| 320 |
+
earthSystem.add(eclipticGrid);
|
| 321 |
+
|
| 322 |
+
// 3. MOON SYSTEM & NODAL PRECESSION
|
| 323 |
+
const nodalPrecessionGroup = new THREE.Group();
|
| 324 |
+
earthSystem.add(nodalPrecessionGroup);
|
| 325 |
+
|
| 326 |
+
const moonOrbitRadius = 22;
|
| 327 |
+
const moonSystem = new THREE.Group();
|
| 328 |
+
|
| 329 |
+
// TILT THE LUNAR ORBIT (Exaggerated to 15 degrees for visual clarity, real is ~5 degrees)
|
| 330 |
+
let lunarTilt = 15 * (Math.PI / 180);
|
| 331 |
+
moonSystem.rotation.x = lunarTilt;
|
| 332 |
+
nodalPrecessionGroup.add(moonSystem);
|
| 333 |
+
|
| 334 |
+
// Moon Orbit Axis (To visualize the wobble)
|
| 335 |
+
const axisGeo = new THREE.BufferGeometry().setFromPoints([
|
| 336 |
+
new THREE.Vector3(0, -30, 0),
|
| 337 |
+
new THREE.Vector3(0, 30, 0)
|
| 338 |
+
]);
|
| 339 |
+
const axisMat = new THREE.LineBasicMaterial({ color: 0xdd44ff, transparent: true, opacity: 0.8, linewidth: 2 });
|
| 340 |
+
const moonOrbitAxis = new THREE.Line(axisGeo, axisMat);
|
| 341 |
+
moonSystem.add(moonOrbitAxis);
|
| 342 |
+
|
| 343 |
+
// Moon Plane
|
| 344 |
+
const lunarPlaneGeo = new THREE.PlaneGeometry(50, 50);
|
| 345 |
+
const lunarPlaneMat = new THREE.MeshBasicMaterial({ color: 0x44aaff, transparent: true, opacity: 0.15, side: THREE.DoubleSide });
|
| 346 |
+
const lunarPlane = new THREE.Mesh(lunarPlaneGeo, lunarPlaneMat);
|
| 347 |
+
lunarPlane.rotation.x = Math.PI / 2;
|
| 348 |
+
moonSystem.add(lunarPlane);
|
| 349 |
+
|
| 350 |
+
// Moon Orbit Line
|
| 351 |
+
const moonOrbitLineGeo = new THREE.RingGeometry(moonOrbitRadius - 0.2, moonOrbitRadius + 0.2, 64);
|
| 352 |
+
const moonOrbitLineMat = new THREE.MeshBasicMaterial({ color: 0xaaaaff, side: THREE.DoubleSide, transparent: true, opacity: 0.8 });
|
| 353 |
+
const moonOrbitLine = new THREE.Mesh(moonOrbitLineGeo, moonOrbitLineMat);
|
| 354 |
+
moonOrbitLine.rotation.x = Math.PI / 2;
|
| 355 |
+
moonSystem.add(moonOrbitLine);
|
| 356 |
+
|
| 357 |
+
// Moon Mesh
|
| 358 |
+
const moonGeo = new THREE.SphereGeometry(1.2, 16, 16);
|
| 359 |
+
const moonMat = new THREE.MeshStandardMaterial({ color: 0xdddddd, roughness: 0.8 });
|
| 360 |
+
const moon = new THREE.Mesh(moonGeo, moonMat);
|
| 361 |
+
moonSystem.add(moon);
|
| 362 |
+
|
| 363 |
+
// --- RAHU & KETU NODES ---
|
| 364 |
+
// Create Sprite Text Labels
|
| 365 |
+
function createTextLabel(text, color) {
|
| 366 |
+
const canvas = document.createElement('canvas');
|
| 367 |
+
canvas.width = 512; canvas.height = 128;
|
| 368 |
+
const ctx = canvas.getContext('2d');
|
| 369 |
+
ctx.fillStyle = color;
|
| 370 |
+
ctx.font = 'bold 36px sans-serif';
|
| 371 |
+
ctx.textAlign = 'center';
|
| 372 |
+
ctx.textBaseline = 'middle';
|
| 373 |
+
|
| 374 |
+
// Background capsule
|
| 375 |
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
|
| 376 |
+
ctx.beginPath();
|
| 377 |
+
ctx.roundRect(64, 20, 384, 88, 44);
|
| 378 |
+
ctx.fill();
|
| 379 |
+
ctx.strokeStyle = color;
|
| 380 |
+
ctx.lineWidth = 3;
|
| 381 |
+
ctx.stroke();
|
| 382 |
+
|
| 383 |
+
// Text
|
| 384 |
+
ctx.fillStyle = color;
|
| 385 |
+
ctx.fillText(text, 256, 64);
|
| 386 |
+
|
| 387 |
+
const texture = new THREE.CanvasTexture(canvas);
|
| 388 |
+
const material = new THREE.SpriteMaterial({ map: texture, depthTest: false });
|
| 389 |
+
const sprite = new THREE.Sprite(material);
|
| 390 |
+
sprite.scale.set(16, 4, 1);
|
| 391 |
+
return sprite;
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
function createNodeMarker(color) {
|
| 395 |
+
const geo = new THREE.SphereGeometry(1.5, 16, 16);
|
| 396 |
+
const mat = new THREE.MeshBasicMaterial({ color: color });
|
| 397 |
+
return new THREE.Mesh(geo, mat);
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
// The intersection of the tilted Moon system with the Ecliptic (Earth's XZ plane) happens along the local X-axis.
|
| 401 |
+
// Because we rotated the moonSystem around X, the X-axis remains unchanged and perfectly lies on the Ecliptic.
|
| 402 |
+
// Rahu (Ascending Node) - Going South to North. Due to rotation rules, this occurs at -X.
|
| 403 |
+
const rahuLabel = createTextLabel("Rahu ☊", "#ff8800");
|
| 404 |
+
rahuLabel.position.set(-moonOrbitRadius, 5, 0);
|
| 405 |
+
const rahuMarker = createNodeMarker(0xff8800);
|
| 406 |
+
rahuMarker.position.set(-moonOrbitRadius, 0, 0);
|
| 407 |
+
moonSystem.add(rahuLabel);
|
| 408 |
+
moonSystem.add(rahuMarker);
|
| 409 |
+
|
| 410 |
+
// Ketu (Descending Node) - Going North to South. This occurs at +X.
|
| 411 |
+
const ketuLabel = createTextLabel("Ketu ☋", "#a0a0a0");
|
| 412 |
+
ketuLabel.position.set(moonOrbitRadius, 5, 0);
|
| 413 |
+
const ketuMarker = createNodeMarker(0xa0a0a0);
|
| 414 |
+
ketuMarker.position.set(moonOrbitRadius, 0, 0);
|
| 415 |
+
moonSystem.add(ketuLabel);
|
| 416 |
+
moonSystem.add(ketuMarker);
|
| 417 |
+
|
| 418 |
+
// Line of Nodes
|
| 419 |
+
const lineOfNodesGeo = new THREE.BufferGeometry().setFromPoints([
|
| 420 |
+
new THREE.Vector3(-moonOrbitRadius - 5, 0, 0),
|
| 421 |
+
new THREE.Vector3(moonOrbitRadius + 5, 0, 0)
|
| 422 |
+
]);
|
| 423 |
+
const lineOfNodesMat = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.7 });
|
| 424 |
+
const lineOfNodes = new THREE.Line(lineOfNodesGeo, lineOfNodesMat);
|
| 425 |
+
moonSystem.add(lineOfNodes);
|
| 426 |
+
|
| 427 |
+
// --- ANIMATION LOGIC ---
|
| 428 |
+
let time = 0;
|
| 429 |
+
let wobbleTime = 0; // Separate time tracker for wobble
|
| 430 |
+
let isPaused = false;
|
| 431 |
+
let isEarthRotating = true; // Earth rotation toggle
|
| 432 |
+
let speedMultiplier = 1;
|
| 433 |
+
let wobbleSpeedMultiplier = 1; // Default independent wobble speed
|
| 434 |
+
let currentTilt = 15; // Default tilt in degrees
|
| 435 |
+
let cameraFocus = 'earth'; // 'earth' or 'sun'
|
| 436 |
+
let nodeDisplayMode = 'always'; // 'always' or 'intersect'
|
| 437 |
+
const earthWorldPos = new THREE.Vector3();
|
| 438 |
+
|
| 439 |
+
// Set initial camera to Earth
|
| 440 |
+
camera.position.set(170, 40, 60);
|
| 441 |
+
|
| 442 |
+
function animate() {
|
| 443 |
+
requestAnimationFrame(animate);
|
| 444 |
+
|
| 445 |
+
const manualToggle = document.getElementById('manual-precession-toggle');
|
| 446 |
+
const precessionSlider = document.getElementById('precession-slider');
|
| 447 |
+
const precessionVal = document.getElementById('precession-year-val');
|
| 448 |
+
const exactPrecessionYears = 18.5996; // Cutting edge precise astronomical period
|
| 449 |
+
|
| 450 |
+
if (!isPaused) {
|
| 451 |
+
time += 0.005 * speedMultiplier;
|
| 452 |
+
|
| 453 |
+
if (!manualToggle.checked) {
|
| 454 |
+
wobbleTime += 0.005 * speedMultiplier * wobbleSpeedMultiplier;
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
// Earth revolution around Sun (1 Earth Year)
|
| 458 |
+
earthSystem.position.x = Math.cos(time) * earthOrbitRadius;
|
| 459 |
+
earthSystem.position.z = Math.sin(time) * earthOrbitRadius;
|
| 460 |
+
|
| 461 |
+
// Earth rotation
|
| 462 |
+
if (isEarthRotating) {
|
| 463 |
+
earth.rotation.y += 0.05 * speedMultiplier;
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
// Moon revolution around Earth
|
| 467 |
+
// Inside moonSystem, rotation around Y moves it along the tilted orbit!
|
| 468 |
+
const moonSpeed = time * 13.36; // Moon revolves ~13.36 times a year
|
| 469 |
+
moon.position.x = Math.cos(moonSpeed) * moonOrbitRadius;
|
| 470 |
+
moon.position.z = Math.sin(moonSpeed) * moonOrbitRadius;
|
| 471 |
+
moon.rotation.y += 0.02 * speedMultiplier;
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
// --- PRECESSION LOGIC (18.6 Year Cycle) ---
|
| 475 |
+
// Moved outside the !isPaused block so you can scrub while paused!
|
| 476 |
+
if (manualToggle.checked) {
|
| 477 |
+
// Manual Scrubbing
|
| 478 |
+
let manualYears = parseFloat(precessionSlider.value);
|
| 479 |
+
nodalPrecessionGroup.rotation.y = -(manualYears / exactPrecessionYears) * (Math.PI * 2);
|
| 480 |
+
precessionVal.innerText = manualYears.toFixed(2) + " Yrs";
|
| 481 |
+
|
| 482 |
+
// Sync internal time so it doesn't jump when resuming auto play
|
| 483 |
+
wobbleTime = manualYears * (Math.PI * 2);
|
| 484 |
+
} else {
|
| 485 |
+
// Auto Precession
|
| 486 |
+
nodalPrecessionGroup.rotation.y = -(wobbleTime / exactPrecessionYears);
|
| 487 |
+
|
| 488 |
+
// Sync slider visually
|
| 489 |
+
let baseWobbleYears = (wobbleTime / (Math.PI * 2));
|
| 490 |
+
let currentWobbleYears = baseWobbleYears % exactPrecessionYears;
|
| 491 |
+
if (currentWobbleYears < 0) currentWobbleYears += exactPrecessionYears;
|
| 492 |
+
|
| 493 |
+
precessionSlider.value = currentWobbleYears;
|
| 494 |
+
precessionVal.innerText = currentWobbleYears.toFixed(2) + " Yrs";
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
// Update Lunar Tilt Dynamically
|
| 498 |
+
moonSystem.rotation.x = currentTilt * (Math.PI / 180);
|
| 499 |
+
|
| 500 |
+
// Handle Node Visibility Mode (Moved outside so scrubbing reveals nodes even when paused)
|
| 501 |
+
const currentMoonSpeed = time * 13.36;
|
| 502 |
+
if (nodeDisplayMode === 'intersect') {
|
| 503 |
+
// Calculate moon's current angle to determine proximity to nodes (0 and PI)
|
| 504 |
+
let moonAngle = currentMoonSpeed % (Math.PI * 2);
|
| 505 |
+
if (moonAngle < 0) moonAngle += Math.PI * 2;
|
| 506 |
+
|
| 507 |
+
const threshold = 0.35; // Visibility window in radians (~20 degrees)
|
| 508 |
+
const isNearKetu = (moonAngle < threshold) || (moonAngle > Math.PI * 2 - threshold); // Ketu is at 0 / 2PI (+X)
|
| 509 |
+
const isNearRahu = Math.abs(moonAngle - Math.PI) < threshold; // Rahu is at PI (-X)
|
| 510 |
+
|
| 511 |
+
rahuLabel.visible = isNearRahu;
|
| 512 |
+
rahuMarker.visible = isNearRahu;
|
| 513 |
+
ketuLabel.visible = isNearKetu;
|
| 514 |
+
ketuMarker.visible = isNearKetu;
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
// Make text sprites always face camera
|
| 518 |
+
rahuLabel.material.rotation = 0;
|
| 519 |
+
ketuLabel.material.rotation = 0;
|
| 520 |
+
|
| 521 |
+
// Camera Tracking
|
| 522 |
+
earthSystem.getWorldPosition(earthWorldPos);
|
| 523 |
+
|
| 524 |
+
if (cameraFocus === 'earth') {
|
| 525 |
+
controls.target.copy(earthWorldPos);
|
| 526 |
+
} else {
|
| 527 |
+
controls.target.set(0, 0, 0);
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
controls.update();
|
| 531 |
+
renderer.render(scene, camera);
|
| 532 |
+
}
|
| 533 |
+
|
| 534 |
+
// --- UI INTERACTIONS ---
|
| 535 |
+
|
| 536 |
+
// Pause Button
|
| 537 |
+
const btnPause = document.getElementById('btn-pause');
|
| 538 |
+
btnPause.addEventListener('click', () => {
|
| 539 |
+
isPaused = !isPaused;
|
| 540 |
+
btnPause.innerText = isPaused ? "Play" : "Pause";
|
| 541 |
+
btnPause.className = isPaused
|
| 542 |
+
? "bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded text-sm transition"
|
| 543 |
+
: "bg-white/10 hover:bg-white/20 text-white px-3 py-1 rounded text-sm transition";
|
| 544 |
+
});
|
| 545 |
+
|
| 546 |
+
// Speed Slider
|
| 547 |
+
const speedSlider = document.getElementById('time-speed');
|
| 548 |
+
const speedVal = document.getElementById('speed-val');
|
| 549 |
+
speedSlider.addEventListener('input', (e) => {
|
| 550 |
+
speedMultiplier = parseFloat(e.target.value);
|
| 551 |
+
speedVal.innerText = speedMultiplier.toFixed(1) + "x";
|
| 552 |
+
});
|
| 553 |
+
|
| 554 |
+
// Camera Focus Toggles
|
| 555 |
+
const btnFocusSun = document.getElementById('focus-sun');
|
| 556 |
+
const btnFocusEarth = document.getElementById('focus-earth');
|
| 557 |
+
|
| 558 |
+
btnFocusSun.addEventListener('click', () => {
|
| 559 |
+
cameraFocus = 'sun';
|
| 560 |
+
btnFocusSun.className = "flex-1 py-1.5 text-sm rounded-md transition bg-blue-500/30 text-blue-200 border border-blue-500/50";
|
| 561 |
+
btnFocusEarth.className = "flex-1 py-1.5 text-sm rounded-md transition text-gray-400 hover:text-white border border-transparent";
|
| 562 |
+
|
| 563 |
+
// Move camera to see whole system
|
| 564 |
+
const targetPos = new THREE.Vector3(0, 200, 300);
|
| 565 |
+
camera.position.lerp(targetPos, 1);
|
| 566 |
+
});
|
| 567 |
+
|
| 568 |
+
btnFocusEarth.addEventListener('click', () => {
|
| 569 |
+
cameraFocus = 'earth';
|
| 570 |
+
btnFocusEarth.className = "flex-1 py-1.5 text-sm rounded-md transition bg-blue-500/30 text-blue-200 border border-blue-500/50";
|
| 571 |
+
btnFocusSun.className = "flex-1 py-1.5 text-sm rounded-md transition text-gray-400 hover:text-white border border-transparent";
|
| 572 |
+
|
| 573 |
+
// Move camera closer to earth
|
| 574 |
+
earthSystem.getWorldPosition(earthWorldPos);
|
| 575 |
+
camera.position.set(earthWorldPos.x, earthWorldPos.y + 40, earthWorldPos.z + 60);
|
| 576 |
+
});
|
| 577 |
+
|
| 578 |
+
// Visibility Toggles
|
| 579 |
+
document.getElementById('node-display-mode').addEventListener('change', (e) => {
|
| 580 |
+
nodeDisplayMode = e.target.value;
|
| 581 |
+
if (nodeDisplayMode === 'always') {
|
| 582 |
+
rahuLabel.visible = true;
|
| 583 |
+
rahuMarker.visible = true;
|
| 584 |
+
ketuLabel.visible = true;
|
| 585 |
+
ketuMarker.visible = true;
|
| 586 |
+
}
|
| 587 |
+
});
|
| 588 |
+
|
| 589 |
+
document.getElementById('toggle-ecliptic').addEventListener('change', (e) => {
|
| 590 |
+
eclipticPlane.visible = e.target.checked;
|
| 591 |
+
eclipticGrid.visible = e.target.checked;
|
| 592 |
+
});
|
| 593 |
+
|
| 594 |
+
document.getElementById('toggle-lunar').addEventListener('change', (e) => {
|
| 595 |
+
lunarPlane.visible = e.target.checked;
|
| 596 |
+
});
|
| 597 |
+
|
| 598 |
+
document.getElementById('toggle-moon-orbit-line').addEventListener('change', (e) => {
|
| 599 |
+
moonOrbitLine.visible = e.target.checked;
|
| 600 |
+
});
|
| 601 |
+
|
| 602 |
+
document.getElementById('toggle-earth-rotation').addEventListener('change', (e) => {
|
| 603 |
+
isEarthRotating = e.target.checked;
|
| 604 |
+
});
|
| 605 |
+
|
| 606 |
+
// Wobble Control Listeners
|
| 607 |
+
const wobbleSpeedSlider = document.getElementById('wobble-speed');
|
| 608 |
+
const wobbleSpeedVal = document.getElementById('wobble-speed-val');
|
| 609 |
+
wobbleSpeedSlider.addEventListener('input', (e) => {
|
| 610 |
+
wobbleSpeedMultiplier = parseFloat(e.target.value);
|
| 611 |
+
wobbleSpeedVal.innerText = wobbleSpeedMultiplier + "x";
|
| 612 |
+
});
|
| 613 |
+
|
| 614 |
+
const tiltSlider = document.getElementById('tilt-angle');
|
| 615 |
+
const tiltVal = document.getElementById('tilt-val');
|
| 616 |
+
tiltSlider.addEventListener('input', (e) => {
|
| 617 |
+
currentTilt = parseFloat(e.target.value);
|
| 618 |
+
tiltVal.innerText = currentTilt + "°";
|
| 619 |
+
});
|
| 620 |
+
|
| 621 |
+
document.getElementById('toggle-axis').addEventListener('change', (e) => {
|
| 622 |
+
moonOrbitAxis.visible = e.target.checked;
|
| 623 |
+
});
|
| 624 |
+
|
| 625 |
+
// Manual Precession Scrub Toggle
|
| 626 |
+
document.getElementById('manual-precession-toggle').addEventListener('change', (e) => {
|
| 627 |
+
document.getElementById('precession-slider').disabled = !e.target.checked;
|
| 628 |
+
});
|
| 629 |
+
|
| 630 |
+
// Isolate System Toggle
|
| 631 |
+
document.getElementById('isolate-system').addEventListener('change', (e) => {
|
| 632 |
+
const isIsolated = e.target.checked;
|
| 633 |
+
sun.visible = !isIsolated;
|
| 634 |
+
sunGlow.visible = !isIsolated;
|
| 635 |
+
earthOrbitLine.visible = !isIsolated;
|
| 636 |
+
|
| 637 |
+
if (isIsolated) {
|
| 638 |
+
// Force camera focus to Earth
|
| 639 |
+
cameraFocus = 'earth';
|
| 640 |
+
document.getElementById('focus-earth').className = "flex-1 py-1.5 text-sm rounded-md transition bg-blue-500/30 text-blue-200 border border-blue-500/50";
|
| 641 |
+
document.getElementById('focus-sun').className = "flex-1 py-1.5 text-sm rounded-md transition text-gray-400 hover:text-white border border-transparent";
|
| 642 |
+
}
|
| 643 |
+
});
|
| 644 |
+
|
| 645 |
+
// Handle Window Resize
|
| 646 |
+
window.addEventListener('resize', () => {
|
| 647 |
+
camera.aspect = window.innerWidth / window.innerHeight;
|
| 648 |
+
camera.updateProjectionMatrix();
|
| 649 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 650 |
+
});
|
| 651 |
+
|
| 652 |
+
// Start animation loop
|
| 653 |
+
animate();
|
| 654 |
+
|
| 655 |
+
</script>
|
| 656 |
+
</body>
|
| 657 |
+
</html>
|