Ramesh-vani commited on
Commit
4e6dd2f
·
verified ·
1 Parent(s): 333e97a

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +253 -552
index.html CHANGED
@@ -1,562 +1,263 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <meta charset="UTF-8">
5
- <title>Physics Simulator for IIT Aspirants</title>
6
- <style>
7
- body { margin: 0; overflow: hidden; font-family: Arial, sans-serif; }
8
- canvas { display: block; width: 100%; height: 100vh; }
9
-
10
- /* Top Navbar for Controls */
11
- #navbar {
12
- position: fixed;
13
- top: 0;
14
- left: 0;
15
- width: 100%;
16
- background: rgba(255, 255, 255, 0.95);
17
- padding: 10px 20px;
18
- z-index: 1000;
19
- display: flex;
20
- justify-content: space-between;
21
- align-items: center;
22
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
23
- }
24
- #controls {
25
- display: flex;
26
- align-items: center;
27
- gap: 15px;
28
- flex-wrap: wrap;
29
- max-width: 70%;
30
- }
31
- #controls label { margin: 0 5px 0 0; font-size: 14px; }
32
- #controls select, #controls button {
33
- padding: 6px;
34
- font-size: 14px;
35
- border-radius: 4px;
36
- border: 1px solid #ccc;
37
- }
38
- #controls select { min-width: 150px; }
39
- #buttonContainer { display: flex; gap: 10px; }
40
-
41
- /* Suboptions Sidebar (Left) */
42
- #subOptionsSidebar {
43
- position: fixed;
44
- top: 60px;
45
- left: 0;
46
- width: 250px;
47
- height: calc(100vh - 60px);
48
- background: rgba(245, 245, 245, 0.95);
49
- padding: 15px;
50
- z-index: 900;
51
- overflow-y: auto;
52
- box-shadow: 2px 0 5px rgba(0,0,0,0.1);
53
- transition: transform 0.3s ease;
54
- }
55
- #subOptionsSidebar.hidden { transform: translateX(-100%); }
56
- #subOptionsContainer label { display: block; margin: 10px 0 5px; font-size: 14px; }
57
- #subOptionsContainer input, #subOptionsContainer select {
58
- width: 100%; padding: 6px; font-size: 14px; border-radius: 4px; border: 1px solid #ccc; }
59
-
60
- /* Right Sidebar for Edit and Connection Panels */
61
- #rightSidebar {
62
- position: fixed;
63
- top: 60px;
64
- right: 0;
65
- width: 300px;
66
- height: calc(100vh - 60px);
67
- background: rgba(245, 245, 245, 0.95);
68
- padding: 15px;
69
- z-index: 900;
70
- overflow-y: auto;
71
- box-shadow: -2px 0 5px rgba(0,0,0,0.1);
72
- transition: transform 0.3s ease;
73
- }
74
- #rightSidebar.hidden { transform: translateX(100%); }
75
- #editPanel, #connectionPanel { margin-bottom: 20px; }
76
- #editPanel h3, #connectionPanel h3 { margin: 0 0 10px; font-size: 16px; }
77
- #editPanel label, #connectionPanel label { display: block; margin: 10px 0 5px; font-size: 14px; }
78
- #editPanel input, #editPanel select, #editPanel button,
79
- #connectionPanel input, #connectionPanel select, #connectionPanel button {
80
- width: 100%; padding: 6px; font-size: 14px; border-radius: 4px; border: 1px solid #ccc; }
81
- #editPanel button, #connectionPanel button { margin-top: 10px; }
82
-
83
- /* Bottom Bar for Constraint Edit */
84
- #bottomBar {
85
- position: fixed;
86
- bottom: 0;
87
- left: 0;
88
- width: 100%;
89
- background: rgba(255, 255, 255, 0.95);
90
- padding: 10px 20px;
91
- z-index: 1000;
92
- display: flex;
93
- justify-content: flex-end;
94
- align-items: center;
95
- box-shadow: 0 -2px 5px rgba(0,0,0,0.1);
96
- }
97
- #constraintEditPanel { flex: 0 0 300px; display: none; }
98
- #constraintEditPanel h3 { margin: 0 0 10px; font-size: 16px; }
99
- #constraintEditPanel label { display: block; margin: 10px 0 5px; font-size: 14px; }
100
- #constraintEditPanel input, #constraintEditPanel select, #constraintEditPanel button {
101
- width: 100%; padding: 6px; font-size: 14px; border-radius: 4px; border: 1px solid #ccc; }
102
- #constraintEditPanel button { margin-top: 10px; }
103
- </style>
104
  </head>
105
  <body>
