Ramesh-vani commited on
Commit
2df038e
·
verified ·
1 Parent(s): cc20f6c

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +147 -496
index.html CHANGED
@@ -2,12 +2,12 @@
2
  <html lang="hi">
3
  <head>
4
  <meta charset="UTF-8">
5
- <title>Responsive Physics Simulator with Menus on Top</title>
6
  <style>
7
  body { margin: 0; overflow: hidden; font-family: Arial, sans-serif; }
8
  /* Full-page canvas */
9
  canvas { display: block; }
10
- /* Controls Panel (Menus) z-index high to always appear on top */
11
  #controls {
12
  position: absolute;
13
  top: 10px;
@@ -24,8 +24,7 @@
24
  #controls input, #controls select, #controls button {
25
  width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
26
  }
27
- #buttonContainer { display: flex; justify-content: space-between; }
28
- /* Edit Panel – Always on top */
29
  #editPanel {
30
  position: absolute;
31
  top: 10px;
@@ -44,7 +43,7 @@
44
  #editPanel input, #editPanel select, #editPanel button {
45
  width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
46
  }
47
- /* Connection Options Panel */
48
  #connectionPanel {
49
  position: absolute;
50
  top: 150px;
@@ -61,92 +60,57 @@
61
  #connectionPanel input, #connectionPanel select, #connectionPanel button {
62
  width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
63
  }
64
- /* Constraint Edit Panel */
65
- #constraintEditPanel {
66
- position: absolute;
67
- bottom: 10px;
68
- right: 10px;
69
- z-index: 1000;
70
- background: rgba(255,255,255,0.95);
71
- padding: 10px;
72
- border-radius: 5px;
73
- max-width: 250px;
74
- display: none;
75
- }
76
- #constraintEditPanel h3 { margin: 0 0 5px 0; }
77
- #constraintEditPanel label { display: block; margin-top: 8px; }
78
- #constraintEditPanel input, #constraintEditPanel select, #constraintEditPanel button {
79
- width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
80
- }
81
  </style>
82
  </head>
83
  <body>
84
- <!-- Controls Panel (Menus) -->
85
  <div id="controls">
86
- <label for="simulationSelect">Simulation Element Chunein:</label>
87
- <select id="simulationSelect">
88
- <option value="bouncingBall">Bouncing Ball</option>
89
- <option value="pendulum">Pendulum</option>
90
- <option value="projectile">Projectile Motion</option>
91
- <option value="inclinedPlane">Inclined Plane</option>
92
- <option value="springMass">Spring–Mass System</option>
93
  </select>
94
- <!-- Dynamic Suboptions Container -->
95
- <div id="subOptionsContainer"></div>
96
- <div id="buttonContainer">
97
- <button id="addElement">Add Element</button>
98
- <button id="reset">Reset Simulation</button>
 
 
 
 
 
 
 
 
 
 
99
  </div>
 
 
 
 
100
  </div>
101
 
102
- <!-- Edit Panel (Opens on DoubleClick on an Element) -->
103
  <div id="editPanel"></div>
104
 
105
- <!-- Connection Options Panel -->
106
  <div id="connectionPanel">
107
- <h3>Connection Options</h3>
108
  <label for="connType">Connection Type:</label>
109
  <select id="connType">
110
- <option value="string">String</option>
111
  <option value="spring">Spring</option>
112
- <option value="stick">Stick</option>
113
- </select>
114
- <div id="sourceEndpointDiv">
115
- <label for="sourceEndpoint">Source Endpoint:</label>
116
- <select id="sourceEndpoint">
117
- <option value="mass">Mass</option>
118
- <option value="fixed">Fixed</option>
119
- </select>
120
- </div>
121
- <button id="cancelConn">Cancel</button>
122
- <p style="font-size: 12px; color: #555;">Ab target element par click karein jisse attach karna hai.</p>
123
- </div>
124
-
125
- <!-- Constraint Edit Panel -->
126
- <div id="constraintEditPanel">
127
- <h3>Edit Constraint</h3>
128
- <label for="consA_X">Endpoint A X:</label>
129
- <input type="number" id="consA_X" step="1">
130
- <label for="consA_Y">Endpoint A Y:</label>
131
- <input type="number" id="consA_Y" step="1">
132
- <label for="consB_X">Endpoint B X:</label>
133
- <input type="number" id="consB_X" step="1">
134
- <label for="consB_Y">Endpoint B Y:</label>
135
- <input type="number" id="consB_Y" step="1">
136
- <label for="consType">Connection Type:</label>
137
- <select id="consType">
138
  <option value="string">String</option>
139
- <option value="spring">Spring</option>
140
- <option value="stick">Stick</option>
141
  </select>
142
- <button id="updateConstraint">Update Constraint</button>
143
- <button id="deleteConstraint">Delete Constraint</button>
144
  </div>
145
 
146
- <!-- Matter.js Library and Script -->
147
  <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
148
  <script>
149
- // Module aliases and Engine Setup
150
  const Engine = Matter.Engine,
151
  Render = Matter.Render,
152
  Runner = Matter.Runner,
@@ -158,6 +122,7 @@
158
  MouseConstraint = Matter.MouseConstraint,
159
  Events = Matter.Events;
160
 
 
161
  const engine = Engine.create();
162
  const world = engine.world;
