| <html lang="th"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'none';"> | |
| <title>Ω Hydro-Compute — Water-Based Computer Visualization</title> | |
| <style> | |
| :root { | |
| --water: #00d4ff; | |
| --steam: #ff6b35; | |
| --heat: #ff0044; | |
| --cool: #00ff88; | |
| --dark: #0a0a1a; | |
| --darker: #050510; | |
| --light: #e0e8ff; | |
| --glass: rgba(10, 10, 30, 0.85); | |
| --glass-border: rgba(0, 212, 255, 0.15); | |
| } | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: var(--darker); | |
| color: var(--light); | |
| overflow-x: hidden; | |
| } | |
| .bg { | |
| position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; | |
| background: radial-gradient(ellipse at 30% 70%, rgba(0,212,255,0.08) 0%, transparent 50%), | |
| radial-gradient(ellipse at 70% 30%, rgba(255,0,68,0.06) 0%, transparent 50%), | |
| var(--darker); | |
| } | |
| .container { max-width: 1400px; margin: 0 auto; padding: 20px; } | |
| /* Header */ | |
| .header { | |
| text-align: center; padding: 30px 20px; | |
| background: var(--glass); border: 1px solid var(--glass-border); | |
| border-radius: 16px; margin-bottom: 20px; | |
| } | |
| .header h1 { | |
| font-size: clamp(1.5rem, 3vw, 2.5rem); | |
| background: linear-gradient(135deg, var(--water), var(--cool)); | |
| -webkit-background-clip: text; -webkit-text-fill-color: transparent; | |
| } | |
| .header .subtitle { color: rgba(224,232,255,0.6); margin-top: 8px; } | |
| /* Controls */ | |
| .controls { | |
| display: flex; gap: 15px; flex-wrap: wrap; justify-content: center; | |
| padding: 20px; background: var(--glass); border: 1px solid var(--glass-border); | |
| border-radius: 12px; margin-bottom: 20px; | |
| } | |
| .controls label { color: var(--water); font-size: 0.85rem; font-weight: 600; } | |
| .controls input, .controls select { | |
| background: rgba(0,0,0,0.4); border: 1px solid var(--glass-border); | |
| color: var(--light); padding: 8px 12px; border-radius: 8px; font-size: 0.85rem; | |
| } | |
| .controls button { | |
| background: linear-gradient(135deg, var(--water), #0088cc); | |
| color: white; border: none; padding: 10px 24px; border-radius: 8px; | |
| font-weight: 700; cursor: pointer; font-size: 0.9rem; | |
| } | |
| .controls button:hover { opacity: 0.85; } | |
| .controls button.stop { background: linear-gradient(135deg, var(--heat), #cc0033); } | |
| /* Dashboard Grid */ | |
| .dashboard { | |
| display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); | |
| gap: 20px; margin-bottom: 20px; | |
| } | |
| .card { | |
| background: var(--glass); border: 1px solid var(--glass-border); | |
| border-radius: 12px; padding: 20px; position: relative; overflow: hidden; | |
| } | |
| .card::before { | |
| content: ''; position: absolute; top: 0; left: 0; width: 4px; height: 100%; | |
| background: linear-gradient(180deg, var(--water), var(--cool)); | |
| } | |
| .card h3 { | |
| font-size: 0.85rem; color: var(--water); margin-bottom: 12px; | |
| text-transform: uppercase; letter-spacing: 1px; | |
| } | |
| .card .value { | |
| font-size: 2rem; font-weight: 900; font-family: 'Consolas', monospace; | |
| } | |
| .card .unit { font-size: 0.8rem; color: rgba(224,232,255,0.5); } | |
| .card .bar { | |
| height: 6px; background: rgba(0,0,0,0.3); border-radius: 3px; | |
| margin-top: 12px; overflow: hidden; | |
| } | |
| .card .bar-fill { | |
| height: 100%; border-radius: 3px; transition: width 0.3s; | |
| } | |
| /* Canvas */ | |
| .canvas-container { | |
| background: var(--glass); border: 1px solid var(--glass-border); | |
| border-radius: 12px; padding: 20px; margin-bottom: 20px; | |
| } | |
| .canvas-container h3 { | |
| color: var(--water); margin-bottom: 15px; font-size: 1rem; | |
| } | |
| canvas { | |
| width: 100%; height: 300px; background: rgba(0,0,0,0.3); | |
| border-radius: 8px; display: block; | |
| } | |
| /* Water Animation */ | |
| .water-viz { | |
| background: var(--glass); border: 1px solid var(--glass-border); | |
| border-radius: 12px; padding: 20px; margin-bottom: 20px; | |
| } | |
| .water-viz h3 { color: var(--water); margin-bottom: 15px; } | |
| .water-tank { | |
| position: relative; width: 100%; height: 200px; | |
| background: rgba(0,0,0,0.3); border-radius: 8px; overflow: hidden; | |
| } | |
| .water-level { | |
| position: absolute; bottom: 0; left: 0; width: 100%; | |
| background: linear-gradient(180deg, rgba(0,212,255,0.3), rgba(0,212,255,0.7)); | |
| transition: height 0.5s; | |
| } | |
| .water-level::before { | |
| content: ''; position: absolute; top: -10px; left: 0; width: 200%; | |
| height: 20px; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 20'%3E%3Cpath d='M0 10 Q25 0 50 10 T100 10 T150 10 T200 10 V20 H0Z' fill='rgba(0,212,255,0.5)'/%3E%3C/svg%3E") repeat-x; | |
| animation: wave 3s linear infinite; | |
| } | |
| @keyframes wave { from { transform: translateX(0); } to { transform: translateX(-50%); } } | |
| .steam-particles { | |
| position: absolute; top: 0; left: 0; width: 100%; height: 100%; | |
| pointer-events: none; | |
| } | |
| .steam { | |
| position: absolute; width: 8px; height: 8px; | |
| background: rgba(255,107,53,0.4); border-radius: 50%; | |
| animation: rise 2s ease-out infinite; | |
| } | |
| @keyframes rise { | |
| 0% { transform: translateY(0) scale(1); opacity: 0.6; } | |
| 100% { transform: translateY(-150px) scale(0.2); opacity: 0; } | |
| } | |
| /* Status */ | |
| .status-bar { | |
| display: flex; gap: 20px; flex-wrap: wrap; justify-content: center; | |
| padding: 15px; background: var(--glass); border: 1px solid var(--glass-border); | |
| border-radius: 12px; margin-bottom: 20px; | |
| } | |
| .status-item { | |
| display: flex; align-items: center; gap: 8px; font-size: 0.85rem; | |
| } | |
| .status-dot { | |
| width: 10px; height: 10px; border-radius: 50%; | |
| animation: pulse 2s ease-in-out infinite; | |
| } | |
| @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } } | |
| .status-dot.active { background: var(--cool); } | |
| .status-dot.warning { background: #ffaa00; } | |
| .status-dot.danger { background: var(--heat); } | |
| /* Logic Gate Viz */ | |
| .logic-gates { | |
| display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 15px; margin-bottom: 20px; | |
| } | |
| .gate-card { | |
| background: rgba(0,0,0,0.3); border: 1px solid var(--glass-border); | |
| border-radius: 8px; padding: 15px; text-align: center; | |
| } | |
| .gate-card h4 { color: var(--water); font-size: 0.8rem; margin-bottom: 10px; } | |
| .gate-inputs { display: flex; justify-content: center; gap: 15px; margin-bottom: 10px; } | |
| .gate-input { | |
| width: 30px; height: 30px; border-radius: 50%; | |
| display: flex; align-items: center; justify-content: center; | |
| font-size: 0.75rem; font-weight: 700; | |
| } | |
| .gate-input.on { background: var(--cool); color: var(--dark); } | |
| .gate-input.off { background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.3); } | |
| .gate-output { | |
| width: 40px; height: 40px; border-radius: 50%; margin: 0 auto; | |
| display: flex; align-items: center; justify-content: center; | |
| font-weight: 900; font-size: 1rem; | |
| } | |
| .gate-output.on { background: var(--water); color: var(--dark); } | |
| .gate-output.off { background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.3); } | |
| /* Footer */ | |
| .footer { | |
| text-align: center; padding: 30px; color: rgba(224,232,255,0.3); | |
| font-size: 0.8rem; | |
| } | |
| @media (max-width: 768px) { | |
| .dashboard { grid-template-columns: 1fr; } | |
| .container { padding: 10px; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="bg"></div> | |
| <div class="container"> | |
| <!-- HEADER --> | |
| <div class="header"> | |
| <h1>Ω Hydro-Compute Engine</h1> | |
| <p class="subtitle">คอมพิวเตอร์พลังงานน้ำ — ใช้ความร้อนขับเคลื่อนคณิตศาสตร์</p> | |
| </div> | |
| <!-- STATUS BAR --> | |
| <div class="status-bar"> | |
| <div class="status-item"> | |
| <div class="status-dot active" id="statusDot"></div> | |
| <span id="statusText">พร้อมทำงาน</span> | |
| </div> | |
| <div class="status-item"> | |
| <span>รอบ: <strong id="roundDisplay">0</strong></span> | |
| </div> | |
| <div class="status-item"> | |
| <span>เวลา: <strong id="timeDisplay">00:00</strong></span> | |
| </div> | |
| <div class="status-item"> | |
| <span>สถานะน้ำ: <strong id="waterState">ของเหลว</strong></span> | |
| </div> | |
| </div> | |
| <!-- CONTROLS --> | |
| <div class="controls"> | |
| <div> | |
| <label>จำนวนรอบ:</label><br> | |
| <input type="number" id="roundsInput" value="10000" min="100" max="1000000" step="1000"> | |
| </div> | |
| <div> | |
| <label>พลังงานเริ่มต้น (W):</label><br> | |
| <input type="number" id="powerInput" value="0.01" min="0.001" max="100" step="0.001"> | |
| </div> | |
| <div> | |
| <label>เพิ่มพลังงาน/รอบ:</label><br> | |
| <input type="number" id="incrementInput" value="0.00000001" step="0.00000001"> | |
| </div> | |
| <div> | |
| <label>ความเร็วจำลอง:</label><br> | |
| <select id="speedSelect"> | |
| <option value="1">ช้า (1x)</option> | |
| <option value="10" selected>ปกติ (10x)</option> | |
| <option value="100">เร็ว (100x)</option> | |
| <option value="1000">เร็วสุด (1000x)</option> | |
| </select> | |
| </div> | |
| <div style="display:flex;align-items:flex-end;gap:10px;"> | |
| <button id="startBtn" onclick="startSimulation()">▶ เริ่มจำลอง</button> | |
| <button class="stop" id="stopBtn" onclick="stopSimulation()" disabled>⏹ หยุด</button> | |
| </div> | |
| </div> | |
| <!-- DASHBOARD --> | |
| <div class="dashboard"> | |
| <div class="card"> | |
| <h3>🔥 อุณหภูมิ</h3> | |
| <div class="value" id="tempValue" style="color:var(--heat)">25.0°C</div> | |
| <div class="unit">เป้าหมาย: 50°C (เหมาะสมที่สุด)</div> | |
| <div class="bar"><div class="bar-fill" id="tempBar" style="width:25%;background:var(--heat)"></div></div> | |
| </div> | |
| <div class="card"> | |
| <h3>⚡ พลังงาน</h3> | |
| <div class="value" id="powerValue" style="color:var(--water)">0.010 W</div> | |
| <div class="unit">พลังงานความร้อน → การไหลของน้ำ</div> | |
| <div class="bar"><div class="bar-fill" id="powerBar" style="width:1%;background:var(--water)"></div></div> | |
| </div> | |
| <div class="card"> | |
| <h3>💧 การไหลของน้ำ</h3> | |
| <div class="value" id="flowValue" style="color:var(--cool)">0.000</div> | |
| <div class="unit">หน่วย/วินาที</div> | |
| <div class="bar"><div class="bar-fill" id="flowBar" style="width:0%;background:var(--cool)"></div></div> | |
| </div> | |
| <div class="card"> | |
| <h3>🧠 ประสิทธิภาพคณิตศาสตร์</h3> | |
| <div class="value" id="effValue" style="color:#ffaa00">1.000</div> | |
| <div class="unit">M(t) = M₀ × e^(-α(T-T_opt)²)</div> | |
| <div class="bar"><div class="bar-fill" id="effBar" style="width:100%;background:#ffaa00"></div></div> | |
| </div> | |
| <div class="card"> | |
| <h3>🔢 การดำเนินการทั้งหมด</h3> | |
| <div class="value" id="opsValue" style="color:var(--water)">0</div> | |
| <div class="unit">กิริยาคณิตศาสตร์ (bit flips)</div> | |
| <div class="bar"><div class="bar-fill" id="opsBar" style="width:0%;background:var(--water)"></div></div> | |
| </div> | |
| <div class="card"> | |
| <h3>💾 บิตที่ประมวลผล</h3> | |
| <div class="value" id="bitsValue" style="color:var(--cool)">0</div> | |
| <div class="unit">1 จูล = 1 บิต (สมมุติฐาน)</div> | |
| <div class="bar"><div class="bar-fill" id="bitsBar" style="width:0%;background:var(--cool)"></div></div> | |
| </div> | |
| </div> | |
| <!-- WATER TANK VISUALIZATION --> | |
| <div class="water-viz"> | |
| <h3>💧 ถังน้ำจำลอง — ระดับน้ำ + ไอน้ำ</h3> | |
| <div class="water-tank" id="waterTank"> | |
| <div class="water-level" id="waterLevel" style="height:60%"></div> | |
| <div class="steam-particles" id="steamParticles"></div> | |
| </div> | |
| </div> | |
| <!-- CHART --> | |
| <div class="canvas-container"> | |
| <h3>📈 กราฟอุณหภูมิ + ประสิทธิภาพ (Real-time)</h3> | |
| <canvas id="mainChart"></canvas> | |
| </div> | |
| <!-- LOGIC GATES --> | |
| <div class="canvas-container"> | |
| <h3>🔧 ประตูตรรกะน้ำ — Water Logic Gates</h3> | |
| <div class="logic-gates"> | |
| <div class="gate-card"> | |
| <h4>AND Gate</h4> | |
| <div class="gate-inputs"> | |
| <div class="gate-input on" id="andA">1</div> | |
| <div class="gate-input on" id="andB">1</div> | |
| </div> | |
| <div class="gate-output on" id="andOut">1</div> | |
| </div> | |
| <div class="gate-card"> | |
| <h4>OR Gate</h4> | |
| <div class="gate-inputs"> | |
| <div class="gate-input on" id="orA">1</div> | |
| <div class="gate-input off" id="orB">0</div> | |
| </div> | |
| <div class="gate-output on" id="orOut">1</div> | |
| </div> | |
| <div class="gate-card"> | |
| <h4>NOT Gate</h4> | |
| <div class="gate-inputs"> | |
| <div class="gate-input on" id="notA">1</div> | |
| </div> | |
| <div class="gate-output off" id="notOut">0</div> | |
| </div> | |
| <div class="gate-card"> | |
| <h4>XOR Gate</h4> | |
| <div class="gate-inputs"> | |
| <div class="gate-input on" id="xorA">1</div> | |
| <div class="gate-input off" id="xorB">0</div> | |
| </div> | |
| <div class="gate-output on" id="xorOut">1</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- FOOTER --> | |
| <div class="footer"> | |
| <p>Ω Hydro-Compute Engine v1.0 | โดย นายไชยภพ นิลแพทย์ (Chaiyphop Nilpat)</p> | |
| <p>100% Offline — ไม่มีการเชื่อมต่อภายนอก</p> | |
| </div> | |
| </div> | |
| <script> | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // OFFLINE ENFORCEMENT | |
| // ═══════════════════════════════════════════════════════════════════ | |
| (function() { | |
| window.fetch = function() { return Promise.reject(new Error('Blocked: Offline only')); }; | |
| window.XMLHttpRequest = function() { throw new Error('Blocked: Offline only'); }; | |
| window.WebSocket = function() { throw new Error('Blocked: Offline only'); }; | |
| window.open = function() { return null; }; | |
| document.addEventListener('click', function(e) { | |
| if (e.target.tagName === 'A' && e.target.href && e.target.href.startsWith('http')) { | |
| e.preventDefault(); | |
| } | |
| }); | |
| })(); | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // PHYSICS CONSTANTS | |
| // ═══════════════════════════════════════════════════════════════════ | |
| const CONSTANTS = { | |
| ROOM_TEMP: 25.0, | |
| OPTIMAL_TEMP: 50.0, | |
| ALPHA_TEMP: 0.01, | |
| EFFICIENCY_HEAT: 0.85, | |
| HEAT_TRANSFER: 10.0, | |
| SURFACE_AREA: 0.5, | |
| THERMAL_CAPACITY: 500.0, | |
| FLOW_COEFF: 10.0, | |
| FLOW_RESISTANCE: 0.5, | |
| BITS_PER_FLOW: 1000, | |
| VAPORIZATION_THRESHOLD: 0.226, | |
| MAX_ENERGY: 10000000 | |
| }; | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // SIMULATION STATE | |
| // ═══════════════════════════════════════════════════════════════════ | |
| let sim = { | |
| running: false, | |
| round: 0, | |
| power: 0.01, | |
| temperature: 25.0, | |
| totalOps: 0, | |
| totalBits: 0, | |
| history: [], | |
| maxHistory: 500, | |
| startTime: null, | |
| animFrame: null | |
| }; | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // PHYSICS ENGINE | |
| // ═══════════════════════════════════════════════════════════════════ | |
| function thermalStep(power, temp, dt) { | |
| const Q_in = CONSTANTS.EFFICIENCY_HEAT * power; | |
| const Q_out = CONSTANTS.HEAT_TRANSFER * CONSTANTS.SURFACE_AREA * (temp - CONSTANTS.ROOM_TEMP); | |
| const dT = (Q_in - Q_out) / CONSTANTS.THERMAL_CAPACITY * dt; | |
| return Math.max(temp + dT, CONSTANTS.ROOM_TEMP); | |
| } | |
| function flowStep(power, temp, dt) { | |
| const tempFactor = 1.0 / (1.0 + CONSTANTS.FLOW_RESISTANCE * (temp - CONSTANTS.ROOM_TEMP) / 100.0); | |
| return CONSTANTS.FLOW_COEFF * power * tempFactor; | |
| } | |
| function mathActionStep(flow, temp) { | |
| const eff = Math.exp(-CONSTANTS.ALPHA_TEMP * (temp - CONSTANTS.OPTIMAL_TEMP) ** 2); | |
| return { efficiency: eff, operations: flow * eff, bits: flow * eff * CONSTANTS.BITS_PER_FLOW }; | |
| } | |
| function decodeEnergy(energy) { | |
| const normalized = Math.min(energy / CONSTANTS.MAX_ENERGY, 1.0); | |
| return { | |
| normalized, | |
| state: normalized >= CONSTANTS.VAPORIZATION_THRESHOLD ? 'ไอ' : 'ของเหลว', | |
| binaryBits: Math.floor(energy) | |
| }; | |
| } | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // SIMULATION LOOP | |
| // ═══════════════════════════════════════════════════════════════════ | |
| function simulationTick() { | |
| if (!sim.running) return; | |
| const speed = parseInt(document.getElementById('speedSelect').value); | |
| const roundsPerTick = speed; | |
| for (let i = 0; i < roundsPerTick; i++) { | |
| sim.round++; | |
| // Physics | |
| sim.temperature = thermalStep(sim.power, sim.temperature, 1.0); | |
| const flow = flowStep(sim.power, sim.temperature, 1.0); | |
| const action = mathActionStep(flow, sim.temperature); | |
| sim.totalOps += action.operations; | |
| sim.totalBits += action.bits; | |
| // Increase power | |
| const increment = parseFloat(document.getElementById('incrementInput').value); | |
| sim.power = Math.min(sim.power + increment, 100.0); | |
| // Record history | |
| if (sim.round % Math.max(1, Math.floor(roundsPerTick / 10)) === 0) { | |
| sim.history.push({ | |
| round: sim.round, | |
| temp: sim.temperature, | |
| eff: action.efficiency, | |
| flow: flow, | |
| ops: sim.totalOps, | |
| bits: sim.totalBits, | |
| power: sim.power | |
| }); | |
| if (sim.history.length > sim.maxHistory) sim.history.shift(); | |
| } | |
| } | |
| updateUI(); | |
| drawChart(); | |
| updateWaterViz(); | |
| updateLogicGates(); | |
| sim.animFrame = requestAnimationFrame(simulationTick); | |
| } | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // UI UPDATE | |
| // ═══════════════════════════════════════════════════════════════════ | |
| function updateUI() { | |
| const action = mathActionStep(flowStep(sim.power, sim.temperature, 1.0), sim.temperature); | |
| const code = decodeEnergy(sim.power); | |
| document.getElementById('roundDisplay').textContent = sim.round.toLocaleString(); | |
| document.getElementById('tempValue').textContent = sim.temperature.toFixed(1) + '°C'; | |
| document.getElementById('powerValue').textContent = sim.power.toFixed(4) + ' W'; | |
| document.getElementById('flowValue').textContent = flowStep(sim.power, sim.temperature, 1.0).toFixed(4); | |
| document.getElementById('effValue').textContent = action.efficiency.toFixed(4); | |
| document.getElementById('opsValue').textContent = Math.floor(sim.totalOps).toLocaleString(); | |
| document.getElementById('bitsValue').textContent = Math.floor(sim.totalBits).toLocaleString(); | |
| document.getElementById('waterState').textContent = code.state; | |
| // Bars | |
| document.getElementById('tempBar').style.width = Math.min(sim.temperature / 100 * 100, 100) + '%'; | |
| document.getElementById('powerBar').style.width = Math.min(sim.power / 100 * 100, 100) + '%'; | |
| document.getElementById('flowBar').style.width = Math.min(flowStep(sim.power, sim.temperature, 1.0) * 100, 100) + '%'; | |
| document.getElementById('effBar').style.width = (action.efficiency * 100) + '%'; | |
| document.getElementById('opsBar').style.width = Math.min(sim.totalOps / 1000000 * 100, 100) + '%'; | |
| document.getElementById('bitsBar').style.width = Math.min(sim.totalBits / 1000000000 * 100, 100) + '%'; | |
| // Colors | |
| const tempColor = sim.temperature > 70 ? 'var(--heat)' : sim.temperature > 55 ? '#ffaa00' : 'var(--heat)'; | |
| document.getElementById('tempValue').style.color = tempColor; | |
| // Status | |
| const statusDot = document.getElementById('statusDot'); | |
| const statusText = document.getElementById('statusText'); | |
| if (sim.temperature > 70) { | |
| statusDot.className = 'status-dot danger'; | |
| statusText.textContent = '⚠️ ร้อนเกิน — ประสิทธิภาพลด'; | |
| } else if (Math.abs(sim.temperature - 50) <= 5) { | |
| statusDot.className = 'status-dot active'; | |
| statusText.textContent = '✅ อุณหภูมิเหมาะสม'; | |
| } else { | |
| statusDot.className = 'status-dot warning'; | |
| statusText.textContent = '🔄 กำลังปรับอุณหภูมิ'; | |
| } | |
| // Time | |
| if (sim.startTime) { | |
| const elapsed = Math.floor((Date.now() - sim.startTime) / 1000); | |
| const min = Math.floor(elapsed / 60).toString().padStart(2, '0'); | |
| const sec = (elapsed % 60).toString().padStart(2, '0'); | |
| document.getElementById('timeDisplay').textContent = min + ':' + sec; | |
| } | |
| } | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // CHART DRAWING | |
| // ═══════════════════════════════════════════════════════════════════ | |
| function drawChart() { | |
| const canvas = document.getElementById('mainChart'); | |
| const ctx = canvas.getContext('2d'); | |
| canvas.width = canvas.offsetWidth; | |
| canvas.height = canvas.offsetHeight; | |
| const w = canvas.width; | |
| const h = canvas.height; | |
| const data = sim.history; | |
| if (data.length < 2) return; | |
| ctx.clearRect(0, 0, w, h); | |
| // Grid | |
| ctx.strokeStyle = 'rgba(0,212,255,0.1)'; | |
| ctx.lineWidth = 1; | |
| for (let i = 0; i < 5; i++) { | |
| const y = (h / 5) * i; | |
| ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke(); | |
| } | |
| // Optimal temp line | |
| const optY = h - (50 / 100) * h; | |
| ctx.strokeStyle = 'rgba(0,255,136,0.3)'; | |
| ctx.setLineDash([5, 5]); | |
| ctx.beginPath(); ctx.moveTo(0, optY); ctx.lineTo(w, optY); ctx.stroke(); | |
| ctx.setLineDash([]); | |
| ctx.fillStyle = 'rgba(0,255,136,0.5)'; | |
| ctx.font = '10px sans-serif'; | |
| ctx.fillText('Optimal 50°C', 5, optY - 3); | |
| // Temperature line | |
| ctx.strokeStyle = '#ff0044'; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| for (let i = 0; i < data.length; i++) { | |
| const x = (i / (data.length - 1)) * w; | |
| const y = h - (Math.min(data[i].temp, 100) / 100) * h; | |
| if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); | |
| } | |
| ctx.stroke(); | |
| // Efficiency line | |
| ctx.strokeStyle = '#ffaa00'; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| for (let i = 0; i < data.length; i++) { | |
| const x = (i / (data.length - 1)) * w; | |
| const y = h - data[i].eff * h; | |
| if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); | |
| } | |
| ctx.stroke(); | |
| // Legend | |
| ctx.fillStyle = '#ff0044'; | |
| ctx.fillRect(w - 120, 10, 12, 3); | |
| ctx.fillStyle = 'rgba(224,232,255,0.7)'; | |
| ctx.fillText('อุณหภูมิ', w - 104, 14); | |
| ctx.fillStyle = '#ffaa00'; | |
| ctx.fillRect(w - 120, 25, 12, 3); | |
| ctx.fillStyle = 'rgba(224,232,255,0.7)'; | |
| ctx.fillText('ประสิทธิภาพ', w - 104, 29); | |
| } | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // WATER VISUALIZATION | |
| // ═══════════════════════════════════════════════════════════════════ | |
| function updateWaterViz() { | |
| const level = Math.max(10, 100 - (sim.temperature - 25) * 1.5); | |
| document.getElementById('waterLevel').style.height = level + '%'; | |
| // Steam particles | |
| const container = document.getElementById('steamParticles'); | |
| if (sim.temperature > 40 && Math.random() < (sim.temperature - 40) / 100) { | |
| const steam = document.createElement('div'); | |
| steam.className = 'steam'; | |
| steam.style.left = Math.random() * 100 + '%'; | |
| steam.style.bottom = level + '%'; | |
| steam.style.animationDuration = (1 + Math.random() * 2) + 's'; | |
| container.appendChild(steam); | |
| setTimeout(() => steam.remove(), 3000); | |
| } | |
| } | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // LOGIC GATES | |
| // ═══════════════════════════════════════════════════════════════════ | |
| function updateLogicGates() { | |
| const flow = flowStep(sim.power, sim.temperature, 1.0); | |
| const threshold = 0.1; | |
| const a = flow > threshold ? 1 : 0; | |
| const b = (flow * 0.7) > threshold ? 1 : 0; | |
| // AND | |
| document.getElementById('andA').className = 'gate-input ' + (a ? 'on' : 'off'); | |
| document.getElementById('andA').textContent = a; | |
| document.getElementById('andB').className = 'gate-input ' + (b ? 'on' : 'off'); | |
| document.getElementById('andB').textContent = b; | |
| const andOut = (a && b) ? 1 : 0; | |
| document.getElementById('andOut').className = 'gate-output ' + (andOut ? 'on' : 'off'); | |
| document.getElementById('andOut').textContent = andOut; | |
| // OR | |
| document.getElementById('orA').className = 'gate-input ' + (a ? 'on' : 'off'); | |
| document.getElementById('orA').textContent = a; | |
| document.getElementById('orB').className = 'gate-input ' + (b ? 'on' : 'off'); | |
| document.getElementById('orB').textContent = b; | |
| const orOut = (a || b) ? 1 : 0; | |
| document.getElementById('orOut').className = 'gate-output ' + (orOut ? 'on' : 'off'); | |
| document.getElementById('orOut').textContent = orOut; | |
| // NOT | |
| document.getElementById('notA').className = 'gate-input ' + (a ? 'on' : 'off'); | |
| document.getElementById('notA').textContent = a; | |
| const notOut = a ? 0 : 1; | |
| document.getElementById('notOut').className = 'gate-output ' + (notOut ? 'on' : 'off'); | |
| document.getElementById('notOut').textContent = notOut; | |
| // XOR | |
| document.getElementById('xorA').className = 'gate-input ' + (a ? 'on' : 'off'); | |
| document.getElementById('xorA').textContent = a; | |
| document.getElementById('xorB').className = 'gate-input ' + (b ? 'on' : 'off'); | |
| document.getElementById('xorB').textContent = b; | |
| const xorOut = (a !== b) ? 1 : 0; | |
| document.getElementById('xorOut').className = 'gate-output ' + (xorOut ? 'on' : 'off'); | |
| document.getElementById('xorOut').textContent = xorOut; | |
| } | |
| // ═══════════════════════════════════════════════════════════════════ | |
| // CONTROLS | |
| // ═══════════════════════════════════════════════════════════════════ | |
| function startSimulation() { | |
| if (sim.running) return; | |
| sim.running = true; | |
| sim.round = 0; | |
| sim.power = parseFloat(document.getElementById('powerInput').value); | |
| sim.temperature = CONSTANTS.ROOM_TEMP; | |
| sim.totalOps = 0; | |
| sim.totalBits = 0; | |
| sim.history = []; | |
| sim.startTime = Date.now(); | |
| document.getElementById('startBtn').disabled = true; | |
| document.getElementById('stopBtn').disabled = false; | |
| simulationTick(); | |
| } | |
| function stopSimulation() { | |
| sim.running = false; | |
| if (sim.animFrame) cancelAnimationFrame(sim.animFrame); | |
| document.getElementById('startBtn').disabled = false; | |
| document.getElementById('stopBtn').disabled = true; | |
| document.getElementById('statusText').textContent = '⏹ หยุดแล้ว'; | |
| document.getElementById('statusDot').className = 'status-dot'; | |
| } | |
| // Init | |
| updateUI(); | |
| </script> | |
| </body> | |
| </html> | |
Xet Storage Details
- Size:
- 35.6 kB
- Xet hash:
- 5475d2c5b1abd0eb6ce889093a69ca7e176ce940ab1a2cdc04f5216159a7f65b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.