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

Update index.html

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