163
  const render = Render.create({
@@ -174,16 +139,13 @@
174
  const runner = Runner.create();
175
  Runner.run(runner, engine);
176
 
177
- // Global variables for boundaries (declare as let so we can update them)
178
  let floor, wallLeft, wallRight;
179
-
180
- // Function to create/update boundaries based on current window dimensions
181
  function updateBoundaries() {
182
- // Agar boundaries already exist, remove unhein
183
- if(floor && wallLeft && wallRight) {
184
  World.remove(world, [floor, wallLeft, wallRight]);
185
  }
186
- floor = Bodies.rectangle(window.innerWidth/2, window.innerHeight - 50, window.innerWidth, 100, {
187
  isStatic: true,
188
  render: { fillStyle: '#060a19' }
189
  });
@@ -191,7 +153,6 @@
191
  wallRight = Bodies.rectangle(window.innerWidth+50, window.innerHeight/2, 100, window.innerHeight, { isStatic: true });
192
  World.add(world, [floor, wallLeft, wallRight]);
193
  }
194
- // Initial boundaries creation
195
  updateBoundaries();
196
 
197
  // Mouse control for drag & drop
@@ -203,331 +164,107 @@
203
  World.add(world, mouseConstraint);
204
  render.mouse = mouse;
205
 
206
- // Global Variables for Editing & Attachment
207
  let selectedBody = null;
208
- let selectedConstraint = null;
209
  let attachMode = false;
210
  let attachFrom = null;
211
- const connectionPanel = document.getElementById('connectionPanel');
212
- const editPanel = document.getElementById('editPanel');
213
 
214
- // Helper function for random color
215
  function getRandomColor() {
216
  return '#' + Math.floor(Math.random()*16777215).toString(16);
217
  }
218
 
219
- /* ---------- Dynamic Suboptions for Adding Elements ---------- */
220
- function updateSubOptions() {
221
- const simulationSelect = document.getElementById('simulationSelect');
222
- const subOptionsContainer = document.getElementById('subOptionsContainer');
223
- let selected = simulationSelect.value;
224
- let html = '';
225
- if(selected === 'bouncingBall'){
226
- html += `
227
- <label for="ballInitialX">Initial X:</label>
228
- <input type="number" id="ballInitialX" value="100">
229
- <label for="ballInitialY">Initial Y:</label>
230
- <input type="number" id="ballInitialY" value="50">
231
- <label for="ballRadius">Radius:</label>
232
- <input type="number" id="ballRadius" value="30">
233
- <label for="ballRestitution">Restitution:</label>
234
- <input type="number" id="ballRestitution" value="0.9" step="0.1" min="0" max="1">
235
- <label for="ballFriction">Friction:</label>
236
- <input type="number" id="ballFriction" value="0.005" step="0.001" min="0" max="1">
237
- <label for="ballColor">Color:</label>
238
- <input type="color" id="ballColor" value="#3498db">
239
- `;
240
- } else if(selected === 'pendulum'){
241
- html += `
242
- <label for="pendulumPivotX">Pivot X:</label>
243
- <input type="number" id="pendulumPivotX" value="${window.innerWidth-200}">
244
- <label for="pendulumPivotY">Pivot Y:</label>
245
- <input type="number" id="pendulumPivotY" value="50">
246
- <label for="pendulumBobRadius">Bob Radius:</label>
247
- <input type="number" id="pendulumBobRadius" value="40">
248
- <label for="pendulumLength">Length:</label>
249
- <input type="number" id="pendulumLength" value="250">
250
- <label for="pendulumStiffness">Stiffness:</label>
251
- <input type="number" id="pendulumStiffness" value="1" step="0.1" min="0" max="1">
252
- <label for="pendulumBobColor">Color:</label>
253
- <input type="color" id="pendulumBobColor" value="#ff0000">
254
- `;
255
- } else if(selected === 'projectile'){
256
- html += `
257
- <label for="projectileInitialX">Initial X:</label>
258
- <input type="number" id="projectileInitialX" value="100">
259
- <label for="projectileInitialY">Initial Y:</label>
260
- <input type="number" id="projectileInitialY" value="300">
261
- <label for="projectileRadius">Radius:</label>
262
- <input type="number" id="projectileRadius" value="20">
263
- <label for="projectileVelX">Velocity X:</label>
264
- <input type="number" id="projectileVelX" value="15">
265
- <label for="projectileVelY">Velocity Y:</label>
266
- <input type="number" id="projectileVelY" value="-15">
267
- <label for="projectileColor">Color:</label>
268
- <input type="color" id="projectileColor" value="#e67e22">
269
- `;
270
- } else if(selected === 'inclinedPlane'){
271
- html += `
272
- <label for="rampWidth">Ramp Width:</label>
273
- <input type="number" id="rampWidth" value="300">
274
- <label for="rampHeight">Ramp Height:</label>
275
- <input type="number" id="rampHeight" value="20">
276
- <label for="rampAngle">Ramp Angle (°):</label>
277
- <input type="number" id="rampAngle" value="30">
278
- <label for="rampX">Ramp X:</label>
279
- <input type="number" id="rampX" value="400">
280
- <label for="rampY">Ramp Y:</label>
281
- <input type="number" id="rampY" value="${window.innerHeight-150}">
282
- <label for="rampColor">Ramp Color:</label>
283
- <input type="color" id="rampColor" value="#8e44ad">
284
- <hr>
285
- <label for="blockWidth">Block Width:</label>
286
- <input type="number" id="blockWidth" value="40">
287
- <label for="blockHeight">Block Height:</label>
288
- <input type="number" id="blockHeight" value="40">
289
- <label for="blockFriction">Block Friction:</label>
290
- <input type="number" id="blockFriction" value="0.05" step="0.01" min="0" max="1">
291
- <label for="blockColor">Block Color:</label>
292
- <input type="color" id="blockColor" value="#1abc9c">
293
- `;
294
- } else if(selected === 'springMass'){
295
- html += `
296
- <label for="springMassRadius">Mass Radius:</label>
297
- <input type="number" id="springMassRadius" value="25">
298
- <label for="springMassRestitution">Restitution:</label>
299
- <input type="number" id="springMassRestitution" value="0.8" step="0.1" min="0" max="1">
300
- <label for="fixedPointX">Fixed X:</label>
301
- <input type="number" id="fixedPointX" value="${Math.floor(window.innerWidth/2)}">
302
- <label for="fixedPointY">Fixed Y:</label>
303
- <input type="number" id="fixedPointY" value="100">
304
- <label for="springLength">Spring Length:</label>
305
- <input type="number" id="springLength" value="200">
306
- <label for="springStiffness">Stiffness:</label>
307
- <input type="number" id="springStiffness" value="0.02" step="0.01" min="0" max="1">
308
- <label for="springMassColor">Color:</label>
309
- <input type="color" id="springMassColor" value="#f39c12">
310
- `;
311
  }
312
- subOptionsContainer.innerHTML = html;
313
- }
314
- updateSubOptions();
315
- document.getElementById('simulationSelect').addEventListener('change', updateSubOptions);
316
 
317
- /* ---------- Functions to Add Simulation Elements ---------- */
318
- function addBouncingBall(){
319
- const x = parseFloat(document.getElementById("ballInitialX").value);
320
- const y = parseFloat(document.getElementById("ballInitialY").value);
321
- const radius = parseFloat(document.getElementById("ballRadius").value);
322
- const restitution = parseFloat(document.getElementById("ballRestitution").value);
323
- const friction = parseFloat(document.getElementById("ballFriction").value);
324
- const color = document.getElementById("ballColor").value || getRandomColor();
325
- const ball = Bodies.circle(x, y, radius, { restitution, friction, render: { fillStyle: color } });
326
- ball.elementType = "bouncingBall";
327
- ball.customOptions = { x, y, radius, restitution, friction, color };
328
- World.add(world, ball);
329
- }
330
- function addPendulum(){
331
- const pivotX = parseFloat(document.getElementById("pendulumPivotX").value);
332
- const pivotY = parseFloat(document.getElementById("pendulumPivotY").value);
333
- const bobRadius = parseFloat(document.getElementById("pendulumBobRadius").value);
334
- const pendulumLength = parseFloat(document.getElementById("pendulumLength").value);
335
- const stiffness = parseFloat(document.getElementById("pendulumStiffness").value);
336
- const bobColor = document.getElementById("pendulumBobColor").value || "#ff0000";
337
- const bob = Bodies.circle(pivotX, pivotY+pendulumLength, bobRadius, { restitution: 1, density: 0.005, render: { fillStyle: bobColor } });
338
- bob.elementType = "pendulum";
339
- bob.customOptions = { pivotX, pivotY, bobRadius, pendulumLength, stiffness, bobColor };
340
- const pendulumConstraint = Constraint.create({
341
- pointA: { x: pivotX, y: pivotY },
342
- bodyB: bob,
343
- length: pendulumLength,
344
- stiffness: stiffness,
345
- render: { strokeStyle: '#000', lineWidth: 2 }
346
- });
347
- World.add(world, [bob, pendulumConstraint]);
348
- }
349
- function addProjectile(){
350
- const x = parseFloat(document.getElementById("projectileInitialX").value);
351
- const y = parseFloat(document.getElementById("projectileInitialY").value);
352
- const radius = parseFloat(document.getElementById("projectileRadius").value);
353
- const velX = parseFloat(document.getElementById("projectileVelX").value);
354
- const velY = parseFloat(document.getElementById("projectileVelY").value);
355
- const color = document.getElementById("projectileColor").value || getRandomColor();
356
- const proj = Bodies.circle(x, y, radius, { restitution: 0.8, frictionAir: 0.001, render: { fillStyle: color } });
357
- proj.elementType = "projectile";
358
- proj.customOptions = { x, y, radius, velX, velY, color };
359
- Body.setVelocity(proj, { x: velX, y: velY });
360
- World.add(world, proj);
361
- }
362
- function addInclinedPlane(){
363
- // Do bodies: ek ramp (static) aur ek block.
364
- const rampWidth = parseFloat(document.getElementById("rampWidth").value);
365
- const rampHeight = parseFloat(document.getElementById("rampHeight").value);
366
- const angleDeg = parseFloat(document.getElementById("rampAngle").value);
367
- const angle = angleDeg * Math.PI/180;
368
- const rampX = parseFloat(document.getElementById("rampX").value);
369
- const rampY = parseFloat(document.getElementById("rampY").value);
370
- const rampColor = document.getElementById("rampColor").value || "#8e44ad";
371
- const ramp = Bodies.rectangle(rampX, rampY, rampWidth, rampHeight, {
372
- isStatic: true,
373
- angle: angle,
374
- render: { fillStyle: rampColor }
375
  });
376
- ramp.elementType = "inclinedPlane_ramp";
377
- ramp.customOptions = { rampWidth, rampHeight, angleDeg, rampX, rampY, rampColor };
378
- const blockWidth = parseFloat(document.getElementById("blockWidth").value);
379
- const blockHeight = parseFloat(document.getElementById("blockHeight").value);
380
- const blockFriction = parseFloat(document.getElementById("blockFriction").value);
381
- const blockColor = document.getElementById("blockColor").value || "#1abc9c";
382
- const block = Bodies.rectangle(rampX - rampWidth/4, rampY - 50, blockWidth, blockHeight, {
383
- friction: blockFriction,
384
- render: { fillStyle: blockColor }
385
- });
386
- block.elementType = "inclinedPlane_block";
387
- block.customOptions = { blockWidth, blockHeight, blockFriction, blockColor };
388
- World.add(world, [ramp, block]);
389
  }
390
- function addSpringMass(){
391
- const massRadius = parseFloat(document.getElementById("springMassRadius").value);
392
- const massRestitution = parseFloat(document.getElementById("springMassRestitution").value);
393
- const fixedPointX = parseFloat(document.getElementById("fixedPointX").value);
394
- const fixedPointY = parseFloat(document.getElementById("fixedPointY").value);
395
- const springLength = parseFloat(document.getElementById("springLength").value);
396
- const springStiffness = parseFloat(document.getElementById("springStiffness").value);
397
- const massColor = document.getElementById("springMassColor").value || getRandomColor();
398
- const mass = Bodies.circle(fixedPointX, fixedPointY+springLength, massRadius, {
399
- restitution: massRestitution,
400
- density: 0.004,
401
- render: { fillStyle: massColor }
402
- });
403
- mass.elementType = "springMass";
404
- mass.customOptions = { massRadius, massRestitution, fixedPointX, fixedPointY, springLength, springStiffness, massColor };
405
- mass.fixedPoint = { x: fixedPointX, y: fixedPointY };
406
- const spring = Constraint.create({
407
- pointA: { x: fixedPointX, y: fixedPointY },
408
- bodyB: mass,
409
- length: springLength,
410
- stiffness: springStiffness,
411
- damping: 0.05,
412
- render: { strokeStyle: '#000', lineWidth: 2 }
413
  });
414
- World.add(world, [mass, spring]);
 
 
415
  }
416
- document.getElementById('addElement').addEventListener('click', function(){
417
- const selected = document.getElementById('simulationSelect').value;
418
- if(selected === 'bouncingBall') addBouncingBall();
419
- else if(selected === 'pendulum') addPendulum();
420
- else if(selected === 'projectile') addProjectile();
421
- else if(selected === 'inclinedPlane') addInclinedPlane();
422
- else if(selected === 'springMass') addSpringMass();
423
  });
424
- document.getElementById('reset').addEventListener('click', function(){
425
  World.clear(world);
426
  Engine.clear(engine);
427
- // Re-create boundaries on reset
428
  updateBoundaries();
429
  mouse = Mouse.create(render.canvas);
430
- mouseConstraint = MouseConstraint.create(engine, { mouse: mouse, constraint: { stiffness: 0.2, render: { visible: false } } });
 
 
 
431
  World.add(world, mouseConstraint);
432
  render.mouse = mouse;
433
  hideEditPanel();
434
  hideConnectionPanel();
435
- hideConstraintEditPanel();
436
  });
437
 
438
- /* ---------- Edit Panel Functions (DoubleClick to Open) ---------- */
439
  function openEditPanel(body) {
440
  selectedBody = body;
441
  let html = `<h3>Edit Element</h3>
442
- <label for="editPosX">Position X:</label>
443
  <input type="number" id="editPosX" value="${body.position.x.toFixed(2)}">
444
- <label for="editPosY">Position Y:</label>
445
  <input type="number" id="editPosY" value="${body.position.y.toFixed(2)}">
446
- <label for="editAngle">Angle (radians):</label>
447
  <input type="number" id="editAngle" value="${body.angle.toFixed(2)}">
448
- <label for="editColor">Color:</label>
449
- <input type="color" id="editColor" value="${body.render.fillStyle || '#ffffff'}">`;
450
- if(body.elementType === "bouncingBall"){
451
- const opts = body.customOptions;
452
- html += `<label for="editBallRadius">Radius:</label>
453
- <input type="number" id="editBallRadius" value="${opts.radius}">
454
- <label for="editBallRestitution">Restitution:</label>
455
- <input type="number" id="editBallRestitution" value="${opts.restitution}" step="0.1" min="0" max="1">
456
- <label for="editBallFriction">Friction:</label>
457
- <input type="number" id="editBallFriction" value="${opts.friction}" step="0.001" min="0" max="1">`;
458
- } else if(body.elementType === "pendulum"){
459
- const opts = body.customOptions;
460
- html += `<label for="editPendulumPivotX">Pivot X:</label>
461
- <input type="number" id="editPendulumPivotX" value="${opts.pivotX}">
462
- <label for="editPendulumPivotY">Pivot Y:</label>
463
- <input type="number" id="editPendulumPivotY" value="${opts.pivotY}">
464
- <label for="editPendulumBobRadius">Bob Radius:</label>
465
- <input type="number" id="editPendulumBobRadius" value="${opts.bobRadius}">
466
- <label for="editPendulumLength">Length:</label>
467
- <input type="number" id="editPendulumLength" value="${opts.pendulumLength}">
468
- <label for="editPendulumStiffness">Stiffness:</label>
469
- <input type="number" id="editPendulumStiffness" value="${opts.stiffness}" step="0.1" min="0" max="1">
470
- <label for="editPendulumBobColor">Bob Color:</label>
471
- <input type="color" id="editPendulumBobColor" value="${opts.bobColor}">`;
472
- } else if(body.elementType === "projectile"){
473
- const opts = body.customOptions;
474
- html += `<label for="editProjectileRadius">Radius:</label>
475
- <input type="number" id="editProjectileRadius" value="${opts.radius}">
476
- <label for="editProjectileVelX">Velocity X:</label>
477
- <input type="number" id="editProjectileVelX" value="${opts.velX}">
478
- <label for="editProjectileVelY">Velocity Y:</label>
479
- <input type="number" id="editProjectileVelY" value="${opts.velY}">`;
480
- } else if(body.elementType === "inclinedPlane_ramp"){
481
- const opts = body.customOptions;
482
- html += `<label for="editRampWidth">Ramp Width:</label>
483
- <input type="number" id="editRampWidth" value="${opts.rampWidth}">
484
- <label for="editRampHeight">Ramp Height:</label>
485
- <input type="number" id="editRampHeight" value="${opts.rampHeight}">
486
- <label for="editRampAngle">Ramp Angle (°):</label>
487
- <input type="number" id="editRampAngle" value="${opts.angleDeg}">
488
- <label for="editRampX">Ramp X:</label>
489
- <input type="number" id="editRampX" value="${opts.rampX}">
490
- <label for="editRampY">Ramp Y:</label>
491
- <input type="number" id="editRampY" value="${opts.rampY}">
492
- <label for="editRampColor">Ramp Color:</label>
493
- <input type="color" id="editRampColor" value="${opts.rampColor}">`;
494
- } else if(body.elementType === "inclinedPlane_block"){
495
- const opts = body.customOptions;
496
- html += `<label for="editBlockWidth">Block Width:</label>
497
- <input type="number" id="editBlockWidth" value="${opts.blockWidth}">
498
- <label for="editBlockHeight">Block Height:</label>
499
- <input type="number" id="editBlockHeight" value="${opts.blockHeight}">
500
- <label for="editBlockFriction">Block Friction:</label>
501
- <input type="number" id="editBlockFriction" value="${opts.blockFriction}" step="0.01" min="0" max="1">
502
- <label for="editBlockColor">Block Color:</label>
503
- <input type="color" id="editBlockColor" value="${opts.blockColor}">`;
504
- } else if(body.elementType === "springMass"){
505
- const opts = body.customOptions;
506
- html += `<label for="editSpringMassRadius">Mass Radius:</label>
507
- <input type="number" id="editSpringMassRadius" value="${opts.massRadius}">
508
- <label for="editSpringMassRestitution">Restitution:</label>
509
- <input type="number" id="editSpringMassRestitution" value="${opts.massRestitution}" step="0.1" min="0" max="1">
510
- <label for="editFixedPointX">Fixed X:</label>
511
- <input type="number" id="editFixedPointX" value="${opts.fixedPointX}">
512
- <label for="editFixedPointY">Fixed Y:</label>
513
- <input type="number" id="editFixedPointY" value="${opts.fixedPointY}">
514
- <label for="editSpringLength">Spring Length:</label>
515
- <input type="number" id="editSpringLength" value="${opts.springLength}">
516
- <label for="editSpringStiffness">Spring Stiffness:</label>
517
- <input type="number" id="editSpringStiffness" value="${opts.springStiffness}" step="0.01" min="0" max="1">
518
- <label for="editSpringMassColor">Mass Color:</label>
519
- <input type="color" id="editSpringMassColor" value="${opts.massColor}">`;
520
- }
521
- html += `<div style="margin-top:10px;">
522
- <button id="updateElement">Update</button>
523
- <button id="deleteElement">Delete</button>
524
- </div>`;
525
  editPanel.innerHTML = html;
526
  editPanel.style.display = "block";
527
  document.getElementById("updateElement").addEventListener("click", updateElementFromEditPanel);
528
  document.getElementById("deleteElement").addEventListener("click", function(){
529
- World.remove(world, selectedBody);
530
- hideEditPanel();
 
 
 
 
 
 
531
  });
532
  }
533
  function hideEditPanel() {
@@ -543,150 +280,64 @@
543
  Body.setPosition(selectedBody, { x: newX, y: newY });
544
  Body.setAngle(selectedBody, newAngle);
545
  selectedBody.render.fillStyle = newColor;
546
- if(selectedBody.elementType === "bouncingBall"){
547
- const oldRadius = selectedBody.circleRadius;
548
- const newRadius = parseFloat(document.getElementById("editBallRadius").value);
549
- const newRestitution = parseFloat(document.getElementById("editBallRestitution").value);
550
- const newFriction = parseFloat(document.getElementById("editBallFriction").value);
551
- if(newRadius && oldRadius && newRadius !== oldRadius){
552
- const scaleFactor = newRadius / oldRadius;
553
- Body.scale(selectedBody, scaleFactor, scaleFactor);
554
- }
555
- selectedBody.restitution = newRestitution;
556
- selectedBody.friction = newFriction;
557
- selectedBody.customOptions = { x: newX, y: newY, radius: newRadius, restitution: newRestitution, friction: newFriction, color: newColor };
558
- }
559
- // (Update code for other types as needed...)
560
  hideEditPanel();
561
  }
562
-
563
- // Open edit panel on double–click on an element
564
  render.canvas.addEventListener("dblclick", function(event){
565
  const rect = render.canvas.getBoundingClientRect();
566
  const mousePos = { x: event.clientX - rect.left, y: event.clientY - rect.top };
567
  const bodies = Matter.Composite.allBodies(world);
568
  const clicked = Matter.Query.point(bodies, mousePos);
569
- if(clicked.length > 0){
570
- openEditPanel(clicked[0]);
571
- } else {
572
- hideEditPanel();
573
- }
574
  });
575
 
576
- /* ---------- Constraint & Connection Panels (Same as previous example) ---------- */
577
- document.getElementById('cancelConn').addEventListener('click', function(){
578
- connectionPanel.style.display = "none";
579
- attachMode = false;
580
- attachFrom = null;
581
- });
582
- Events.on(mouseConstraint, 'mouseup', function(event){
583
  const mousePos = event.mouse.position;
584
  const bodies = Matter.Composite.allBodies(world);
585
  const clickedBodies = Matter.Query.point(bodies, mousePos);
586
- if(clickedBodies.length > 0){
587
- const clickedBody = clickedBodies[0];
588
- if(attachMode && attachFrom && clickedBody !== attachFrom){
589
- const connType = document.getElementById('connType').value;
590
- let sourceEndpoint = "mass";
591
- if(attachFrom.elementType === "springMass"){
592
- sourceEndpoint = document.getElementById('sourceEndpoint').value;
593
- }
594
- let targetEndpoint = "mass";
595
- if(clickedBody.elementType === "springMass"){
596
- targetEndpoint = prompt("Target springMass: 'fixed' ya 'mass' (default mass):", "mass") || "mass";
597
- if(targetEndpoint !== "fixed") { targetEndpoint = "mass"; }
598
- }
599
- let pointA = { x: 0, y: 0 }, bodyA = attachFrom;
600
- if(attachFrom.elementType === "springMass" && sourceEndpoint === "fixed"){
601
- bodyA = null;
602
- pointA = attachFrom.fixedPoint;
603
- }
604
- let pointB = { x: 0, y: 0 }, bodyB = clickedBody;
605
- if(clickedBody.elementType === "springMass" && targetEndpoint === "fixed"){
606
- bodyB = null;
607
- pointB = clickedBody.fixedPoint || { x: clickedBody.position.x, y: clickedBody.position.y };
608
- }
609
- let posA = bodyA ? attachFrom.position : pointA;
610
- let posB = bodyB ? clickedBody.position : pointB;
611
- let dx = posB.x - posA.x, dy = posB.y - posA.y;
612
- let length = Math.sqrt(dx*dx+dy*dy);
613
- let stiffness = (connType === "spring") ? 0.05 : 1;
614
- const newConstraint = Constraint.create({
615
- bodyA: bodyA,
616
- pointA: pointA,
617
- bodyB: bodyB,
618
- pointB: pointB,
619
- length: length,
620
- stiffness: stiffness,
621
- render: { strokeStyle: '#000', lineWidth: 2 }
622
- });
623
- World.add(world, newConstraint);
624
- attachMode = false;
625
- attachFrom = null;
626
- connectionPanel.style.display = "none";
627
- alert("Attachment created using " + connType + " connection.");
628
- }
629
- } else {
630
- const constraints = Matter.Composite.allConstraints(world);
631
- for(let cons of constraints){
632
- let posA = cons.bodyA ? { x: cons.bodyA.position.x + cons.pointA.x, y: cons.bodyA.position.y + cons.pointA.y } : cons.pointA;
633
- let posB = cons.bodyB ? { x: cons.bodyB.position.x + cons.pointB.x, y: cons.bodyB.position.y + cons.pointB.y } : cons.pointB;
634
- let dist = distanceToSegment(mousePos, posA, posB);
635
- if(dist < 5){
636
- showConstraintEditPanel(cons);
637
- return;
638
- }
639
- }
640
- hideConstraintEditPanel();
641
- }
642
- });
643
- function distanceToSegment(p, v, w){
644
- let l2 = (w.x-v.x)**2 + (w.y-v.y)**2;
645
- if(l2 === 0) return Math.hypot(p.x-v.x, p.y-v.y);
646
- let t = ((p.x-v.x)*(w.x-v.x) + (p.y-v.y)*(w.y-v.y)) / l2;
647
- t = Math.max(0, Math.min(1, t));
648
- let proj = { x: v.x + t*(w.x-v.x), y: v.y + t*(w.y-v.y) };
649
- return Math.hypot(p.x-proj.x, p.y-proj.y);
650
- }
651
- const constraintEditPanel = document.getElementById('constraintEditPanel');
652
- function showConstraintEditPanel(cons){
653
- selectedConstraint = cons;
654
- let posA = cons.bodyA ? { x: cons.bodyA.position.x + cons.pointA.x, y: cons.bodyA.position.y + cons.pointA.y } : cons.pointA;
655
- let posB = cons.bodyB ? { x: cons.bodyB.position.x + cons.pointB.x, y: cons.bodyB.position.y + cons.pointB.y } : cons.pointB;
656
- document.getElementById('consA_X').value = posA.x.toFixed(2);
657
- document.getElementById('consA_Y').value = posA.y.toFixed(2);
658
- document.getElementById('consB_X').value = posB.x.toFixed(2);
659
- document.getElementById('consB_Y').value = posB.y.toFixed(2);
660
- let connType = (cons.stiffness < 0.1) ? "spring" : "string";
661
- document.getElementById('consType').value = connType;
662
- constraintEditPanel.style.display = "block";
663
- }
664
- function hideConstraintEditPanel(){
665
- constraintEditPanel.style.display = "none";
666
- selectedConstraint = null;
667
- }
668
- document.getElementById('updateConstraint').addEventListener('click', function(){
669
- if(selectedConstraint){
670
- let aX = parseFloat(document.getElementById('consA_X').value);
671
- let aY = parseFloat(document.getElementById('consA_Y').value);
672
- let bX = parseFloat(document.getElementById('consB_X').value);
673
- let bY = parseFloat(document.getElementById('consB_Y').value);
674
- if(!selectedConstraint.bodyA) { selectedConstraint.pointA = { x: aX, y: aY }; }
675
- if(!selectedConstraint.bodyB) { selectedConstraint.pointB = { x: bX, y: bY }; }
676
- let connType = document.getElementById('consType').value;
677
- selectedConstraint.stiffness = (connType === "spring") ? 0.05 : 1;
678
- hideConstraintEditPanel();
679
  }
680
  });
681
- document.getElementById('deleteConstraint').addEventListener('click', function(){
682
- if(selectedConstraint){
683
- World.remove(world, selectedConstraint);
684
- hideConstraintEditPanel();
685
- }
686
  });
687
 
688
- // Window resize event: update renderer and boundaries
689
- window.addEventListener('resize', function(){
690
  Render.lookAt(render, { min: { x: 0, y: 0 }, max: { x: window.innerWidth, y: window.innerHeight } });
691
  updateBoundaries();
692
  });
 
2
  <html lang="hi">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <title>Responsive Physics Simulator with Flexible Attachments</title>
6
  <style>
7
  body { margin: 0; overflow: hidden; font-family: Arial, sans-serif; }
8
  /* Full-page canvas */
9
  canvas { display: block; }
10
+ /* Controls Panel – always on top */
11
  #controls {
12
  position: absolute;
13
  top: 10px;
 
24
  #controls input, #controls select, #controls button {
25
  width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
26
  }
27
+ /* Edit Panel appears on double–click */
 
28
  #editPanel {
29
  position: absolute;
30
  top: 10px;
 
43
  #editPanel input, #editPanel select, #editPanel button {
44
  width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
45
  }
