CoolFace
Apppublic

Kodak419/CircularMovement

sourceHugging Facemitupdated 9mo agoView on Hugging Face
0likes
index.html230 linesDownload Raw Back to root
1<!DOCTYPE html>2<html lang="he" dir="rtl">3<head>4    <meta charset="UTF-8">5    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">6    <title>מעבדה פיזיקלית: תנועה סיבובית חיכוך ככוח רדיאלי</title>7    <style>8        :root { --primary: #00d4ff; --bg: #121212; --panel: #1e1e1e; --accent: #ff0055; --safe: #00ff88; }9        body { 10            font-family: 'Segoe UI', system-ui, sans-serif; 11            margin: 0; background: var(--bg); color: white; 12            display: flex; flex-direction: column; height: 100vh; overflow: hidden;13            direction: rtl;14        }15        @media (min-width: 768px) { body { flex-direction: row; } }16        #controls { 17            background: var(--panel); padding: 15px; box-sizing: border-box;18            z-index: 10; box-shadow: -5px 0 15px rgba(0,0,0,0.5);19            overflow-y: auto; width: 100%; display: flex; flex-direction: column;20        }21        @media (min-width: 768px) { #controls { width: 380px; height: 100vh; } }22        #canvas-container { flex-grow: 1; position: relative; background: #222; overflow: hidden; display: flex; align-items: center; justify-content: center; }23        canvas#simCanvas { display: block; width: 100%; height: 100%; }24        .param-group { margin-bottom: 12px; border-bottom: 1px solid #333; padding-bottom: 8px; }25        label { display: block; font-size: 0.8rem; color: var(--primary); font-weight: bold; margin-bottom: 4px; }26        input[type="range"] { width: 100%; height: 8px; accent-color: var(--primary); cursor: pointer; }27        select { width: 100%; padding: 8px; background: #333; color: white; border: none; border-radius: 5px; font-size: 14px; }28        .stats-panel { background: rgba(0,0,0,0.4); padding: 10px; border-radius: 8px; margin: 10px 0; font-size: 0.85rem; border: 1px solid #444; }29        .stat-line { display: flex; justify-content: space-between; margin: 3px 0; }30        .stat-val { color: var(--primary); font-family: monospace; direction: ltr; }31        #graph-container { background: #000; height: 100px; border-radius: 5px; margin-top: 10px; border: 1px solid #444; }32        canvas#graphCanvas { width: 100%; height: 100%; }33        .status { padding: 10px; border-radius: 6px; text-align: center; font-weight: bold; margin-top: 10px; font-size: 0.9rem; }34        .stable { background: rgba(0, 255, 136, 0.1); color: var(--safe); border: 1px solid var(--safe); }35        .slipping { background: rgba(255, 0, 85, 0.1); color: var(--accent); border: 1px solid var(--accent); animation: blink 0.4s infinite; }36        @keyframes blink { 50% { opacity: 0.5; } }37        button { width: 100%; padding: 12px; background: var(--primary); border: none; color: #000; border-radius: 5px; font-weight: bold; cursor: pointer; margin-top: 5px; transition: 0.2s; }38        button:active { transform: scale(0.98); background: #fff; }39        .value-label { float: left; color: #aaa; font-size: 0.75rem; }40    </style>41</head>42<body>43 44<div id="controls">45    <h3 style="margin: 0 0 10px 0; color: var(--primary); text-align: center;">מערכת ייצוב אקטיביתמעבדה פיזיקלית: תנועה סיבובית חיכוך ככוח רדיאלי</h3>46    47    <div class="param-group">48        <label>דגם רכב</label>49        <select id="carType">50            <option value="sedan">🚗 רכב משפחתי</option>51            <option value="truck">🚛 משאית כבדה</option>52            <option value="race">🏎️ רכב מרוץ F1</option>53        </select>54    </div>55 56    <div class="param-group">57        <label>מהירות (v): <span class="value-label" id="vDisp">60</span> מ'/שנ'</label>58        <input type="range" id="vel" min="5" max="250" value="60">59    </div>60 61    <div class="param-group">62        <label>רדיוס (r): <span class="value-label" id="rDisp">150</span> מטר</label>63        <input type="range" id="rad" min="40" max="600" value="150">64    </div>65 66    <div class="param-group">67        <label>מסה (m): <span class="value-label" id="mDisp">1500</span> ק"ג</label>68        <input type="range" id="mass" min="500" max="15000" step="100" value="1500">69    </div>70 71    <div class="param-group">72        <label>חיכוך (μ): <span class="value-label" id="fDisp">0.7</span></label>73        <input type="range" id="fric" min="0.05" max="2.0" step="0.01" value="0.7">74    </div>75 76    <div class="stats-panel">77        <div class="stat-line"><span>מהירות זוויתית (ω):</span> <span class="stat-val" id="angVelVal">0 rad/s</span></div>78        <div class="stat-line"><span>מיקום (θ):</span> <span class="stat-val" id="angPosVal">0.00 rad</span></div>79        <div class="stat-line"><span>משקל (W):</span> <span class="stat-val" id="weightVal">0 N</span></div>80    </div>81 82    <div id="graph-container">83        <canvas id="graphCanvas"></canvas>84    </div>85 86    <button id="resetMode">ייצב והחזר לכביש (v & μ)</button>87    <div id="status" class="status stable">אחיזה תקינה</div>88</div>89 90<div id="canvas-container">91    <canvas id="simCanvas"></canvas>92</div>93 94<script>95    const simCanvas = document.getElementById('simCanvas');96    const simCtx = simCanvas.getContext('2d');97    const graphCanvas = document.getElementById('graphCanvas');98    const graphCtx = graphCanvas.getContext('2d');99    const container = document.getElementById('canvas-container');100 101    const inputs = {102        vel: document.getElementById('vel'),103        rad: document.getElementById('rad'),104        mass: document.getElementById('mass'),105        fric: document.getElementById('fric'),106        type: document.getElementById('carType')107    };108 109    let carAngle = 0;110    let driftOffset = 0;111    let forceHistory = [];112    const G = 9.8;113 114    function resize() {115        simCanvas.width = container.clientWidth;116        simCanvas.height = container.clientHeight;117        graphCanvas.width = graphCanvas.offsetWidth;118        graphCanvas.height = graphCanvas.offsetHeight;119    }120    window.addEventListener('resize', resize);121    resize();122 123    // לוגיקת הכפתור החדשה: משנה גם מהירות וגם חיכוך124    document.getElementById('resetMode').onclick = () => {125        const r = parseFloat(inputs.rad.value);126        127        // 1. קביעת חיכוך גבוה ובטוח128        const targetMu = 0.85;129        inputs.fric.value = targetMu;130 131        // 2. חישוב מהירות מקסימלית מותרת: v = sqrt(mu * g * r)132        // אנחנו לוקחים 90% מהמהירות המקסימלית לביטחון133        const maxSafeV = Math.sqrt(targetMu * G * r) * 0.9;134        135        // 3. עדכון הסליידר של המהירות136        inputs.vel.value = Math.min(maxSafeV, 250).toFixed(0);137 138        // 4. איפוס פיזיקלי של המיקום139        driftOffset = 0;140    };141 142    function drawCar(type, x, y, angle) {143        simCtx.save();144        simCtx.translate(x, y);145        simCtx.rotate(angle); 146        if (type === 'sedan') {147            simCtx.fillStyle = "#00d4ff"; simCtx.beginPath(); simCtx.roundRect(-8, -14, 16, 28, 4); simCtx.fill();148            simCtx.fillStyle = "rgba(255,255,255,0.4)"; simCtx.fillRect(-6, 5, 12, 5); 149        } else if (type === 'truck') {150            simCtx.fillStyle = "#eee"; simCtx.fillRect(-10, -25, 20, 35);151            simCtx.fillStyle = "#f39c12"; simCtx.fillRect(-11, 10, 22, 12);152        } else if (type === 'race') {153            simCtx.fillStyle = "#ff0055"; simCtx.beginPath(); simCtx.moveTo(0, 18); simCtx.lineTo(-12, -8); simCtx.lineTo(12, -8); simCtx.fill();154            simCtx.fillStyle = "#000"; simCtx.fillRect(-14, -10, 28, 4);155        }156        simCtx.restore();157    }158 159    function updateGraph(req, avail) {160        forceHistory.push({req, avail});161        if (forceHistory.length > 100) forceHistory.shift();162        graphCtx.clearRect(0, 0, graphCanvas.width, graphCanvas.height);163        const maxV = Math.max(...forceHistory.map(d => Math.max(d.req, d.avail)), 1000);164        const sY = graphCanvas.height / (maxV * 1.2);165        const sX = graphCanvas.width / 100;166        graphCtx.strokeStyle = '#00ff88'; graphCtx.beginPath();167        forceHistory.forEach((d, i) => { const x = i*sX; const y = graphCanvas.height - d.avail*sY; i===0?graphCtx.moveTo(x,y):graphCtx.lineTo(x,y); });168        graphCtx.stroke();169        graphCtx.strokeStyle = '#ff0055'; graphCtx.beginPath();170        forceHistory.forEach((d, i) => { const x = i*sX; const y = graphCanvas.height - d.req*sY; i===0?graphCtx.moveTo(x,y):graphCtx.lineTo(x,y); });171        graphCtx.stroke();172    }173 174    function draw() {175        simCtx.clearRect(0, 0, simCanvas.width, simCanvas.height);176        const cx = simCanvas.width / 2;177        const cy = simCanvas.height / 2;178        179        const v = parseFloat(inputs.vel.value);180        const r = parseFloat(inputs.rad.value);181        const m = parseFloat(inputs.mass.value);182        const mu = parseFloat(inputs.fric.value);183 184        const weight = m * G;185        const centripetalReq = (m * v * v) / r;186        const frictionAvail = mu * weight;187        const angVel = v / r;188 189        document.getElementById('vDisp').innerText = v;190        document.getElementById('rDisp').innerText = r;191        document.getElementById('mDisp').innerText = m;192        document.getElementById('fDisp').innerText = mu.toFixed(2);193        document.getElementById('weightVal').innerText = Math.round(weight).toLocaleString() + " N";194        document.getElementById('angVelVal').innerText = angVel.toFixed(3);195        const displayRad = (carAngle % (Math.PI * 2) + (Math.PI * 2)) % (Math.PI * 2);196        document.getElementById('angPosVal').innerText = displayRad.toFixed(2) + " rad";197 198        updateGraph(centripetalReq, frictionAvail);199 200        const isSlipping = centripetalReq > frictionAvail;201        const scale = Math.min(simCanvas.width, simCanvas.height) * 0.42;202        const visualR = (r / 600) * scale;203 204        simCtx.beginPath();205        simCtx.arc(cx, cy, visualR, 0, Math.PI * 2);206        simCtx.strokeStyle = '#333'; simCtx.lineWidth = 50; simCtx.stroke();207        simCtx.strokeStyle = '#f1c40f'; simCtx.lineWidth = 2; simCtx.setLineDash([20, 20]); simCtx.stroke();208        simCtx.setLineDash([]);209 210        if (isSlipping) {211            document.getElementById('status').innerText = "אזהרה: איבוד אחיזה!";212            document.getElementById('status').className = "status slipping";213            driftOffset += (centripetalReq / frictionAvail) * 0.6;214        } else {215            document.getElementById('status').innerText = "אחיזה תקינה";216            document.getElementById('status').className = "status stable";217            driftOffset *= 0.9;218        }219 220        carAngle += angVel * 0.035; 221        const carX = cx + (visualR + driftOffset) * Math.cos(carAngle);222        const carY = cy + (visualR + driftOffset) * Math.sin(carAngle);223        drawCar(inputs.type.value, carX, carY, carAngle + Math.PI/2);224 225        requestAnimationFrame(draw);226    }227    draw();228</script>229</body>230</html>