Spaces:
Running
Running
Update index.html
Browse files- index.html +398 -221
index.html
CHANGED
@@ -1,130 +1,126 @@
|
|
1 |
<!DOCTYPE html>
|
2 |
-
<html lang="
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
-
<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:
|
18 |
-
border-radius:
|
19 |
-
max-width:
|
20 |
max-height: 90vh;
|
21 |
overflow-y: auto;
|
|
|
22 |
}
|
23 |
-
#controls label { display: block; margin-
|
24 |
#controls input, #controls select, #controls button {
|
25 |
-
width: 100%; padding:
|
26 |
-
}
|
27 |
-
|
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:
|
35 |
-
border-radius:
|
36 |
max-width: 300px;
|
37 |
max-height: 90vh;
|
38 |
overflow-y: auto;
|
|
|
39 |
display: none;
|
40 |
}
|
41 |
-
#editPanel
|
42 |
-
#
|
43 |
-
#
|
44 |
-
|
45 |
-
|
46 |
-
/* Connection Panel – for attachments */
|
47 |
-
#connectionPanel {
|
48 |
position: absolute;
|
49 |
-
|
50 |
-
|
51 |
z-index: 1000;
|
52 |
background: rgba(255,255,255,0.95);
|
53 |
padding: 10px;
|
54 |
-
border-radius:
|
55 |
-
max-width:
|
56 |
-
|
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="
|
69 |
-
<select id="
|
70 |
-
<option value="
|
71 |
-
<option value="
|
|
|
|
|
|
|
72 |
</select>
|
73 |
-
<
|
74 |
-
<
|
75 |
-
|
76 |
-
|
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>
|
101 |
<label for="connType">Connection Type:</label>
|
102 |
<select id="connType">
|
103 |
-
<option value="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
<option value="string">String</option>
|
|
|
|
|
105 |
</select>
|
106 |
-
<button id="
|
107 |
-
<
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
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: '#
|
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 |
-
|
147 |
-
}
|
148 |
-
|
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 |
-
|
159 |
-
|
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 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
|
174 |
-
|
175 |
-
function getRandomColor() {
|
176 |
-
return '#' + Math.floor(Math.random()*16777215).toString(16);
|
177 |
-
}
|
178 |
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
}
|
189 |
-
|
|
|
|
|
|
|
190 |
|
191 |
-
|
192 |
-
|
193 |
-
const
|
194 |
-
const
|
195 |
-
const
|
196 |
-
const
|
197 |
-
const color = document.getElementById("
|
198 |
-
const
|
199 |
-
|
200 |
-
|
201 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
202 |
});
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
207 |
}
|
208 |
-
|
209 |
-
|
210 |
-
const
|
211 |
-
const
|
212 |
-
const
|
213 |
-
const
|
214 |
-
|
215 |
-
|
216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
});
|
218 |
-
|
219 |
-
|
220 |
-
|
|
|
221 |
}
|
222 |
-
|
223 |
-
|
224 |
-
|
|
|
|
|
|
|
|
|
|
|
225 |
});
|
226 |
-
|
|
|
227 |
World.clear(world);
|
228 |
Engine.clear(engine);
|
229 |
updateBoundaries();
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
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="
|
247 |
-
<label>
|
248 |
-
<input type="
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
256 |
editPanel.innerHTML = html;
|
257 |
editPanel.style.display = "block";
|
258 |
document.getElementById("updateElement").addEventListener("click", updateElementFromEditPanel);
|
259 |
-
document.getElementById("deleteElement").addEventListener("click",
|
260 |
-
|
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 |
-
|
275 |
-
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
286 |
-
render.canvas.addEventListener("dblclick",
|
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)
|
292 |
-
else
|
293 |
});
|
294 |
|
295 |
-
|
296 |
-
|
297 |
-
|
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
|
303 |
-
const
|
304 |
-
|
305 |
-
const
|
306 |
-
|
307 |
-
|
308 |
-
const
|
309 |
-
|
310 |
-
const
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
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,
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
331 |
}
|
332 |
});
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
337 |
});
|
338 |
|
339 |
-
|
340 |
-
|
|
|
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>
|