106
- <!-- Top Navbar -->
107
- <div id="navbar">
108
- <div id="controls">
109
- <label for="simulationSelect">Select Simulation:</label>
110
- <select id="simulationSelect">
111
- <option value="bouncingBall">Bouncing Ball</option>
112
- <option value="pendulum">Pendulum</option>
113
- <option value="projectile">Projectile Motion</option>
114
- <option value="inclinedPlane">Inclined Plane</option>
115
- <option value="springMass">Spring-Mass System</option>
116
- </select>
117
- <div id="buttonContainer">
118
- <button id="addElement">Add Element</button>
119
- <button id="reset">Reset</button>
120
- </div>
121
- </div>
122
- </div>
123
-
124
- <!-- Left Sidebar for Suboptions -->
125
- <div id="subOptionsSidebar">
126
- <div id="subOptionsContainer"></div>
127
- </div>
128
-
129
- <!-- Right Sidebar for Edit and Connection Panels -->
130
- <div id="rightSidebar" class="hidden">
131
- <div id="editPanel"></div>
132
- <div id="connectionPanel">
133
- <h3>Connect Elements</h3>
134
- <label for="connType">Connection Type:</label>
135
- <select id="connType">
136
- <option value="string">String (Inextensible)</option>
137
- <option value="spring">Spring (Elastic)</option>
138
- <option value="stick">Rigid Stick</option>
139
- </select>
140
- <div id="sourceEndpointDiv">
141
- <label for="sourceEndpoint">Source Endpoint:</label>
142
- <select id="sourceEndpoint">
143
- <option value="mass">Mass</option>
144
- <option value="fixed">Fixed Point</option>
145
- </select>
146
- </div>
147
- <button id="cancelConn">Cancel</button>
148
- <p style="font-size: 12px; color: #555;">Click the target element to connect.</p>
149
  </div>
150
- </div>
151
-
152
- <!-- Bottom Bar for Constraints -->
153
- <div id="bottomBar">
154
- <div id="constraintEditPanel">
155
- <h3>Edit Constraint</h3>
156
- <label for="consA_X">Endpoint A X:</label>
157
- <input type="number" id="consA_X" step="0.1">
158
- <label for="consA_Y">Endpoint A Y:</label>
159
- <input type="number" id="consA_Y" step="0.1">
160
- <label for="consB_X">Endpoint B X:</label>
161
- <input type="number" id="consB_X" step="0.1">
162
- <label for="consB_Y">Endpoint B Y:</label>
163
- <input type="number" id="consB_Y" step="0.1">
164
- <label for="consType">Connection Type:</label>
165
- <select id="consType">
166
- <option value="string">String</option>
167
- <option value="spring">Spring</option>
168
- <option value="stick">Stick</option>
169
- </select>
170
- <button id="updateConstraint">Update</button>
171
- <button id="deleteConstraint">Delete</button>
172
  </div>