46
+ /* Connection Panel – for attachments */
47
  #connectionPanel {
48
  position: absolute;
49
  top: 150px;
 
60
  #connectionPanel input, #connectionPanel select, #connectionPanel button {
61
  width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
62
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  </style>
64
  </head>
65
  <body>
66
+ <!-- Controls Panel -->
67
  <div id="controls">
68
+ <label for="elementType">Element Type:</label>
69
+ <select id="elementType">
70
+ <option value="block">Block (Rectangle)</option>
71
+ <option value="circle">Circle</option>
 
 
 
72
  </select>
73
+ <label for="initX">Initial X:</label>
74
+ <input type="number" id="initX" value="150">
75
+ <label for="initY">Initial Y:</label>
76
+ <input type="number" id="initY" value="100">
77
+ <!-- For block -->
78
+ <div id="blockOptions">
79
+ <label for="blockWidth">Width:</label>
80
+ <input type="number" id="blockWidth" value="80">
81
+ <label for="blockHeight">Height:</label>
82
+ <input type="number" id="blockHeight" value="60">
83
+ </div>
84
+ <!-- For circle -->
85
+ <div id="circleOptions" style="display:none;">
86
+ <label for="circleRadius">Radius:</label>
87
+ <input type="number" id="circleRadius" value="30">
88
  </div>
