Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- index.html +832 -19
index.html
CHANGED
|
@@ -1,19 +1,832 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*Thinking...*
|
| 2 |
+
|
| 3 |
+
> **Reimagining the Explainer**
|
| 4 |
+
>
|
| 5 |
+
> I'm currently focused on adapting the 2D explainer into a 3D voxel-based version. The challenge lies in rewriting the HTML, CSS, and JavaScript. I'm exploring lightweight, CDN-friendly approaches for 3D rendering to avoid complex setup, focusing on a voxel grid implementation.
|
| 6 |
+
>
|
| 7 |
+
>
|
| 8 |
+
> **Analyzing New Directions**
|
| 9 |
+
>
|
| 10 |
+
> I've re-evaluated my options, leaning towards Three.js for its robust 3D capabilities. I'm focusing on simplicity, considering BoxGeometry and potentially custom VoxelGeometry to create the reactor's voxelized elements. I'm leaning toward using JavaScript for interactions. However, I still need to evaluate the feasibility of doing this via pure CSS to match the original constraints.
|
| 11 |
+
>
|
| 12 |
+
>
|
| 13 |
+
> **Choosing Three.js Implementation**
|
| 14 |
+
>
|
| 15 |
+
> I've decided to proceed with Three.js, leaning into its power and the prompt's request for a 3D voxel experience. Currently, I'm sketching out the scene, envisioning the reactor as a collection of voxel-based components: the core, pipes, steam generator, turbine, and cooling tower. The goal is to make it interactive, linking the JavaScript logic to the 3D scene's visual elements.
|
| 16 |
+
>
|
| 17 |
+
>
|
| 18 |
+
> **Designing the Reactor**
|
| 19 |
+
>
|
| 20 |
+
> I'm now deep into designing the reactor's visual elements within Three.js. I'm focusing on the "blocky" voxel aesthetic, using BoxGeometry. I have begun creating the core, envisioning grey voxel blocks with a blue inner glow. I'm also planning the control rods, the pipes, the turbine, and adding particles to simulate steam. My focus now is on translating the original 2D elements into these 3D, interactive voxel components.
|
| 21 |
+
>
|
| 22 |
+
>
|
| 23 |
+
> **Developing the Scene Structure**
|
| 24 |
+
>
|
| 25 |
+
> I'm presently building the scaffolding within Three.js. Using the "Blocky/Voxel Art" style with `BoxGeometry`, I'm creating a `createVoxel()` helper function. I have the basic HTML/CSS setup. I am sketching the scene, beginning with the reactor core as a stack of grey cubes with an inner blue glow, and the control rods. I am also working on the structure for the pipes.
|
| 26 |
+
>
|
| 27 |
+
>
|
| 28 |
+
> **Developing Physics Integration**
|
| 29 |
+
>
|
| 30 |
+
> My current focus is integrating the physics-based calculations from the original 2D explainer into the 3D scene. I'm working to tie the power, temperature, and control rod values to visual elements, such as the turbine's rotation speed and the rods' position. I'm also adding particle effects to visualize steam flow between components.
|
| 31 |
+
>
|
| 32 |
+
>
|
| 33 |
+
```html
|
| 34 |
+
<!DOCTYPE html>
|
| 35 |
+
<html lang="en">
|
| 36 |
+
<head>
|
| 37 |
+
<meta charset="UTF-8">
|
| 38 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 39 |
+
<title>3D Voxel Nuclear Reactor</title>
|
| 40 |
+
|
| 41 |
+
<!-- Fonts -->
|
| 42 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 43 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 44 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet">
|
| 45 |
+
|
| 46 |
+
<!-- Icons -->
|
| 47 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 48 |
+
|
| 49 |
+
<!-- Three.js -->
|
| 50 |
+
<script type="importmap">
|
| 51 |
+
{
|
| 52 |
+
"imports": {
|
| 53 |
+
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
|
| 54 |
+
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
</script>
|
| 58 |
+
|
| 59 |
+
<style>
|
| 60 |
+
:root {
|
| 61 |
+
--bg-dark: #0b0c15;
|
| 62 |
+
--panel-bg: rgba(15, 23, 42, 0.75);
|
| 63 |
+
--primary: #00d2ff;
|
| 64 |
+
--accent: #ff0055;
|
| 65 |
+
--warning: #ffcc00;
|
| 66 |
+
--success: #00ff88;
|
| 67 |
+
--text-main: #e0e6ed;
|
| 68 |
+
--text-muted: #94a3b8;
|
| 69 |
+
--border: 1px solid rgba(255, 255, 255, 0.1);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
* {
|
| 73 |
+
box-sizing: border-box;
|
| 74 |
+
margin: 0;
|
| 75 |
+
padding: 0;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
body {
|
| 79 |
+
background-color: var(--bg-dark);
|
| 80 |
+
color: var(--text-main);
|
| 81 |
+
font-family: 'Inter', sans-serif;
|
| 82 |
+
overflow: hidden; /* Hide scrollbars, canvas covers all */
|
| 83 |
+
height: 100vh;
|
| 84 |
+
width: 100vw;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
/* Canvas Container */
|
| 88 |
+
#canvas-container {
|
| 89 |
+
position: absolute;
|
| 90 |
+
top: 0;
|
| 91 |
+
left: 0;
|
| 92 |
+
width: 100%;
|
| 93 |
+
height: 100%;
|
| 94 |
+
z-index: 1;
|
| 95 |
+
background: radial-gradient(circle at 50% 50%, #1a202c 0%, #020205 100%);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
/* UI Overlay Layer */
|
| 99 |
+
#ui-layer {
|
| 100 |
+
position: absolute;
|
| 101 |
+
top: 0;
|
| 102 |
+
left: 0;
|
| 103 |
+
width: 100%;
|
| 104 |
+
height: 100%;
|
| 105 |
+
z-index: 10;
|
| 106 |
+
pointer-events: none; /* Let clicks pass through to canvas where empty */
|
| 107 |
+
display: flex;
|
| 108 |
+
flex-direction: column;
|
| 109 |
+
justify-content: space-between;
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
/* Header */
|
| 113 |
+
header {
|
| 114 |
+
display: flex;
|
| 115 |
+
justify-content: space-between;
|
| 116 |
+
align-items: center;
|
| 117 |
+
padding: 1rem 2rem;
|
| 118 |
+
background: linear-gradient(180deg, rgba(0,0,0,0.8) 0%, transparent 100%);
|
| 119 |
+
pointer-events: auto;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
.logo {
|
| 123 |
+
font-family: 'Orbitron', sans-serif;
|
| 124 |
+
font-weight: 900;
|
| 125 |
+
font-size: 1.5rem;
|
| 126 |
+
letter-spacing: 2px;
|
| 127 |
+
color: var(--primary);
|
| 128 |
+
text-shadow: 0 0 10px rgba(0, 210, 255, 0.5);
|
| 129 |
+
display: flex;
|
| 130 |
+
align-items: center;
|
| 131 |
+
gap: 10px;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
.anycoder-link {
|
| 135 |
+
text-decoration: none;
|
| 136 |
+
color: var(--text-muted);
|
| 137 |
+
font-size: 0.9rem;
|
| 138 |
+
transition: color 0.3s;
|
| 139 |
+
display: flex;
|
| 140 |
+
align-items: center;
|
| 141 |
+
gap: 0.5rem;
|
| 142 |
+
background: rgba(255,255,255,0.05);
|
| 143 |
+
padding: 8px 16px;
|
| 144 |
+
border-radius: 20px;
|
| 145 |
+
border: 1px solid rgba(255,255,255,0.1);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
.anycoder-link:hover {
|
| 149 |
+
color: var(--primary);
|
| 150 |
+
background: rgba(255,255,255,0.1);
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
/* Main UI Grid */
|
| 154 |
+
.main-ui {
|
| 155 |
+
display: grid;
|
| 156 |
+
grid-template-columns: 300px 1fr 350px;
|
| 157 |
+
padding: 2rem;
|
| 158 |
+
height: 100%;
|
| 159 |
+
align-items: end; /* Align items to bottom */
|
| 160 |
+
gap: 2rem;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
/* Left Panel: Controls */
|
| 164 |
+
.control-panel {
|
| 165 |
+
background: var(--panel-bg);
|
| 166 |
+
backdrop-filter: blur(12px);
|
| 167 |
+
border: var(--border);
|
| 168 |
+
border-radius: 16px;
|
| 169 |
+
padding: 1.5rem;
|
| 170 |
+
pointer-events: auto;
|
| 171 |
+
display: flex;
|
| 172 |
+
flex-direction: column;
|
| 173 |
+
gap: 1.5rem;
|
| 174 |
+
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
|
| 175 |
+
margin-bottom: 2rem;
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
.panel-title {
|
| 179 |
+
font-family: 'Orbitron', sans-serif;
|
| 180 |
+
font-size: 1.1rem;
|
| 181 |
+
color: white;
|
| 182 |
+
border-bottom: 1px solid rgba(255,255,255,0.1);
|
| 183 |
+
padding-bottom: 0.5rem;
|
| 184 |
+
margin-bottom: 0.5rem;
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
.control-group {
|
| 188 |
+
display: flex;
|
| 189 |
+
flex-direction: column;
|
| 190 |
+
gap: 0.5rem;
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
.label-row {
|
| 194 |
+
display: flex;
|
| 195 |
+
justify-content: space-between;
|
| 196 |
+
font-size: 0.85rem;
|
| 197 |
+
color: var(--text-muted);
|
| 198 |
+
text-transform: uppercase;
|
| 199 |
+
letter-spacing: 1px;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
.value-display {
|
| 203 |
+
font-family: 'Orbitron', sans-serif;
|
| 204 |
+
color: var(--primary);
|
| 205 |
+
font-weight: bold;
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
input[type=range] {
|
| 209 |
+
width: 100%;
|
| 210 |
+
-webkit-appearance: none;
|
| 211 |
+
background: transparent;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
input[type=range]::-webkit-slider-thumb {
|
| 215 |
+
-webkit-appearance: none;
|
| 216 |
+
height: 18px;
|
| 217 |
+
width: 18px;
|
| 218 |
+
border-radius: 2px; /* Square for voxel feel */
|
| 219 |
+
background: var(--primary);
|
| 220 |
+
margin-top: -7px;
|
| 221 |
+
cursor: pointer;
|
| 222 |
+
box-shadow: 0 0 10px var(--primary);
|
| 223 |
+
border: 2px solid white;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
input[type=range]::-webkit-slider-runnable-track {
|
| 227 |
+
width: 100%;
|
| 228 |
+
height: 4px;
|
| 229 |
+
background: rgba(255,255,255,0.2);
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
.bar-container {
|
| 233 |
+
height: 6px;
|
| 234 |
+
background: rgba(0,0,0,0.5);
|
| 235 |
+
border-radius: 3px;
|
| 236 |
+
overflow: hidden;
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
.bar-fill {
|
| 240 |
+
height: 100%;
|
| 241 |
+
width: 50%;
|
| 242 |
+
background: var(--success);
|
| 243 |
+
transition: width 0.3s, background 0.3s;
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
.btn-scram {
|
| 247 |
+
background: rgba(255, 0, 85, 0.1);
|
| 248 |
+
border: 1px solid var(--accent);
|
| 249 |
+
color: var(--accent);
|
| 250 |
+
padding: 0.8rem;
|
| 251 |
+
border-radius: 4px;
|
| 252 |
+
font-family: 'Orbitron', sans-serif;
|
| 253 |
+
font-weight: 700;
|
| 254 |
+
cursor: pointer;
|
| 255 |
+
transition: all 0.2s;
|
| 256 |
+
text-transform: uppercase;
|
| 257 |
+
letter-spacing: 2px;
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
.btn-scram:hover {
|
| 261 |
+
background: var(--accent);
|
| 262 |
+
color: white;
|
| 263 |
+
box-shadow: 0 0 20px var(--accent);
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
/* Center: Just spacing for the 3D model visibility */
|
| 267 |
+
.center-spacer {
|
| 268 |
+
pointer-events: none;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
/* Right Panel: Info Cards */
|
| 272 |
+
.info-panel {
|
| 273 |
+
pointer-events: auto;
|
| 274 |
+
display: flex;
|
| 275 |
+
flex-direction: column;
|
| 276 |
+
gap: 1rem;
|
| 277 |
+
margin-bottom: 2rem;
|
| 278 |
+
max-height: 60vh;
|
| 279 |
+
overflow-y: auto;
|
| 280 |
+
padding-right: 5px;
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
/* Scrollbar styling */
|
| 284 |
+
.info-panel::-webkit-scrollbar { width: 6px; }
|
| 285 |
+
.info-panel::-webkit-scrollbar-track { background: rgba(0,0,0,0.1); }
|
| 286 |
+
.info-panel::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
|
| 287 |
+
|
| 288 |
+
.step-card {
|
| 289 |
+
background: var(--panel-bg);
|
| 290 |
+
backdrop-filter: blur(12px);
|
| 291 |
+
border: var(--border);
|
| 292 |
+
border-radius: 12px;
|
| 293 |
+
padding: 1.2rem;
|
| 294 |
+
transition: transform 0.2s, border-color 0.2s;
|
| 295 |
+
cursor: pointer;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
.step-card:hover, .step-card.active {
|
| 299 |
+
border-color: var(--primary);
|
| 300 |
+
background: rgba(20, 24, 36, 0.9);
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
.step-header {
|
| 304 |
+
display: flex;
|
| 305 |
+
align-items: center;
|
| 306 |
+
gap: 0.8rem;
|
| 307 |
+
margin-bottom: 0.5rem;
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
.step-number {
|
| 311 |
+
background: rgba(0, 210, 255, 0.1);
|
| 312 |
+
width: 24px;
|
| 313 |
+
height: 24px;
|
| 314 |
+
border-radius: 4px; /* Square */
|
| 315 |
+
display: flex;
|
| 316 |
+
align-items: center;
|
| 317 |
+
justify-content: center;
|
| 318 |
+
font-weight: 700;
|
| 319 |
+
color: var(--primary);
|
| 320 |
+
font-family: 'Orbitron', sans-serif;
|
| 321 |
+
font-size: 0.8rem;
|
| 322 |
+
border: 1px solid rgba(0, 210, 255, 0.3);
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
.step-title {
|
| 326 |
+
font-weight: 600;
|
| 327 |
+
color: white;
|
| 328 |
+
font-size: 0.95rem;
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
.step-desc {
|
| 332 |
+
color: var(--text-muted);
|
| 333 |
+
font-size: 0.85rem;
|
| 334 |
+
line-height: 1.5;
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
/* Loading Screen */
|
| 338 |
+
#loader {
|
| 339 |
+
position: fixed;
|
| 340 |
+
top: 0;
|
| 341 |
+
left: 0;
|
| 342 |
+
width: 100%;
|
| 343 |
+
height: 100%;
|
| 344 |
+
background: var(--bg-dark);
|
| 345 |
+
z-index: 100;
|
| 346 |
+
display: flex;
|
| 347 |
+
justify-content: center;
|
| 348 |
+
align-items: center;
|
| 349 |
+
flex-direction: column;
|
| 350 |
+
transition: opacity 0.5s;
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
.loader-spinner {
|
| 354 |
+
width: 50px;
|
| 355 |
+
height: 50px;
|
| 356 |
+
border: 3px solid rgba(255,255,255,0.1);
|
| 357 |
+
border-top-color: var(--primary);
|
| 358 |
+
border-radius: 50%;
|
| 359 |
+
animation: spin 1s linear infinite;
|
| 360 |
+
margin-bottom: 1rem;
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
@keyframes spin { 100% { transform: rotate(360deg); } }
|
| 364 |
+
|
| 365 |
+
/* Responsive */
|
| 366 |
+
@media (max-width: 1024px) {
|
| 367 |
+
.main-ui {
|
| 368 |
+
grid-template-columns: 1fr;
|
| 369 |
+
grid-template-rows: auto 1fr auto;
|
| 370 |
+
padding: 1rem;
|
| 371 |
+
gap: 1rem;
|
| 372 |
+
overflow-y: auto;
|
| 373 |
+
display: block;
|
| 374 |
+
}
|
| 375 |
+
.control-panel, .info-panel {
|
| 376 |
+
width: 100%;
|
| 377 |
+
margin-bottom: 1rem;
|
| 378 |
+
}
|
| 379 |
+
#ui-layer {
|
| 380 |
+
overflow-y: auto;
|
| 381 |
+
pointer-events: auto; /* Enable scrolling on mobile */
|
| 382 |
+
}
|
| 383 |
+
#canvas-container {
|
| 384 |
+
position: fixed;
|
| 385 |
+
}
|
| 386 |
+
}
|
| 387 |
+
</style>
|
| 388 |
+
</head>
|
| 389 |
+
<body>
|
| 390 |
+
|
| 391 |
+
<!-- Loading Screen -->
|
| 392 |
+
<div id="loader">
|
| 393 |
+
<div class="loader-spinner"></div>
|
| 394 |
+
<div style="font-family:'Orbitron'; letter-spacing:2px;">INITIALIZING VOXEL ENGINE...</div>
|
| 395 |
+
</div>
|
| 396 |
+
|
| 397 |
+
<!-- 3D Canvas -->
|
| 398 |
+
<div id="canvas-container"></div>
|
| 399 |
+
|
| 400 |
+
<!-- UI Overlay -->
|
| 401 |
+
<div id="ui-layer">
|
| 402 |
+
<header>
|
| 403 |
+
<div class="logo">
|
| 404 |
+
<i class="fa-solid fa-cube fa-spin-pulse" style="font-size: 1.2rem;"></i> VOXEL REACTOR
|
| 405 |
+
</div>
|
| 406 |
+
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
|
| 407 |
+
Built with anycoder <i class="fa-solid fa-arrow-up-right-from-square"></i>
|
| 408 |
+
</a>
|
| 409 |
+
</header>
|
| 410 |
+
|
| 411 |
+
<div class="main-ui">
|
| 412 |
+
<!-- Controls -->
|
| 413 |
+
<div class="control-panel">
|
| 414 |
+
<div class="panel-title">CONTROL CENTER</div>
|
| 415 |
+
|
| 416 |
+
<div class="control-group">
|
| 417 |
+
<div class="label-row">
|
| 418 |
+
<span>Control Rods</span>
|
| 419 |
+
<span class="value-display" id="rodValue">50%</span>
|
| 420 |
+
</div>
|
| 421 |
+
<input type="range" id="rodSlider" min="0" max="100" value="50">
|
| 422 |
+
</div>
|
| 423 |
+
|
| 424 |
+
<div class="control-group">
|
| 425 |
+
<div class="label-row">
|
| 426 |
+
<span>Core Temp</span>
|
| 427 |
+
<span class="value-display" id="tempValue">300°C</span>
|
| 428 |
+
</div>
|
| 429 |
+
<div class="bar-container">
|
| 430 |
+
<div class="bar-fill" id="tempBar"></div>
|
| 431 |
+
</div>
|
| 432 |
+
</div>
|
| 433 |
+
|
| 434 |
+
<div class="control-group">
|
| 435 |
+
<div class="label-row">
|
| 436 |
+
<span>Output Power</span>
|
| 437 |
+
<span class="value-display" id="powerValue">450 MW</span>
|
| 438 |
+
</div>
|
| 439 |
+
<div class="bar-container">
|
| 440 |
+
<div class="bar-fill" id="powerBar" style="background: var(--primary); width: 45%;"></div>
|
| 441 |
+
</div>
|
| 442 |
+
</div>
|
| 443 |
+
|
| 444 |
+
<div class="control-group" style="margin-top: 10px;">
|
| 445 |
+
<div class="label-row">
|
| 446 |
+
<span>Status</span>
|
| 447 |
+
<span class="value-display" id="statusText" style="color: var(--success);">STABLE</span>
|
| 448 |
+
</div>
|
| 449 |
+
</div>
|
| 450 |
+
|
| 451 |
+
<button class="btn-scram" onclick="scramReactor()">
|
| 452 |
+
<i class="fa-solid fa-radiation"></i> SCRAM (EMERGENCY)
|
| 453 |
+
</button>
|
| 454 |
+
</div>
|
| 455 |
+
|
| 456 |
+
<!-- Spacer to keep center clear for 3D view -->
|
| 457 |
+
<div class="center-spacer"></div>
|
| 458 |
+
|
| 459 |
+
<!-- Explainer -->
|
| 460 |
+
<div class="info-panel">
|
| 461 |
+
<div class="step-card active" onclick="focusCamera(0)">
|
| 462 |
+
<div class="step-header">
|
| 463 |
+
<div class="step-number">1</div>
|
| 464 |
+
<div class="step-title">Reactor Core</div>
|
| 465 |
+
</div>
|
| 466 |
+
<div class="step-desc">
|
| 467 |
+
Uranium fuel blocks undergo fission here. The blue glow represents Cherenkov radiation.
|
| 468 |
+
</div>
|
| 469 |
+
</div>
|
| 470 |
+
|
| 471 |
+
<div class="step-card" onclick="focusCamera(1)">
|
| 472 |
+
<div class="step-header">
|
| 473 |
+
<div class="step-number">2</div>
|
| 474 |
+
<div class="step-title">Control Rods</div>
|
| 475 |
+
</div>
|
| 476 |
+
<div class="step-desc">
|
| 477 |
+
Grey blocks on top. Lowering them (Slider right) absorbs neutrons and stops the reaction.
|
| 478 |
+
</div>
|
| 479 |
+
</div>
|
| 480 |
+
|
| 481 |
+
<div class="step-card" onclick="focusCamera(2)">
|
| 482 |
+
<div class="step-header">
|
| 483 |
+
<div class="step-number">3</div>
|
| 484 |
+
<div class="step-title">Heat Exchange</div>
|
| 485 |
+
</div>
|
| 486 |
+
<div class="step-desc">
|
| 487 |
+
Red pipes carry superheated water to the Steam Generator, boiling the secondary loop.
|
| 488 |
+
</div>
|
| 489 |
+
</div>
|
| 490 |
+
|
| 491 |
+
<div class="step-card" onclick="focusCamera(3)">
|
| 492 |
+
<div class="step-header">
|
| 493 |
+
<div class="step-number">4</div>
|
| 494 |
+
<div class="step-title">Turbine</div>
|
| 495 |
+
</div>
|
| 496 |
+
<div class="step-desc">
|
| 497 |
+
Steam spins the turbine blades (Fan) to generate electricity. Faster spin = More Power.
|
| 498 |
+
</div>
|
| 499 |
+
</div>
|
| 500 |
+
|
| 501 |
+
<div class="step-card" onclick="focusCamera(4)">
|
| 502 |
+
<div class="step-header">
|
| 503 |
+
<div class="step-number">5</div>
|
| 504 |
+
<div class="step-title">Cooling Tower</div>
|
| 505 |
+
</div>
|
| 506 |
+
<div class="step-desc">
|
| 507 |
+
Excess heat is released as steam. The water is cooled and pumped back in.
|
| 508 |
+
</div>
|
| 509 |
+
</div>
|
| 510 |
+
</div>
|
| 511 |
+
</div>
|
| 512 |
+
</div>
|
| 513 |
+
|
| 514 |
+
<script type="module">
|
| 515 |
+
import * as THREE from 'three';
|
| 516 |
+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
| 517 |
+
|
| 518 |
+
// --- 1. Simulation Logic (The Brains) ---
|
| 519 |
+
const state = {
|
| 520 |
+
rodPosition: 50, // 0-100
|
| 521 |
+
targetTemp: 300,
|
| 522 |
+
currentTemp: 300,
|
| 523 |
+
powerOutput: 0,
|
| 524 |
+
isScrammed: false
|
| 525 |
+
};
|
| 526 |
+
|
| 527 |
+
// DOM Elements
|
| 528 |
+
const els = {
|
| 529 |
+
rodSlider: document.getElementById('rodSlider'),
|
| 530 |
+
rodValue: document.getElementById('rodValue'),
|
| 531 |
+
tempValue: document.getElementById('tempValue'),
|
| 532 |
+
tempBar: document.getElementById('tempBar'),
|
| 533 |
+
powerValue: document.getElementById('powerValue'),
|
| 534 |
+
powerBar: document.getElementById('powerBar'),
|
| 535 |
+
statusText: document.getElementById('statusText'),
|
| 536 |
+
stepCards: document.querySelectorAll('.step-card')
|
| 537 |
+
};
|
| 538 |
+
|
| 539 |
+
// Event Listeners
|
| 540 |
+
els.rodSlider.addEventListener('input', (e) => {
|
| 541 |
+
state.rodPosition = parseInt(e.target.value);
|
| 542 |
+
state.isScrammed = false; // User interaction cancels scram state visually
|
| 543 |
+
});
|
| 544 |
+
|
| 545 |
+
window.scramReactor = () => {
|
| 546 |
+
state.isScrammed = true;
|
| 547 |
+
};
|
| 548 |
+
|
| 549 |
+
// --- 2. Three.js Setup (The Visuals) ---
|
| 550 |
+
const container = document.getElementById('canvas-container');
|
| 551 |
+
const scene = new THREE.Scene();
|
| 552 |
+
|
| 553 |
+
// Fog for depth
|
| 554 |
+
scene.fog = new THREE.FogExp2(0x0b0c15, 0.008);
|
| 555 |
+
|
| 556 |
+
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
|
| 557 |
+
camera.position.set(80, 60, 120);
|
| 558 |
+
|
| 559 |
+
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
| 560 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 561 |
+
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
| 562 |
+
renderer.shadowMap.enabled = true;
|
| 563 |
+
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
| 564 |
+
container.appendChild(renderer.domElement);
|
| 565 |
+
|
| 566 |
+
const controls = new OrbitControls(camera, renderer.domElement);
|
| 567 |
+
controls.enableDamping = true;
|
| 568 |
+
controls.dampingFactor = 0.05;
|
| 569 |
+
controls.maxPolarAngle = Math.PI / 2 - 0.1; // Don't go below ground
|
| 570 |
+
controls.target.set(30, 10, 0);
|
| 571 |
+
|
| 572 |
+
// --- 3. Lighting ---
|
| 573 |
+
const ambientLight = new THREE.AmbientLight(0x404040, 1.5); // Soft white light
|
| 574 |
+
scene.add(ambientLight);
|
| 575 |
+
|
| 576 |
+
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
|
| 577 |
+
dirLight.position.set(50, 100, 50);
|
| 578 |
+
dirLight.castShadow = true;
|
| 579 |
+
dirLight.shadow.mapSize.width = 2048;
|
| 580 |
+
dirLight.shadow.mapSize.height = 2048;
|
| 581 |
+
scene.add(dirLight);
|
| 582 |
+
|
| 583 |
+
// Reactor Glow Light (Point Light)
|
| 584 |
+
const coreLight = new THREE.PointLight(0x00d2ff, 0, 50);
|
| 585 |
+
coreLight.position.set(0, 15, 0);
|
| 586 |
+
scene.add(coreLight);
|
| 587 |
+
|
| 588 |
+
// --- 4. Voxel Builder Helper ---
|
| 589 |
+
const boxGeo = new THREE.BoxGeometry(1, 1, 1);
|
| 590 |
+
|
| 591 |
+
function createVoxel(x, y, z, color, size = 1, opacity = 1) {
|
| 592 |
+
const mat = new THREE.MeshStandardMaterial({
|
| 593 |
+
color: color,
|
| 594 |
+
roughness: 0.3,
|
| 595 |
+
metalness: 0.1,
|
| 596 |
+
transparent: opacity < 1,
|
| 597 |
+
opacity: opacity
|
| 598 |
+
});
|
| 599 |
+
const mesh = new THREE.Mesh(boxGeo, mat);
|
| 600 |
+
mesh.position.set(x, y, z);
|
| 601 |
+
mesh.scale.set(size, size, size);
|
| 602 |
+
mesh.castShadow = true;
|
| 603 |
+
mesh.receiveShadow = true;
|
| 604 |
+
scene.add(mesh);
|
| 605 |
+
return mesh;
|
| 606 |
+
}
|
| 607 |
+
|
| 608 |
+
function createVoxelGroup(positions, color, size = 1) {
|
| 609 |
+
const group = new THREE.Group();
|
| 610 |
+
positions.forEach(pos => {
|
| 611 |
+
const v = createVoxel(pos[0], pos[1], pos[2], color, size);
|
| 612 |
+
scene.add(v); // Add to scene, but we want to group them?
|
| 613 |
+
// For simplicity in this script, we just create meshes.
|
| 614 |
+
// If we need to move them together, we add to group.
|
| 615 |
+
scene.remove(v);
|
| 616 |
+
group.add(v);
|
| 617 |
+
});
|
| 618 |
+
scene.add(group);
|
| 619 |
+
return group;
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
// --- 5. Build The Scene ---
|
| 623 |
+
|
| 624 |
+
// Floor
|
| 625 |
+
const gridHelper = new THREE.GridHelper(200, 20, 0x333333, 0x222222);
|
| 626 |
+
scene.add(gridHelper);
|
| 627 |
+
const floor = createVoxel(30, -1, 0, 0x1a1d29, 1, 1);
|
| 628 |
+
floor.scale.set(200, 2, 100);
|
| 629 |
+
floor.receiveShadow = true;
|
| 630 |
+
|
| 631 |
+
// A. REACTOR CORE (x: 0)
|
| 632 |
+
// Base
|
| 633 |
+
createVoxel(0, 5, 0, 0x334155, 14); // Outer Shell
|
| 634 |
+
// Inner Core (Translucent Blue)
|
| 635 |
+
const reactorCore = createVoxel(0, 12, 0, 0x00d2ff, 10, 0.8);
|
| 636 |
+
reactorCore.material.emissive = new THREE.Color(0x0044aa);
|
| 637 |
+
|
| 638 |
+
// Control Rods (Group to move)
|
| 639 |
+
const rodGroup = new THREE.Group();
|
| 640 |
+
// Create a 3x3 grid of rods
|
| 641 |
+
for(let i=-2; i<=2; i+=2) {
|
| 642 |
+
for(let j=-2; j<=2; j+=2) {
|
| 643 |
+
const rod = new THREE.Mesh(boxGeo, new THREE.MeshStandardMaterial({color: 0x94a3b8}));
|
| 644 |
+
rod.position.set(i, 5, j);
|
| 645 |
+
rod.scale.set(1.5, 12, 1.5);
|
| 646 |
+
rodGroup.add(rod);
|
| 647 |
+
}
|
| 648 |
+
}
|
| 649 |
+
rodGroup.position.set(0, 15, 0); // Start position
|
| 650 |
+
scene.add(rodGroup);
|
| 651 |
+
|
| 652 |
+
// B. PIPES (Connecting 0 to 40)
|
| 653 |
+
// Hot Pipe (Red) - Top
|
| 654 |
+
const hotPipe = createVoxel(20, 15, 0, 0xff0055, 1);
|
| 655 |
+
hotPipe.scale.set(25, 2, 2);
|
| 656 |
+
|
| 657 |
+
// Cold Pipe (Blue) - Bottom
|
| 658 |
+
const coldPipe = createVoxel(20, 5, 0, 0x00d2ff, 1);
|
| 659 |
+
coldPipe.scale.set(25, 2, 2);
|
| 660 |
+
|
| 661 |
+
// C. STEAM GENERATOR (x: 40)
|
| 662 |
+
createVoxel(40, 10, 0, 0x475569, 12); // Main block
|
| 663 |
+
createVoxel(40, 20, 0, 0x64748b, 8); // Top dome
|
| 664 |
+
|
| 665 |
+
// Steam Pipe to Turbine
|
| 666 |
+
const steamPipe = createVoxel(60, 18, 0, 0xffffff, 1);
|
| 667 |
+
steamPipe.scale.set(25, 2, 2);
|
| 668 |
+
|
| 669 |
+
// D. TURBINE (x: 80)
|
| 670 |
+
const turbineBase = createVoxel(80, 8, 0, 0x1e293b, 12);
|
| 671 |
+
turbineBase.scale.set(12, 16, 12);
|
| 672 |
+
|
| 673 |
+
// The Spinning Blades
|
| 674 |
+
const turbineRotor = new THREE.Group();
|
| 675 |
+
const bladeMat = new THREE.MeshStandardMaterial({color: 0xcccccc, metalness: 0.8});
|
| 676 |
+
|
| 677 |
+
for(let i=0; i<4; i++) {
|
| 678 |
+
const blade = new THREE.Mesh(boxGeo, bladeMat);
|
| 679 |
+
blade.scale.set(14, 2, 4);
|
| 680 |
+
blade.rotation.x = (Math.PI / 2) * i; // 4 blades
|
| 681 |
+
blade.position.set(0, 0, 0); // Center
|
| 682 |
+
// We actually want them radial.
|
| 683 |
+
const bladeGroup = new THREE.Group();
|
| 684 |
+
bladeGroup.add(blade);
|
| 685 |
+
bladeGroup.rotation.x = i * (Math.PI/2);
|
| 686 |
+
turbineRotor.add(bladeGroup);
|
| 687 |
+
}
|
| 688 |
+
turbineRotor.position.set(80, 18, 0);
|
| 689 |
+
turbineRotor.rotation.y = Math.PI / 2; // Face sideways
|
| 690 |
+
scene.add(turbineRotor);
|
| 691 |
+
|
| 692 |
+
// Generator box next to it
|
| 693 |
+
createVoxel(95, 8, 0, 0x0f172a, 8);
|
| 694 |
+
|
| 695 |
+
// E. COOLING TOWER (x: 120)
|
| 696 |
+
// Build a hollow-ish shape using stacked rings of voxels would be expensive.
|
| 697 |
+
// Let's use a simple tapered geometry but render it "flat" shading to look low poly.
|
| 698 |
+
const towerGeo = new THREE.CylinderGeometry(10, 18, 30, 8);
|
| 699 |
+
const towerMat = new THREE.MeshStandardMaterial({color: 0xe2e8f0, flatShading: true});
|
| 700 |
+
const tower = new THREE.Mesh(towerGeo, towerMat);
|
| 701 |
+
tower.position.set(120, 15, 0);
|
| 702 |
+
tower.castShadow = true;
|
| 703 |
+
scene.add(tower);
|
| 704 |
+
|
| 705 |
+
// Steam Particles System (Simple Cubes)
|
| 706 |
+
const particleCount = 20;
|
| 707 |
+
const particles = [];
|
| 708 |
+
for(let i=0; i<particleCount; i++) {
|
| 709 |
+
const p = new THREE.Mesh(boxGeo, new THREE.MeshBasicMaterial({color: 0xffffff, transparent:true, opacity:0.6}));
|
| 710 |
+
p.scale.set(1.5, 1.5, 1.5);
|
| 711 |
+
p.position.set(120, 30, 0);
|
| 712 |
+
p.userData = {
|
| 713 |
+
speed: 0.1 + Math.random() * 0.2,
|
| 714 |
+
offset: Math.random() * 100
|
| 715 |
+
};
|
| 716 |
+
scene.add(p);
|
| 717 |
+
particles.push(p);
|
| 718 |
+
}
|
| 719 |
+
|
| 720 |
+
// --- 6. Camera Focus Logic ---
|
| 721 |
+
window.focusCamera = (stepIndex) => {
|
| 722 |
+
// Update UI
|
| 723 |
+
els.stepCards.forEach((c, i) => {
|
| 724 |
+
if(i === stepIndex) c.classList.add('active');
|
| 725 |
+
else c.classList.remove('active');
|
| 726 |
+
});
|
| 727 |
+
|
| 728 |
+
// Move Camera
|
| 729 |
+
const targets = [
|
| 730 |
+
{ x: 0, y: 15, z: 0, cx: 40, cy: 30, cz: 60 }, // Core
|
| 731 |
+
{ x: 0, y: 20, z: 0, cx: 30, cy: 40, cz: 50 }, // Rods
|
| 732 |
+
{ x: 40, y: 15, z: 0, cx: 40, cy: 30, cz: 60 }, // Heat Ex
|
| 733 |
+
{ x: 80, y: 18, z: 0, cx: 80, cy: 30, cz: 60 }, // Turbine
|
| 734 |
+
{ x: 120, y: 15, z: 0, cx: 120, cy: 40, cz: 70 } // Tower
|
| 735 |
+
];
|
| 736 |
+
|
| 737 |
+
const t = targets[stepIndex];
|
| 738 |
+
|
| 739 |
+
// Use simple interpolation in update loop or GSAP if available.
|
| 740 |
+
// Since no GSAP, we'll snap or use a simple lerp target variable.
|
| 741 |
+
camTargetPos.set(t.cx, t.cy, t.cz);
|
| 742 |
+
controlsTargetPos.set(t.x, t.y, t.z);
|
| 743 |
+
};
|
| 744 |
+
|
| 745 |
+
const camTargetPos = new THREE.Vector3(80, 60, 120);
|
| 746 |
+
const controlsTargetPos = new THREE.Vector3(30, 10, 0);
|
| 747 |
+
|
| 748 |
+
// --- 7. Main Loop ---
|
| 749 |
+
function updatePhysics() {
|
| 750 |
+
// Physics Logic (Same as 2D version)
|
| 751 |
+
let reactionPotential = (100 - state.rodPosition);
|
| 752 |
+
let targetT = 50 + (reactionPotential * 8);
|
| 753 |
+
let changeRate = 1.5;
|
| 754 |
+
|
| 755 |
+
if (state.currentTemp < targetT) state.currentTemp += changeRate;
|
| 756 |
+
if (state.currentTemp > targetT) state.currentTemp -= changeRate;
|
| 757 |
+
|
| 758 |
+
let efficiency = 0;
|
| 759 |
+
if(state.currentTemp > 100) {
|
| 760 |
+
efficiency = (state.currentTemp - 100) / 500;
|
| 761 |
+
if(efficiency > 1) efficiency = 1;
|
| 762 |
+
}
|
| 763 |
+
state.powerOutput = Math.floor(efficiency * 1000);
|
| 764 |
+
|
| 765 |
+
// SCRAM Logic
|
| 766 |
+
if(state.isScrammed && state.rodPosition < 100) {
|
| 767 |
+
state.rodPosition += 2;
|
| 768 |
+
if(state.rodPosition > 100) state.rodPosition = 100;
|
| 769 |
+
els.rodSlider.value = state.rodPosition;
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
// UI Updates
|
| 773 |
+
els.rodValue.innerText = state.rodPosition + "%";
|
| 774 |
+
els.tempValue.innerText = Math.floor(state.currentTemp) + "°C";
|
| 775 |
+
|
| 776 |
+
let tempPercent = Math.min(100, (state.currentTemp / 800) * 100);
|
| 777 |
+
els.tempBar.style.width = `${tempPercent}%`;
|
| 778 |
+
|
| 779 |
+
if(state.currentTemp < 300) {
|
| 780 |
+
els.tempBar.style.background = 'var(--primary)';
|
| 781 |
+
els.statusText.innerText = "WARMING UP";
|
| 782 |
+
els.statusText.style.color = "var(--primary)";
|
| 783 |
+
} else if (state.currentTemp < 600) {
|
| 784 |
+
els.tempBar.style.background = 'var(--success)';
|
| 785 |
+
els.statusText.innerText = "OPTIMAL";
|
| 786 |
+
els.statusText.style.color = "var(--success)";
|
| 787 |
+
} else {
|
| 788 |
+
els.tempBar.style.background = 'var(--accent)';
|
| 789 |
+
els.statusText.innerText = "CRITICAL";
|
| 790 |
+
els.statusText.style.color = "var(--accent)";
|
| 791 |
+
}
|
| 792 |
+
|
| 793 |
+
els.powerValue.innerText = state.powerOutput + " MW";
|
| 794 |
+
els.powerBar.style.width = `${state.powerOutput / 10}%`;
|
| 795 |
+
}
|
| 796 |
+
|
| 797 |
+
function animate() {
|
| 798 |
+
requestAnimationFrame(animate);
|
| 799 |
+
|
| 800 |
+
updatePhysics();
|
| 801 |
+
|
| 802 |
+
// 1. Animate Rods
|
| 803 |
+
// Map 0-100% to Y position.
|
| 804 |
+
// 0% inserted = High Y (out). 100% inserted = Low Y (in).
|
| 805 |
+
// Let's say range is Y=15 (out) to Y=8 (in)
|
| 806 |
+
const rodY = 15 - (state.rodPosition / 100) * 7;
|
| 807 |
+
rodGroup.position.y += (rodY - rodGroup.position.y) * 0.1; // Smooth lerp
|
| 808 |
+
|
| 809 |
+
// 2. Animate Glow
|
| 810 |
+
// Intensity based on reaction (inverse of rod pos)
|
| 811 |
+
const glowIntensity = (100 - state.rodPosition) / 20; // 0 to 5
|
| 812 |
+
coreLight.intensity = glowIntensity;
|
| 813 |
+
reactorCore.material.emissiveIntensity = glowIntensity / 5;
|
| 814 |
+
|
| 815 |
+
// Color shift based on temp
|
| 816 |
+
if(state.currentTemp > 600) {
|
| 817 |
+
coreLight.color.setHex(0xff0055); // Red alert
|
| 818 |
+
reactorCore.material.emissive.setHex(0xff0055);
|
| 819 |
+
} else {
|
| 820 |
+
coreLight.color.setHex(0x00d2ff); // Blue normal
|
| 821 |
+
reactorCore.material.emissive.setHex(0x0044aa);
|
| 822 |
+
}
|
| 823 |
+
|
| 824 |
+
// 3. Animate Turbine
|
| 825 |
+
if(state.powerOutput > 0) {
|
| 826 |
+
const speed = state.powerOutput * 0.0005;
|
| 827 |
+
turbineRotor.rotation.x += speed;
|
| 828 |
+
}
|
| 829 |
+
|
| 830 |
+
// 4. Animate Steam Particles
|
| 831 |
+
particles.forEach(p => {
|
| 832 |
+
if(state.powerOutput > 50) {
|