173
- </div>
174
-
175
- <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
176
- <script>
177
- const { Engine, Render, Runner, World, Bodies, Body, Constraint, Mouse, MouseConstraint, Events, Vector } = Matter;
178
- const engine = Engine.create();
179
- const world = engine.world;
180
- engine.gravity.scale = 0.001;
181
- const render = Render.create({
182
- element: document.body,
183
- engine: engine,
184
- options: {
185
- width: window.innerWidth,
186
- height: window.innerHeight,
187
- wireframes: false,
188
- background: '#e9ecef'
189
- }
190
- });
191
- Render.run(render);
192
- const runner = Runner.create();
193
- Runner.run(runner, engine);
194
-
195
- let floor, wallLeft, wallRight;
196
- function updateBoundaries() {
197
- if (floor && wallLeft && wallRight) World.remove(world, [floor, wallLeft, wallRight]);
198
- floor = Bodies.rectangle(window.innerWidth / 2, window.innerHeight + 50, window.innerWidth * 2, 100, { isStatic: true, render: { fillStyle: '#343a40' } });
199
- wallLeft = Bodies.rectangle(-50, window.innerHeight / 2, 100, window.innerHeight * 2, { isStatic: true, render: { fillStyle: '#343a40' } });
200
- wallRight = Bodies.rectangle(window.innerWidth + 50, window.innerHeight / 2, 100, window.innerHeight * 2, { isStatic: true, render: { fillStyle: '#343a40' } });
201
- World.add(world, [floor, wallLeft, wallRight]);
202
- }
203
- updateBoundaries();
204
-
205
- const mouse = Mouse.create(render.canvas);
206
- const mouseConstraint = MouseConstraint.create(engine, { mouse: mouse, constraint: { stiffness: 0.2, render: { visible: false } } });
207
- World.add(world, mouseConstraint);
208
- render.mouse = mouse;
209
-
210
- let selectedBody = null, selectedConstraint = null, attachMode = false, attachFrom = null;
211
- const subOptionsSidebar = document.getElementById('subOptionsSidebar'),
212
- rightSidebar = document.getElementById('rightSidebar'),
213
- editPanel = document.getElementById('editPanel'),
214
- connectionPanel = document.getElementById('connectionPanel'),
215
- constraintEditPanel = document.getElementById('constraintEditPanel');
216
-
217
- function getRandomColor() { return '#' + Math.floor(Math.random() * 16777215).toString(16); }
218
-
219
- function updateSubOptions() {
220
- const simulationSelect = document.getElementById('simulationSelect');
221
- const subOptionsContainer = document.getElementById('subOptionsContainer');
222
- const selected = simulationSelect.value;
223
- let html = '';
224
- if (selected === 'bouncingBall') {
225
- html = `
226
- <label for="ballInitialX">Initial X (m):</label><input type="number" id="ballInitialX" value="200">
227
- <label for="ballInitialY">Initial Y (m):</label><input type="number" id="ballInitialY" value="50">
228
- <label for="ballRadius">Radius (m):</label><input type="number" id="ballRadius" value="20" min="5">
229
- <label for="ballRestitution">Restitution:</label><input type="number" id="ballRestitution" value="0.9" step="0.1" min="0" max="1">
230
- <label for="ballFriction">Friction:</label><input type="number" id="ballFriction" value="0.01" step="0.01" min="0" max="1">
231
- <label for="ballColor">Color:</label><input type="color" id="ballColor" value="#3498db">
232
- `;
233
- } else if (selected === 'pendulum') {
234
- html = `
235
- <label for="pendulumPivotX">Pivot X (m):</label><input type="number" id="pendulumPivotX" value="${Math.floor(window.innerWidth / 2)}">
236
- <label for="pendulumPivotY">Pivot Y (m):</label><input type="number" id="pendulumPivotY" value="50">
237
- <label for="pendulumBobRadius">Bob Radius (m):</label><input type="number" id="pendulumBobRadius" value="30" min="5">
238
- <label for="pendulumLength">Length (m):</label><input type="number" id="pendulumLength" value="300" min="50">
239
- <label for="pendulumStiffness">Stiffness:</label><input type="number" id="pendulumStiffness" value="1" step="0.1" min="0" max="1">
240
- <label for="pendulumBobColor">Color:</label><input type="color" id="pendulumBobColor" value="#e74c3c">
241
- `;
242
- } else if (selected === 'projectile') {
243
- html = `
244
- <label for="projectileInitialX">Initial X (m):</label><input type="number" id="projectileInitialX" value="100">
245
- <label for="projectileInitialY">Initial Y (m):</label><input type="number" id="projectileInitialY" value="${window.innerHeight - 150}">
246
- <label for="projectileRadius">Radius (m):</label><input type="number" id="projectileRadius" value="15" min="5">
247
- <label for="projectileVelX">Velocity X (m/s):</label><input type="number" id="projectileVelX" value="20">
248
- <label for="projectileVelY">Velocity Y (m/s):</label><input type="number" id="projectileVelY" value="-25">
249
- <label for="projectileColor">Color:</label><input type="color" id="projectileColor" value="#f1c40f">
250
- `;
251
- } else if (selected === 'inclinedPlane') {
252
- html = `
253
- <label for="rampWidth">Ramp Width (m):</label><input type="number" id="rampWidth" value="400" min="100">
254
- <label for="rampHeight">Ramp Height (m):</label><input type="number" id="rampHeight" value="20" min="10">
255
- <label for="rampAngle">Angle (°):</label><input type="number" id="rampAngle" value="30" min="0" max="90">
256
- <label for="rampX">Ramp X (m):</label><input type="number" id="rampX" value="400">
257
- <label for="rampY">Ramp Y (m):</label><input type="number" id="rampY" value="${window.innerHeight - 100}">
258
- <label for="rampColor">Ramp Color:</label><input type="color" id="rampColor" value="#8e44ad">
259
- <label for="blockWidth">Block Width (m):</label><input type="number" id="blockWidth" value="50" min="10">
260
- <label for="blockHeight">Block Height (m):</label><input type="number" id="blockHeight" value="50" min="10">
261
- <label for="blockFriction">Block Friction:</label><input type="number" id="blockFriction" value="0.3" step="0.1" min="0" max="1">
262
- <label for="blockColor">Block Color:</label><input type="color" id="blockColor" value="#2ecc71">
263
- `;
264
- } else if (selected === 'springMass') {
265
- html = `
266
- <label for="springMassRadius">Mass Radius (m):</label><input type="number" id="springMassRadius" value="25" min="5">
267
- <label for="springMassRestitution">Restitution:</label><input type="number" id="springMassRestitution" value="0.5" step="0.1" min="0" max="1">
268
- <label for="fixedPointX">Fixed X (m):</label><input type="number" id="fixedPointX" value="${Math.floor(window.innerWidth / 2)}">
269
- <label for="fixedPointY">Fixed Y (m):</label><input type="number" id="fixedPointY" value="100">
270
- <label for="springLength">Spring Length (m):</label><input type="number" id="springLength" value="200" min="50">
271
- <label for="springStiffness">Stiffness:</label><input type="number" id="springStiffness" value="0.05" step="0.01" min="0" max="1">
272
- <label for="springMassColor">Color:</label><input type="color" id="springMassColor" value="#e67e22">
273
- `;
274
- }
275
- subOptionsContainer.innerHTML = html;
276
- subOptionsSidebar.classList.remove('hidden');
277
- }
278
- updateSubOptions();
279
- document.getElementById('simulationSelect').addEventListener('change', updateSubOptions);
280
-
281
- function addBouncingBall() {
282
- const x = parseFloat(document.getElementById("ballInitialX").value);
283
- const y = parseFloat(document.getElementById("ballInitialY").value);
284
- const radius = parseFloat(document.getElementById("ballRadius").value);
285
- const restitution = parseFloat(document.getElementById("ballRestitution").value);
286
- const friction = parseFloat(document.getElementById("ballFriction").value);
287
- const color = document.getElementById("ballColor").value;
288
- const ball = Bodies.circle(x, y, radius, { restitution, friction, render: { fillStyle: color }, density: 0.001 });
289
- ball.elementType = "bouncingBall";
290
- ball.customOptions = { x, y, radius, restitution, friction, color };
291
- World.add(world, ball);
292
- }
293
-
294
- function addPendulum() {
295
- const pivotX = parseFloat(document.getElementById("pendulumPivotX").value);
296
- const pivotY = parseFloat(document.getElementById("pendulumPivotY").value);
297
- const bobRadius = parseFloat(document.getElementById("pendulumBobRadius").value);
298
- const length = parseFloat(document.getElementById("pendulumLength").value);
299
- const stiffness = parseFloat(document.getElementById("pendulumStiffness").value);
300
- const color = document.getElementById("pendulumBobColor").value;
301
- const bob = Bodies.circle(pivotX, pivotY + length, bobRadius, { restitution: 0.9, density: 0.001, render: { fillStyle: color } });
302
- const constraint = Constraint.create({
303
- pointA: { x: pivotX, y: pivotY },
304
- bodyB: bob,
305
- length,
306
- stiffness,
307
- render: { strokeStyle: '#333', lineWidth: 2 }
308
- });
309
- bob.elementType = "pendulum";
310
- bob.customOptions = { pivotX, pivotY, bobRadius, length, stiffness, color };
311
- World.add(world, [bob, constraint]);
312
- }
313
-
314
- function addProjectile() {
315
- const x = parseFloat(document.getElementById("projectileInitialX").value);
316
- const y = parseFloat(document.getElementById("projectileInitialY").value);
317
- const radius = parseFloat(document.getElementById("projectileRadius").value);
318
- const velX = parseFloat(document.getElementById("projectileVelX").value);
319
- const velY = parseFloat(document.getElementById("projectileVelY").value);
320
- const color = document.getElementById("projectileColor").value;
321
- const proj = Bodies.circle(x, y, radius, { restitution: 0.8, frictionAir: 0.005, render: { fillStyle: color }, density: 0.001 });
322
- Body.setVelocity(proj, { x: velX, y: velY });
323
- proj.elementType = "projectile";
324
- proj.customOptions = { x, y, radius, velX, velY, color };
325
- World.add(world, proj);
326
- }
327
-
328
- function addInclinedPlane() {
329
- const rampWidth = parseFloat(document.getElementById("rampWidth").value);
330
- const rampHeight = parseFloat(document.getElementById("rampHeight").value);
331
- const angleDeg = parseFloat(document.getElementById("rampAngle").value);
332
- const angle = angleDeg * Math.PI / 180;
333
- const rampX = parseFloat(document.getElementById("rampX").value);
334
- const rampY = parseFloat(document.getElementById("rampY").value);
335
- const rampColor = document.getElementById("rampColor").value;
336
- const ramp = Bodies.rectangle(rampX, rampY, rampWidth, rampHeight, {
337
- isStatic: true,
338
- angle,
339
- friction: 0.1,
340
- render: { fillStyle: rampColor }
341
- });
342
- const blockWidth = parseFloat(document.getElementById("blockWidth").value);
343
- const blockHeight = parseFloat(document.getElementById("blockHeight").value);
344
- const blockFriction = parseFloat(document.getElementById("blockFriction").value);
345
- const blockColor = document.getElementById("blockColor").value;
346
- const blockX = rampX - rampWidth / 4 * Math.cos(angle);
347
- const blockY = rampY - rampHeight / 2 - blockHeight / 2 - 10;
348
- const block = Bodies.rectangle(blockX, blockY, blockWidth, blockHeight, {
349
- friction: blockFriction,
350
- render: { fillStyle: blockColor },
351
- density: 0.001
352
- });
353
- ramp.elementType = "inclinedPlane_ramp";
354
- ramp.customOptions = { rampWidth, rampHeight, angleDeg, rampX, rampY, rampColor };
355
- block.elementType = "inclinedPlane_block";
356
- block.customOptions = { blockWidth, blockHeight, blockFriction, blockColor };
357
- World.add(world, [ramp, block]);
358
- }
359
-
360
- function addSpringMass() {
361
- const radius = parseFloat(document.getElementById("springMassRadius").value);
362
- const restitution = parseFloat(document.getElementById("springMassRestitution").value);
363
- const fixedX = parseFloat(document.getElementById("fixedPointX").value);
364
- const fixedY = parseFloat(document.getElementById("fixedPointY").value);
365
- const length = parseFloat(document.getElementById("springLength").value);
366
- const stiffness = parseFloat(document.getElementById("springStiffness").value);
367
- const color = document.getElementById("springMassColor").value;
368
- const mass = Bodies.circle(fixedX, fixedY + length, radius, { restitution, density: 0.001, render: { fillStyle: color } });
369
- const spring = Constraint.create({
370
- pointA: { x: fixedX, y: fixedY },
371
- bodyB: mass,
372
- length,
373
- stiffness,
374
- damping: 0.05,
375
- render: { strokeStyle: '#333', lineWidth: 2 }
376
- });
377
- mass.elementType = "springMass";
378
- mass.customOptions = { radius, restitution, fixedX, fixedY, length, stiffness, color };
379
- mass.fixedPoint = { x: fixedX, y: fixedY };
380
- World.add(world, [mass, spring]);
381
- }
382
-
383
- document.getElementById('addElement').addEventListener('click', () => {
384
- const selected = document.getElementById('simulationSelect').value;
385
- if (selected === 'bouncingBall') addBouncingBall();
386
- else if (selected === 'pendulum') addPendulum();
387
- else if (selected === 'projectile') addProjectile();
388
- else if (selected === 'inclinedPlane') addInclinedPlane();
389
- else if (selected === 'springMass') addSpringMass();
390
- });
391
-
392
- document.getElementById('reset').addEventListener('click', () => {
393
- World.clear(world);
394
- Engine.clear(engine);
395
- updateBoundaries();
396
- const newMouse = Mouse.create(render.canvas);
397
- const newMouseConstraint = MouseConstraint.create(engine, { mouse: newMouse, constraint: { stiffness: 0.2, render: { visible: false } } });
398
- World.add(world, newMouseConstraint);
399
- render.mouse = newMouse;
400
- hideEditPanel();
401
- hideConnectionPanel();
402
- hideConstraintEditPanel();
403
- });
404
-
405
- function openEditPanel(body) {
406
- selectedBody = body;
407
- rightSidebar.classList.remove('hidden');
408
- let html = `<h3>Edit Element</h3>
409
- <label for="editPosX">Position X (m):</label><input type="number" id="editPosX" value="${body.position.x.toFixed(1)}" step="0.1">
410
- <label for="editPosY">Position Y (m):</label><input type="number" id="editPosY" value="${body.position.y.toFixed(1)}" step="0.1">
411
- <label for="editAngle">Angle (rad):</label><input type="number" id="editAngle" value="${body.angle.toFixed(2)}" step="0.01">
412
- <label for="editColor">Color:</label><input type="color" id="editColor" value="${body.render.fillStyle}">`;
413
- if (body.elementType === "bouncingBall") {
414
- const opts = body.customOptions;
415
- html += `<label for="editBallRadius">Radius (m):</label><input type="number" id="editBallRadius" value="${opts.radius}" min="5">
416
- <label for="editBallRestitution">Restitution:</label><input type="number" id="editBallRestitution" value="${opts.restitution}" step="0.1" min="0" max="1">
417
- <label for="editBallFriction">Friction:</label><input type="number" id="editBallFriction" value="${opts.friction}" step="0.01" min="0" max="1">`;
418
- } else if (body.elementType === "pendulum") {
419
- const opts = body.customOptions;
420
- html += `<label for="editPendulumPivotX">Pivot X (m):</label><input type="number" id="editPendulumPivotX" value="${opts.pivotX}">
421
- <label for="editPendulumPivotY">Pivot Y (m):</label><input type="number" id="editPendulumPivotY" value="${opts.pivotY}">
422
- <label for="editPendulumBobRadius">Radius (m):</label><input type="number" id="editPendulumBobRadius" value="${opts.bobRadius}" min="5">
423
- <label for="editPendulumLength">Length (m):</label><input type="number" id="editPendulumLength" value="${opts.length}" min="50">
424
- <label for="editPendulumStiffness">Stiffness:</label><input type="number" id="editPendulumStiffness" value="${opts.stiffness}" step="0.1" min="0" max="1">`;
425
- }
426
- html += `<div style="margin-top:15px;">
427
- <button id="updateElement">Update</button>
428
- <button id="deleteElement">Delete</button>
429
- <button id="connectElement">Connect</button>
430
- </div>`;
431
- editPanel.innerHTML = html;
432
- editPanel.style.display = "block";
433
- connectionPanel.style.display = "none";
434
- document.getElementById("updateElement").addEventListener("click", updateElementFromEditPanel);
435
- document.getElementById("deleteElement").addEventListener("click", () => { World.remove(world, selectedBody); hideEditPanel(); });
436
- document.getElementById("connectElement").addEventListener("click", () => { attachMode = true; attachFrom = body; editPanel.style.display = "none"; connectionPanel.style.display = "block"; });
437
- }
438
-
439
- function hideEditPanel() { editPanel.style.display = "none"; connectionPanel.style.display = "none"; rightSidebar.classList.add('hidden'); selectedBody = null; }
440
- function hideConnectionPanel() { connectionPanel.style.display = "none"; attachMode = false; attachFrom = null; if (!selectedBody) rightSidebar.classList.add('hidden'); }
441
- function hideConstraintEditPanel() { constraintEditPanel.style.display = "none"; selectedConstraint = null; }
442
-
443
- function updateElementFromEditPanel() {
444
- if (!selectedBody) return;
445
- const newX = parseFloat(document.getElementById("editPosX").value);
446
- const newY = parseFloat(document.getElementById("editPosY").value);
447
- const newAngle = parseFloat(document.getElementById("editAngle").value);
448
- const newColor = document.getElementById("editColor").value;
449
- Body.setPosition(selectedBody, { x: newX, y: newY });
450
- Body.setAngle(selectedBody, newAngle);
451
- selectedBody.render.fillStyle = newColor;
452
- if (selectedBody.elementType === "bouncingBall") {
453
- const newRadius = parseFloat(document.getElementById("editBallRadius").value);
454
- const newRestitution = parseFloat(document.getElementById("editBallRestitution").value);
455
- const newFriction = parseFloat(document.getElementById("editBallFriction").value);
456
- Body.scale(selectedBody, newRadius / selectedBody.circleRadius, newRadius / selectedBody.circleRadius);
457
- selectedBody.restitution = newRestitution;
458
- selectedBody.friction = newFriction;
459
- selectedBody.customOptions = { ...selectedBody.customOptions, x: newX, y: newY, radius: newRadius, restitution: newRestitution, friction: newFriction, color: newColor };
460
- }
461
- hideEditPanel();
462
- }
463
-
464
- render.canvas.addEventListener("dblclick", (event) => {
465
- const rect = render.canvas.getBoundingClientRect();
466
- const mousePos = { x: event.clientX - rect.left, y: event.clientY - rect.top };
467
- const bodies = Matter.Composite.allBodies(world);
468
- const clicked = Matter.Query.point(bodies, mousePos);
469
- if (clicked.length > 0 && !clicked[0].isStatic) openEditPanel(clicked[0]);
470
- else hideEditPanel();
471
- });
472
-
473
- document.getElementById('cancelConn').addEventListener('click', hideConnectionPanel);
474
-
475
- Events.on(mouseConstraint, 'mouseup', (event) => {
476
- const mousePos = event.mouse.position;
477
- const bodies = Matter.Composite.allBodies(world);
478
- const clickedBodies = Matter.Query.point(bodies, mousePos);
479
- if (attachMode && clickedBodies.length > 0 && attachFrom && clickedBodies[0] !== attachFrom) {
480
- const connType = document.getElementById('connType').value;
481
- const sourceEndpoint = attachFrom.elementType === "springMass" ? document.getElementById('sourceEndpoint').value : "mass";
482
- const targetBody = clickedBodies[0];
483
- let pointA = sourceEndpoint === "fixed" ? attachFrom.fixedPoint : attachFrom.position;
484
- let pointB = targetBody.elementType === "springMass" && confirm("Connect to fixed point?") ? targetBody.fixedPoint : targetBody.position;
485
- const length = Math.hypot(pointB.x - pointA.x, pointB.y - pointA.y);
486
- const stiffness = connType === "spring" ? 0.05 : (connType === "stick" ? 1 : 0.9);
487
- const constraint = Constraint.create({
488
- bodyA: sourceEndpoint === "mass" ? attachFrom : null,
489
- pointA: sourceEndpoint === "fixed" ? pointA : { x: 0, y: 0 },
490
- bodyB: targetBody.elementType === "springMass" && pointB === targetBody.fixedPoint ? null : targetBody,
491
- pointB: targetBody.elementType === "springMass" && pointB === targetBody.fixedPoint ? pointB : { x: 0, y: 0 },
492
- length,
493
- stiffness,
494
- render: { strokeStyle: '#333', lineWidth: 2 }
495
  });
496
- World.add(world, constraint);
497
- hideConnectionPanel();
498
- } else if (!clickedBodies.length) {
499
- const constraints = Matter.Composite.allConstraints(world);
500
- for (let cons of constraints) {
501
- let posA = cons.bodyA ? Vector.add(cons.bodyA.position, cons.pointA) : cons.pointA;
502
- let posB = cons.bodyB ? Vector.add(cons.bodyB.position, cons.pointB) : cons.pointB;
503
- if (distanceToSegment(mousePos, posA, posB) < 5) {
504
- showConstraintEditPanel(cons);
505
- return;
506
- }
 
 
507
  }
508
- hideConstraintEditPanel();
509
- }
510
- });
511
-
512
- function distanceToSegment(p, v, w) {
513
- const l2 = Vector.magnitudeSquared(Vector.sub(w, v));
514
- if (l2 === 0) return Math.hypot(p.x - v.x, p.y - v.y);
515
- let t = Math.max(0, Math.min(1, ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2));
516
- const proj = Vector.add(v, Vector.mult(Vector.sub(w, v), t));
517
- return Math.hypot(p.x - proj.x, p.y - proj.y);
518
- }
519
-
520
- function showConstraintEditPanel(cons) {
521
- selectedConstraint = cons;
522
- let posA = cons.bodyA ? Vector.add(cons.bodyA.position, cons.pointA) : cons.pointA;
523
- let posB = cons.bodyB ? Vector.add(cons.bodyB.position, cons.pointB) : cons.pointB;
524
- document.getElementById('consA_X').value = posA.x.toFixed(1);
525
- document.getElementById('consA_Y').value = posA.y.toFixed(1);
526
- document.getElementById('consB_X').value = posB.x.toFixed(1);
527
- document.getElementById('consB_Y').value = posB.y.toFixed(1);
528
- document.getElementById('consType').value = cons.stiffness < 0.1 ? "spring" : (cons.stiffness > 0.95 ? "stick" : "string");
529
- constraintEditPanel.style.display = "block";
530
- }
531
-
532
- document.getElementById('updateConstraint').addEventListener('click', () => {
533
- if (selectedConstraint) {
534
- const aX = parseFloat(document.getElementById('consA_X').value);
535
- const aY = parseFloat(document.getElementById('consA_Y').value);
536
- const bX = parseFloat(document.getElementById('consB_X').value);
537
- const bY = parseFloat(document.getElementById('consB_Y').value);
538
- const connType = document.getElementById('consType').value;
539
- if (!selectedConstraint.bodyA) selectedConstraint.pointA = { x: aX, y: aY };
540
- if (!selectedConstraint.bodyB) selectedConstraint.pointB = { x: bX, y: bY };
541
- selectedConstraint.stiffness = connType === "spring" ? 0.05 : (connType === "stick" ? 1 : 0.9);
542
- selectedConstraint.length = Math.hypot(bX - aX, bY - aY);
543
- hideConstraintEditPanel();
544
- }
545
- });
546
-
547
- document.getElementById('deleteConstraint').addEventListener('click', () => {
548
- if (selectedConstraint) {
549
- World.remove(world, selectedConstraint);
550
- hideConstraintEditPanel();
551
- }
552
- });
553
-
554
- window.addEventListener('resize', () => {
555
- render.canvas.width = window.innerWidth;
556
- render.canvas.height = window.innerHeight;
557
- Render.lookAt(render, { min: { x: 0, y: 0 }, max: { x: window.innerWidth, y: window.innerHeight } });
558
- updateBoundaries();
559
- });
560
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
561
  </body>