89
+ <label for="initColor">Color:</label>
90
+ <input type="color" id="initColor" value="#3498db">
91
+ <button id="addElement">Add Element</button>
92
+ <button id="reset">Reset Simulation</button>
93
  </div>
94
 
95
+ <!-- Edit Panel (opens on doubleclick) -->
96
  <div id="editPanel"></div>
97
 
98
+ <!-- Connection Panel (for attachments) -->
99
  <div id="connectionPanel">
100
+ <h3>Attachment Options</h3>
101
  <label for="connType">Connection Type:</label>
102
  <select id="connType">
 
103
  <option value="spring">Spring</option>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  <option value="string">String</option>
 
 
105
  </select>
106
+ <button id="cancelConn">Cancel Attachment</button>
107
+ <p style="font-size:12px; color:#555;">Ab target element par click karein.</p>
108
  </div>
109
 
110
+ <!-- Matter.js Library -->
111
  <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
112
  <script>
113
+ // Module aliases
114
  const Engine = Matter.Engine,
115
  Render = Matter.Render,
116
  Runner = Matter.Runner,
 
122
  MouseConstraint = Matter.MouseConstraint,
123
  Events = Matter.Events;
124
 
125
+ // Create engine and world
126
  const engine = Engine.create();
127
  const world = engine.world;