562
- </html>
 
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>Physics Simulator</title>
7
+ <style>
8
+ /* Basic Reset */
9
+ * { margin: 0; padding: 0; box-sizing: border-box; }
10
+
11
+ /* Body */
12
+ body { font-family: Arial, sans-serif; overflow: hidden; }
13
+
14
+ /* Menu Bar */
15
+ .menu-bar {
16
+ display: flex;
17
+ background-color: #0078D7;
18
+ color: white;
19
+ padding: 10px;
20
+ position: fixed;
21
+ width: 100%;
22
+ top: 0;
23
+ z-index: 10;
24
+ }
25
+
26
+ .menu-bar div {
27
+ margin: 0 15px;
28
+ cursor: pointer;
29
+ position: relative;
30
+ }
31
+
32
+ .menu-bar div:hover { background-color: #005A9E; }
33
+
34
+ /* Dropdown */
35
+ .dropdown {
36
+ display: none;
37
+ position: absolute;
38
+ background-color: white;
39
+ color: black;
40
+ border: 1px solid #ccc;
41
+ top: 35px;
42
+ left: 0;
43
+ z-index: 20;
44
+ }
45
+
46
+ .dropdown div {
47
+ padding: 8px 12px;
48
+ cursor: pointer;
49
+ }
50
+
51
+ .dropdown div:hover { background-color: #f0f0f0; }
52
+
53
+ /* Canvas */
54
+ #physicsCanvas {
55
+ position: absolute;
56
+ top: 50px;
57
+ left: 0;
58
+ width: 100%;
59
+ height: calc(100vh - 50px);
60
+ background-color: #f5f5f5;
61
+ }
62
+
63
+ /* Elements */
64
+ .element {
65
+ width: 50px;
66
+ height: 50px;
67
+ background-color: #ff9800;
68
+ position: absolute;
69
+ cursor: move;
70
+ border-radius: 5px;
71
+ }
72
+
73
+ /* Edit Menu */
74
+ .edit-menu {
75
+ position: absolute;
76
+ background-color: white;
77
+ border: 1px solid #ccc;
78
+ padding: 10px;
79
+ display: none;
80
+ z-index: 50;
81
+ }
82
+
83
+ .edit-menu input { width: 100%; margin-top: 5px; }
84
+ .edit-menu button { margin-top: 10px; width: 100%; }
85
+ </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  </head>
87
  <body>
88
+
89
+ <!-- Menu Bar -->
90
+ <div class="menu-bar">
91
+ <div id="fileMenu">File
92
+ <div class="dropdown" id="fileDropdown">
93
+ <div onclick="addElement()">Add Element</div>
94
+ <div onclick="clearCanvas()">Clear Canvas</div>
95
+ </div>
96
+ </div>
97
+
98
+ <div id="editMenu">Edit
99
+ <div class="dropdown" id="editDropdown">
100
+ <div onclick="undo()">Undo</div>
101
+ <div onclick="redo()">Redo</div>
102
+ <div onclick="deleteElement()">Delete Selected</div>
103
+ </div>
104
+ </div>
105
+
106
+ <div id="viewMenu">View
107
+ <div class="dropdown" id="viewDropdown">
108
+ <div onclick="toggleGrid()">Toggle Grid</div>
109
+ </div>
110
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  </div>
112
+
113
+ <!-- Canvas -->
114
+ <div id="physicsCanvas"></div>
115
+
116
+ <!-- Edit Element Menu -->
117
+ <div class="edit-menu" id="editElementMenu">
118
+ <label>Change Color:</label>
119
+ <input type="color" id="colorPicker">
120
+ <button onclick="applyEdit()">Apply</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  </div>
122
+
123
+ <!-- JavaScript -->
124
+ <script>
125
+ const physicsCanvas = document.getElementById('physicsCanvas');
126
+ let selectedElement = null;
127
+ let undoStack = [];
128
+ let redoStack = [];
129
+
130
+ // Menu Toggle
131
+ document.querySelectorAll('.menu-bar div').forEach(menu => {
132
+ menu.addEventListener('click', () => {
133
+ const dropdown = menu.querySelector('.dropdown');
134
+ document.querySelectorAll('.dropdown').forEach(d => {
135
+ if (d !== dropdown) d.style.display = 'none';
136
+ });
137
+ dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
138
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  });
140
+
141
+ // Add Element
142
+ function addElement() {
143
+ const elem = document.createElement('div');
144
+ elem.classList.add('element');
145
+ elem.style.top = '100px';
146
+ elem.style.left = '100px';
147
+
148
+ elem.addEventListener('mousedown', startDrag);
149
+ elem.addEventListener('dblclick', openEditMenu);
150
+
151
+ physicsCanvas.appendChild(elem);
152
+ saveState();
153
  }
154
+
155
+ // Drag & Drop
156
+ let offsetX, offsetY;
157
+
158
+ function startDrag(e) {
159
+ selectedElement = e.target;
160
+ offsetX = e.clientX - selectedElement.offsetLeft;
161
+ offsetY = e.clientY - selectedElement.offsetTop;
162
+
163
+ document.addEventListener('mousemove', dragElement);
164
+ document.addEventListener('mouseup', stopDrag);
165
+ }
166
+
167
+ function dragElement(e) {
168
+ if (selectedElement) {
169
+ selectedElement.style.left = (e.clientX - offsetX) + 'px';
170
+ selectedElement.style.top = (e.clientY - offsetY) + 'px';
171
+ }
172
+ }
173
+
174
+ function stopDrag() {
175
+ document.removeEventListener('mousemove', dragElement);
176
+ document.removeEventListener('mouseup', stopDrag);
177
+ saveState();
178
+ }
179
+
180
+ // Open Edit Menu
181
+ function openEditMenu(e) {
182
+ selectedElement = e.target;
183
+ const menu = document.getElementById('editElementMenu');
184
+ menu.style.left = e.clientX + 'px';
185
+ menu.style.top = e.clientY + 'px';
186
+ document.getElementById('colorPicker').value = rgbToHex(selectedElement.style.backgroundColor);
187
+ menu.style.display = 'block';
188
+ }
189
+
190
+ // Apply Edit
191
+ function applyEdit() {
192
+ const color = document.getElementById('colorPicker').value;
193
+ selectedElement.style.backgroundColor = color;
194
+ document.getElementById('editElementMenu').style.display = 'none';
195
+ saveState();
196
+ }
197
+
198
+ // Convert RGB to HEX
199
+ function rgbToHex(rgb) {
200
+ if (!rgb) return '#ff9800';
201
+ const result = rgb.match(/\d+/g);
202
+ return "#" + result.map(x => parseInt(x).toString(16).padStart(2, '0')).join('');
203
+ }
204
+
205
+ // Delete Element
206
+ function deleteElement() {
207
+ if (selectedElement) {
208
+ selectedElement.remove();
209
+ selectedElement = null;
210
+ saveState();
211
+ }
212
+ }
213
+
214
+ // Clear Canvas
215
+ function clearCanvas() {
216
+ physicsCanvas.innerHTML = '';
217
+ saveState();
218
+ }
219
+
220
+ // Undo & Redo
221
+ function saveState() {
222
+ undoStack.push(physicsCanvas.innerHTML);
223
+ redoStack = [];
224
+ }
225
+
226
+ function undo() {
227
+ if (undoStack.length > 1) {
228
+ redoStack.push(undoStack.pop());
229
+ physicsCanvas.innerHTML = undoStack[undoStack.length - 1];
230
+ rebindElements();
231
+ }
232
+ }
233
+
234
+ function redo() {
235
+ if (redoStack.length) {
236
+ undoStack.push(redoStack.pop());
237
+ physicsCanvas.innerHTML = undoStack[undoStack.length - 1];
238
+ rebindElements();
239
+ }
240
+ }
241
+
242
+ // Rebind Events after Undo/Redo
243
+ function rebindElements() {
244
+ document.querySelectorAll('.element').forEach(elem => {
245
+ elem.addEventListener('mousedown', startDrag);
246
+ elem.addEventListener('dblclick', openEditMenu);
247
+ });
248
+ }
249
+
250
+ // Toggle Grid View
251
+ let gridOn = false;
252
+ function toggleGrid() {
253
+ gridOn = !gridOn;
254
+ physicsCanvas.style.backgroundImage = gridOn ? 'linear-gradient(#ccc 1px, transparent 1px), linear-gradient(90deg, #ccc 1px, transparent 1px)' : 'none';
255
+ physicsCanvas.style.backgroundSize = gridOn ? '20px 20px' : 'none';
256
+ }
257
+
258
+ // Initial State Save
259
+ saveState();
260
+ </script>
261
+
262
  </body>
263
+ </html>