128
  const render = Render.create({
 
139
  const runner = Runner.create();
140
  Runner.run(runner, engine);
141
 
142
+ // Global boundaries (will be updated on resize)
143
  let floor, wallLeft, wallRight;
 
 
144
  function updateBoundaries() {
145
+ if(floor && wallLeft && wallRight){
 
146
  World.remove(world, [floor, wallLeft, wallRight]);
147
  }
148
+ floor = Bodies.rectangle(window.innerWidth/2, window.innerHeight-50, window.innerWidth, 100, {
149
  isStatic: true,
150
  render: { fillStyle: '#060a19' }
151
  });
 
153
  wallRight = Bodies.rectangle(window.innerWidth+50, window.innerHeight/2, 100, window.innerHeight, { isStatic: true });
154
  World.add(world, [floor, wallLeft, wallRight]);
155
  }
 
156
  updateBoundaries();
157
 
158
  // Mouse control for drag & drop
 
164
  World.add(world, mouseConstraint);
165
  render.mouse = mouse;
166
 
167
+ // Global variables for editing & attachment
168
  let selectedBody = null;
 
169
  let attachMode = false;
170
  let attachFrom = null;
171
+ const editPanel = document.getElementById("editPanel");
172
+ const connectionPanel = document.getElementById("connectionPanel");
173
 
174
+ // Helper: random color
175
  function getRandomColor() {
176
  return '#' + Math.floor(Math.random()*16777215).toString(16);
177
  }
178
 
179
+ // Toggle suboptions based on element type
180
+ const elementTypeSelect = document.getElementById("elementType");
181
+ elementTypeSelect.addEventListener("change", function(){
182
+ if(this.value === "block"){
183
+ document.getElementById("blockOptions").style.display = "block";
184
+ document.getElementById("circleOptions").style.display = "none";
185
+ } else {
186
+ document.getElementById("blockOptions").style.display = "none";
187
+ document.getElementById("circleOptions").style.display = "block";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  }
189
+ });
 
 
 
190
 
191
+ // Functions to add elements
192
+ function addBlock(){
193
+ const x = parseFloat(document.getElementById("initX").value);
194
+ const y = parseFloat(document.getElementById("initY").value);
195
+ const width = parseFloat(document.getElementById("blockWidth").value);
196
+ const height = parseFloat(document.getElementById("blockHeight").value);
197
+ const color = document.getElementById("initColor").value || getRandomColor();
198
+ const block = Bodies.rectangle(x, y, width, height, {
199
+ restitution: 0.1,
200
+ friction: 0.5,
201
+ render: { fillStyle: color }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  });
203
+ // Store creation options for later editing
204
+ block.customOptions = { x, y, width, height, color };
205
+ block.elementType = "block";
206
+ World.add(world, block);
 
 
 
 
 
 
 
 
 
207
  }
208
+ function addCircle(){
209
+ const x = parseFloat(document.getElementById("initX").value);
210
+ const y = parseFloat(document.getElementById("initY").value);
211
+ const radius = parseFloat(document.getElementById("circleRadius").value);
212
+ const color = document.getElementById("initColor").value || getRandomColor();
213
+ const circle = Bodies.circle(x, y, radius, {
214
+ restitution: 0.9,
215
+ friction: 0.005,
216
+ render: { fillStyle: color }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  });
218
+ circle.customOptions = { x, y, radius, color };
219
+ circle.elementType = "circle";
220
+ World.add(world, circle);
221
  }
222
+ document.getElementById("addElement").addEventListener("click", function(){
223
+ if(elementTypeSelect.value === "block") addBlock();
224
+ else addCircle();
 
 
 
 
225
  });
226
+ document.getElementById("reset").addEventListener("click", function(){
227
  World.clear(world);
228
  Engine.clear(engine);
 
229
  updateBoundaries();
230
  mouse = Mouse.create(render.canvas);
231
+ mouseConstraint = MouseConstraint.create(engine, {
232
+ mouse: mouse,
233
+ constraint: { stiffness: 0.2, render: { visible: false } }
234
+ });
235
  World.add(world, mouseConstraint);
236
  render.mouse = mouse;
237
  hideEditPanel();
238
  hideConnectionPanel();
 
239
  });
240
 
241
+ // Edit Panel functionsopen on double–click
242
  function openEditPanel(body) {
243
  selectedBody = body;
244
  let html = `<h3>Edit Element</h3>
245
+ <label>Position X:</label>
246
  <input type="number" id="editPosX" value="${body.position.x.toFixed(2)}">
247
+ <label>Position Y:</label>
248
  <input type="number" id="editPosY" value="${body.position.y.toFixed(2)}">
249
+ <label>Angle (radians):</label>
250
  <input type="number" id="editAngle" value="${body.angle.toFixed(2)}">
251
+ <label>Color:</label>
252
+ <input type="color" id="editColor" value="${body.render.fillStyle || '#ffffff'}">
253
+ <button id="updateElement">Update</button>
254
+ <button id="deleteElement">Delete</button>
255
+ <button id="attachElement">Attach</button>`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  editPanel.innerHTML = html;
257
  editPanel.style.display = "block";
258
  document.getElementById("updateElement").addEventListener("click", updateElementFromEditPanel);
259
  document.getElementById("deleteElement").addEventListener("click", function(){
260
+ World.remove(world, selectedBody);
261
+ hideEditPanel();
262
+ });
263
+ document.getElementById("attachElement").addEventListener("click", function(){
264
+ attachMode = true;
265
+ attachFrom = selectedBody;
266
+ hideEditPanel();
267
+ connectionPanel.style.display = "block";
268
  });
269
  }
270
  function hideEditPanel() {
 
280
  Body.setPosition(selectedBody, { x: newX, y: newY });
281
  Body.setAngle(selectedBody, newAngle);
282
  selectedBody.render.fillStyle = newColor;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  hideEditPanel();
284
  }
285
+ // Open edit panel on double–click (using canvas dblclick)
 
286
  render.canvas.addEventListener("dblclick", function(event){
287
  const rect = render.canvas.getBoundingClientRect();
288
  const mousePos = { x: event.clientX - rect.left, y: event.clientY - rect.top };
289
  const bodies = Matter.Composite.allBodies(world);
290
  const clicked = Matter.Query.point(bodies, mousePos);
291
+ if(clicked.length > 0) { openEditPanel(clicked[0]); }
292
+ else { hideEditPanel(); }
 
 
 
293
  });
294
 
295
+ // Attachment (Connection) creation:
296
+ // When attachMode is true and user clicks on a target body,
297
+ // create a constraint between attachFrom and that body using selected connection type.
298
+ Events.on(mouseConstraint, "mouseup", function(event){
 
 
 
299
  const mousePos = event.mouse.position;
300
  const bodies = Matter.Composite.allBodies(world);
301
  const clickedBodies = Matter.Query.point(bodies, mousePos);
302
+ if(attachMode && attachFrom && clickedBodies.length > 0) {
303
+ const target = clickedBodies[0];
304
+ if(target === attachFrom) return;
305
+ const connType = document.getElementById("connType").value;
306
+ // Default endpoints: attach from centers
307
+ const pointA = { x: 0, y: 0 };
308
+ const pointB = { x: 0, y: 0 };
309
+ // Compute current distance between centers
310
+ const dx = target.position.x - attachFrom.position.x;
311
+ const dy = target.position.y - attachFrom.position.y;
312
+ const length = Math.sqrt(dx*dx + dy*dy);
313
+ // Set stiffness based on connection type:
314
+ let stiffness = (connType === "spring") ? 0.05 : 1;
315
+ let damping = (connType === "spring") ? 0.05 : 0;
316
+ const newConstraint = Constraint.create({
317
+ bodyA: attachFrom,
318
+ pointA: pointA,
319
+ bodyB: target,
320
+ pointB: pointB,
321
+ length: length,
322
+ stiffness: stiffness,
323
+ damping: damping,
324
+ render: { strokeStyle: '#000', lineWidth: 2 }
325
+ });
326
+ World.add(world, newConstraint);
327
+ attachMode = false;
328
+ attachFrom = null;
329
+ connectionPanel.style.display = "none";
330
+ alert("Attachment created (" + connType + ").");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  }
332
  });
333
+ document.getElementById("cancelConn").addEventListener("click", function(){
334
+ connectionPanel.style.display = "none";
335
+ attachMode = false;
336
+ attachFrom = null;
 
337
  });
338
 
339
+ // Update boundaries and renderer on window resize
340
+ window.addEventListener("resize", function(){
341
  Render.lookAt(render, { min: { x: 0, y: 0 }, max: { x: window.innerWidth, y: window.innerHeight } });
342
  updateBoundaries();
343
  });