Ramesh-vani commited on
Commit
71cfcfe
·
verified ·
1 Parent(s): 1ab6ab7

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +161 -109
index.html CHANGED
@@ -2,77 +2,76 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
- <title>Advanced Physics Simulator – Connect Systems & More Elements</title>
6
  <style>
7
  body { margin: 0; overflow: hidden; font-family: Arial, sans-serif; }
 
8
  canvas { display: block; }
9
- /* Menus always on top */
10
- #controls, #editPanel, #connectionPanel, #constraintEditPanel {
11
- position: absolute;
12
- background: rgba(255,255,255,0.95);
13
- padding: 10px;
14
- border-radius: 5px;
15
- z-index: 1000;
16
- }
17
- #controls {
18
- top: 10px;
19
- left: 10px;
20
- max-width: 320px;
21
- max-height: 90vh;
22
- overflow-y: auto;
23
  }
24
- #controls label { display: block; margin-top: 8px; }
25
- #controls input, #controls select, #controls button {
26
- width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
27
  }
28
- #buttonContainer {
29
- display: flex;
30
- justify-content: space-between;
31
- gap: 5px;
 
 
 
 
 
 
32
  }
 
33
  #editPanel {
34
- top: 10px;
 
35
  right: 10px;
 
 
 
 
 
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 Options Panel */
47
- #connectionPanel {
48
- top: 150px;
49
- right: 10px;
50
- max-width: 250px;
51
- display: none;
52
- }
53
- #connectionPanel h3 { margin: 0 0 5px 0; }
54
- #connectionPanel label { display: block; margin-top: 8px; }
55
- #connectionPanel input, #connectionPanel select, #connectionPanel button {
56
- width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
57
- }
58
- /* Constraint Edit Panel */
59
- #constraintEditPanel {
60
- bottom: 10px;
61
- right: 10px;
62
  max-width: 250px;
63
  display: none;
64
  }
65
- #constraintEditPanel h3 { margin: 0 0 5px 0; }
66
- #constraintEditPanel label { display: block; margin-top: 8px; }
67
- #constraintEditPanel input, #constraintEditPanel select, #constraintEditPanel button {
68
- width: 100%; padding: 6px; margin-top: 4px; box-sizing: border-box;
69
- }
70
  </style>
71
  </head>
72
  <body>
73
- <!-- Controls Panel -->
74
- <div id="controls">
75
- <label for="simulationSelect">Choose Simulation Element:</label>
76
  <select id="simulationSelect">
77
  <option value="bouncingBall">Bouncing Ball</option>
78
  <option value="pendulum">Pendulum</option>
@@ -80,19 +79,20 @@
80
  <option value="inclinedPlane">Inclined Plane</option>
81
  <option value="springMass">Spring–Mass System</option>
82
  <option value="rectangleBlock">Rectangle Block</option>
 
 
83
  </select>
84
- <!-- Dynamic Suboptions for creation parameters -->
85
- <div id="subOptionsContainer"></div>
86
- <div id="buttonContainer">
87
- <button id="addElement">Add Element</button>
88
- <button id="connectSystems">Connect Systems</button>
89
- <button id="reset">Reset Simulation</button>
90
- </div>
91
  </div>
92
 
 
 
 
93
  <!-- Edit Panel (opens on double-click on an element) -->
94
  <div id="editPanel"></div>
95
-
96
  <!-- Connection Options Panel -->
97
  <div id="connectionPanel">
98
  <h3>Connection Options</h3>
@@ -107,7 +107,6 @@
107
  <option value="element">Element</option>
108
  <option value="system">System</option>
109
  </select>
110
- <!-- Show endpoint options only for springMass systems -->
111
  <div id="sourceEndpointDiv">
112
  <label for="sourceEndpoint">Source Endpoint:</label>
113
  <select id="sourceEndpoint">
@@ -116,9 +115,9 @@
116
  </select>
117
  </div>
118
  <button id="cancelConn">Cancel</button>
119
- <p style="font-size: 12px; color: #555;">Now click on the target element to attach.</p>
120
  </div>
121
-
122
  <!-- Constraint Edit Panel -->
123
  <div id="constraintEditPanel">
124
  <h3>Edit Constraint</h3>
@@ -139,11 +138,11 @@
139
  <button id="updateConstraint">Update Constraint</button>
140
  <button id="deleteConstraint">Delete Constraint</button>
141
  </div>
142
-
143
  <!-- Matter.js Library -->
144
  <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
145
  <script>
146
- // Module aliases and Engine Setup
147
  const Engine = Matter.Engine,
148
  Render = Matter.Render,
149
  Runner = Matter.Runner,
@@ -154,7 +153,7 @@
154
  Mouse = Matter.Mouse,
155
  MouseConstraint = Matter.MouseConstraint,
156
  Events = Matter.Events;
157
-
158
  const engine = Engine.create();
159
  const world = engine.world;
160
  const render = Render.create({
@@ -171,13 +170,13 @@
171
  const runner = Runner.create();
172
  Runner.run(runner, engine);
173
 
174
- // Global boundaries (to be updated on resize)
175
  let floor, wallLeft, wallRight;
176
  function updateBoundaries() {
177
- if (floor && wallLeft && wallRight) {
178
  World.remove(world, [floor, wallLeft, wallRight]);
179
  }
180
- floor = Bodies.rectangle(window.innerWidth/2, window.innerHeight-50, window.innerWidth, 100, {
181
  isStatic: true,
182
  render: { fillStyle: '#060a19' }
183
  });
@@ -187,7 +186,7 @@
187
  }
188
  updateBoundaries();
189
 
190
- // Mouse control for drag & drop
191
  let mouse = Mouse.create(render.canvas);
192
  let mouseConstraint = MouseConstraint.create(engine, {
193
  mouse: mouse,
@@ -196,10 +195,10 @@
196
  World.add(world, mouseConstraint);
197
  render.mouse = mouse;
198
 
199
- // Global variables for editing & connection
200
  let selectedBody = null;
201
  let selectedConstraint = null;
202
- let attachMode = false; // if true, next click will pick connection target
203
  let attachFrom = null;
204
  const connectionPanel = document.getElementById('connectionPanel');
205
  const editPanel = document.getElementById('editPanel');
@@ -318,6 +317,40 @@
318
  <label for="blockColor">Color:</label>
319
  <input type="color" id="blockColor" value="#2ecc71">
320
  `;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  }
322
  subOptionsContainer.innerHTML = html;
323
  }
@@ -435,6 +468,36 @@
435
  block.customOptions = { x, y, width, height, friction, restitution, color, isSystemRoot: false };
436
  World.add(world, block);
437
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  document.getElementById('addElement').addEventListener('click', function(){
439
  const selected = document.getElementById('simulationSelect').value;
440
  if(selected === 'bouncingBall') addBouncingBall();
@@ -443,13 +506,14 @@
443
  else if(selected === 'inclinedPlane') addInclinedPlane();
444
  else if(selected === 'springMass') addSpringMass();
445
  else if(selected === 'rectangleBlock') addRectangleBlock();
 
 
446
  });
447
- // "Connect Systems" button (sets attach mode in SYSTEM mode)
448
  document.getElementById('connectSystems').addEventListener('click', function(){
449
  attachMode = true;
450
- attachFrom = null; // will be set on first click
451
  connectionPanel.style.display = "block";
452
- // Set connection mode to system by default
453
  document.getElementById('connMode').value = "system";
454
  alert("System connection mode activated. Click on the first system element (must be marked as system root) then the target system element.");
455
  });
@@ -465,8 +529,8 @@
465
  hideConnectionPanel();
466
  hideConstraintEditPanel();
467
  });
468
-
469
- /* ---------- Edit Panel (opens on double-click) ---------- */
470
  function openEditPanel(body) {
471
  selectedBody = body;
472
  let html = `<h3>Edit Element</h3>
@@ -489,7 +553,7 @@
489
  <label for="editBallFriction">Friction:</label>
490
  <input type="number" id="editBallFriction" value="${opts.friction}" step="0.001" min="0" max="1">`;
491
  }
492
- // (Additional type-specific options can be added similarly for other element types)
493
  html += `<div style="margin-top:10px;">
494
  <button id="updateElement">Update</button>
495
  <button id="deleteElement">Delete</button>
@@ -515,7 +579,6 @@
515
  Body.setPosition(selectedBody, { x: newX, y: newY });
516
  Body.setAngle(selectedBody, newAngle);
517
  selectedBody.render.fillStyle = newColor;
518
- // Update the "isSystemRoot" flag
519
  const isSysRoot = document.getElementById("editSystemRoot").checked;
520
  selectedBody.customOptions.isSystemRoot = isSysRoot;
521
  if(selectedBody.elementType === "bouncingBall"){
@@ -533,8 +596,6 @@
533
  }
534
  hideEditPanel();
535
  }
536
-
537
- // Open edit panel on double-click
538
  render.canvas.addEventListener("dblclick", function(event){
539
  const rect = render.canvas.getBoundingClientRect();
540
  const mousePos = { x: event.clientX - rect.left, y: event.clientY - rect.top };
@@ -546,9 +607,8 @@
546
  hideEditPanel();
547
  }
548
  });
549
-
550
- /* ---------- Connection (Attachment) ---------- */
551
- // In attach mode, when clicking on elements, create a constraint
552
  Events.on(mouseConstraint, 'mouseup', function(event){
553
  const mousePos = event.mouse.position;
554
  const bodies = Matter.Composite.allBodies(world);
@@ -557,19 +617,16 @@
557
  const clickedBody = clickedBodies[0];
558
  if(attachMode){
559
  if(!attachFrom){
560
- // First selection for attachment
561
  attachFrom = clickedBody;
562
  alert("First element selected. Now click on the target element.");
563
  return;
564
- } else if (clickedBody === attachFrom) {
565
  alert("Please select a different element.");
566
  return;
567
  }
568
- // Get connection options from the connection panel
569
  const connType = document.getElementById('connType').value;
570
  const connMode = document.getElementById('connMode').value;
571
- // If connection mode is "system", verify both elements are marked as system root
572
- if(connMode === "system") {
573
  if(!attachFrom.customOptions.isSystemRoot){
574
  alert("Source element is not marked as System Root. Please mark it in the edit panel.");
575
  attachMode = false;
@@ -584,19 +641,16 @@
584
  connectionPanel.style.display = "none";
585
  return;
586
  }
587
- // Use the system root's position (we assume the element itself is the root)
588
  }
589
- // For springMass endpoints, get endpoint options if needed
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("For target springMass, choose 'fixed' or 'mass' (default: mass):", "mass") || "mass";
597
- if(targetEndpoint !== "fixed") { targetEndpoint = "mass"; }
598
  }
599
- // Determine attachment points
600
  let pointA = { x: 0, y: 0 }, bodyA = attachFrom;
601
  if(attachFrom.elementType === "springMass" && sourceEndpoint === "fixed"){
602
  bodyA = null;
@@ -607,8 +661,7 @@
607
  bodyB = null;
608
  pointB = clickedBody.fixedPoint || { x: clickedBody.position.x, y: clickedBody.position.y };
609
  }
610
- // If connection mode is "system", override to use the elements as-is
611
- if(connMode === "system") {
612
  bodyA = attachFrom;
613
  bodyB = clickedBody;
614
  pointA = { x: 0, y: 0 };
@@ -635,21 +688,20 @@
635
  alert("Attachment created using " + connType + " connection in " + connMode + " mode.");
636
  }
637
  } else {
638
- // Check if near a constraint line for editing
639
  const constraints = Matter.Composite.allConstraints(world);
640
  for(let cons of constraints){
641
- let posA = cons.bodyA ? { x: cons.bodyA.position.x + cons.pointA.x, y: cons.bodyA.position.y + cons.pointA.y } : cons.pointA;
642
- let posB = cons.bodyB ? { x: cons.bodyB.position.x + cons.pointB.x, y: cons.bodyB.position.y + cons.pointB.y } : cons.pointB;
643
- let dist = distanceToSegment(mousePos, posA, posB);
644
- if(dist < 5){
645
- showConstraintEditPanel(cons);
646
- return;
647
- }
648
  }
649
  hideConstraintEditPanel();
650
  }
651
  });
652
- // Helper: distance from point to segment
653
  function distanceToSegment(p, v, w){
654
  let l2 = (w.x - v.x)**2 + (w.y - v.y)**2;
655
  if(l2 === 0) return Math.hypot(p.x - v.x, p.y - v.y);
@@ -658,7 +710,7 @@
658
  let proj = { x: v.x + t*(w.x-v.x), y: v.y + t*(w.y-v.y) };
659
  return Math.hypot(p.x - proj.x, p.y - proj.y);
660
  }
661
-
662
  /* ---------- Constraint Edit Panel ---------- */
663
  const constraintEditPanel = document.getElementById('constraintEditPanel');
664
  function showConstraintEditPanel(cons){
@@ -696,7 +748,7 @@
696
  hideConstraintEditPanel();
697
  }
698
  });
699
-
700
  // Window resize: update renderer and boundaries
701
  window.addEventListener('resize', function(){
702
  Render.lookAt(render, { min: { x: 0, y: 0 }, max: { x: window.innerWidth, y: window.innerHeight } });
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <title>Advanced Physics Simulator – Broad Elements Menu</title>
6
  <style>
7
  body { margin: 0; overflow: hidden; font-family: Arial, sans-serif; }
8
+ /* The canvas fills the viewport */
9
  canvas { display: block; }
10
+ /* Top horizontal menu bar (outside the canvas) */
11
+ #topMenu {
12
+ position: fixed;
13
+ top: 0;
14
+ left: 0;
15
+ right: 0;
16
+ background: #ececec;
17
+ border-bottom: 1px solid #ccc;
18
+ padding: 5px;
19
+ z-index: 2000;
20
+ display: flex;
21
+ align-items: center;
22
+ gap: 15px;
 
23
  }
24
+ #topMenu select, #topMenu button {
25
+ padding: 5px 10px;
26
+ font-size: 14px;
27
  }
28
+ /* A container below the menu for additional options */
29
+ #subOptionsContainer {
30
+ position: fixed;
31
+ top: 45px;
32
+ left: 10px;
33
+ z-index: 2000;
34
+ background: rgba(255,255,255,0.95);
35
+ padding: 10px;
36
+ border: 1px solid #ccc;
37
+ border-radius: 4px;
38
  }
39
+ /* Edit Panel (floating, triggered on double-click) */
40
  #editPanel {
41
+ position: fixed;
42
+ top: 45px;
43
  right: 10px;
44
+ z-index: 2000;
45
+ background: rgba(255,255,255,0.95);
46
+ padding: 10px;
47
+ border: 1px solid #ccc;
48
+ border-radius: 4px;
49
  max-width: 300px;
50
+ max-height: 80vh;
51
  overflow-y: auto;
52
  display: none;
53
  }
54
+ /* Connection and Constraint panels (similar styling) */
55
+ #connectionPanel, #constraintEditPanel {
56
+ position: fixed;
57
+ z-index: 2000;
58
+ background: rgba(255,255,255,0.95);
59
+ padding: 10px;
60
+ border: 1px solid #ccc;
61
+ border-radius: 4px;
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  max-width: 250px;
63
  display: none;
64
  }
65
+ #connectionPanel { top: 45px; left: 50%; transform: translateX(-50%); }
66
+ #constraintEditPanel { bottom: 10px; right: 10px; }
67
+ /* Common styling for labels/inputs */
68
+ label { display: block; margin-top: 8px; font-size: 13px; }
69
+ input, select, button { width: 100%; padding: 5px; margin-top: 4px; font-size: 13px; }
70
  </style>
71
  </head>
72
  <body>
73
+ <!-- Top horizontal menu bar (outside the canvas) -->
74
+ <div id="topMenu">
 
75
  <select id="simulationSelect">
76
  <option value="bouncingBall">Bouncing Ball</option>
77
  <option value="pendulum">Pendulum</option>
 
79
  <option value="inclinedPlane">Inclined Plane</option>
80
  <option value="springMass">Spring–Mass System</option>
81
  <option value="rectangleBlock">Rectangle Block</option>
82
+ <option value="rotatingRectangle">Rotating Rectangle</option>
83
+ <option value="triangle">Triangle</option>
84
  </select>
85
+ <button id="addElement">Add Element</button>
86
+ <button id="connectSystems">Connect Systems</button>
87
+ <button id="reset">Reset Simulation</button>
 
 
 
 
88
  </div>
89
 
90
+ <!-- Container for dynamic suboptions -->
91
+ <div id="subOptionsContainer"></div>
92
+
93
  <!-- Edit Panel (opens on double-click on an element) -->
94
  <div id="editPanel"></div>
95
+
96
  <!-- Connection Options Panel -->
97
  <div id="connectionPanel">
98
  <h3>Connection Options</h3>
 
107
  <option value="element">Element</option>
108
  <option value="system">System</option>
109
  </select>
 
110
  <div id="sourceEndpointDiv">
111
  <label for="sourceEndpoint">Source Endpoint:</label>
112
  <select id="sourceEndpoint">
 
115
  </select>
116
  </div>
117
  <button id="cancelConn">Cancel</button>
118
+ <p style="font-size: 12px; color: #555;">Click on the target element to attach.</p>
119
  </div>
120
+
121
  <!-- Constraint Edit Panel -->
122
  <div id="constraintEditPanel">
123
  <h3>Edit Constraint</h3>
 
138
  <button id="updateConstraint">Update Constraint</button>
139
  <button id="deleteConstraint">Delete Constraint</button>
140
  </div>
141
+
142
  <!-- Matter.js Library -->
143
  <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
144
  <script>
145
+ // Module aliases and engine setup
146
  const Engine = Matter.Engine,
147
  Render = Matter.Render,
148
  Runner = Matter.Runner,
 
153
  Mouse = Matter.Mouse,
154
  MouseConstraint = Matter.MouseConstraint,
155
  Events = Matter.Events;
156
+
157
  const engine = Engine.create();
158
  const world = engine.world;
159
  const render = Render.create({
 
170
  const runner = Runner.create();
171
  Runner.run(runner, engine);
172
 
173
+ // Boundaries (responsive)
174
  let floor, wallLeft, wallRight;
175
  function updateBoundaries() {
176
+ if(floor && wallLeft && wallRight) {
177
  World.remove(world, [floor, wallLeft, wallRight]);
178
  }
179
+ floor = Bodies.rectangle(window.innerWidth/2, window.innerHeight - 50, window.innerWidth, 100, {
180
  isStatic: true,
181
  render: { fillStyle: '#060a19' }
182
  });
 
186
  }
187
  updateBoundaries();
188
 
189
+ // Mouse control
190
  let mouse = Mouse.create(render.canvas);
191
  let mouseConstraint = MouseConstraint.create(engine, {
192
  mouse: mouse,
 
195
  World.add(world, mouseConstraint);
196
  render.mouse = mouse;
197
 
198
+ // Global variables for editing and connection
199
  let selectedBody = null;
200
  let selectedConstraint = null;
201
+ let attachMode = false;
202
  let attachFrom = null;
203
  const connectionPanel = document.getElementById('connectionPanel');
204
  const editPanel = document.getElementById('editPanel');
 
317
  <label for="blockColor">Color:</label>
318
  <input type="color" id="blockColor" value="#2ecc71">
319
  `;
320
+ } else if(selected === 'rotatingRectangle'){
321
+ html += `
322
+ <label for="rotRectInitX">Initial X:</label>
323
+ <input type="number" id="rotRectInitX" value="200">
324
+ <label for="rotRectInitY">Initial Y:</label>
325
+ <input type="number" id="rotRectInitY" value="200">
326
+ <label for="rotRectWidth">Width:</label>
327
+ <input type="number" id="rotRectWidth" value="100">
328
+ <label for="rotRectHeight">Height:</label>
329
+ <input type="number" id="rotRectHeight" value="50">
330
+ <label for="rotRectRotationSpeed">Rotation Speed (rad/s):</label>
331
+ <input type="number" id="rotRectRotationSpeed" value="0.05" step="0.01">
332
+ <label for="rotRectFriction">Friction:</label>
333
+ <input type="number" id="rotRectFriction" value="0.1" step="0.01" min="0" max="1">
334
+ <label for="rotRectRestitution">Restitution:</label>
335
+ <input type="number" id="rotRectRestitution" value="0.3" step="0.1" min="0" max="1">
336
+ <label for="rotRectColor">Color:</label>
337
+ <input type="color" id="rotRectColor" value="#9b59b6">
338
+ `;
339
+ } else if(selected === 'triangle'){
340
+ html += `
341
+ <label for="triInitX">Initial X:</label>
342
+ <input type="number" id="triInitX" value="300">
343
+ <label for="triInitY">Initial Y:</label>
344
+ <input type="number" id="triInitY" value="300">
345
+ <label for="triSize">Size (radius):</label>
346
+ <input type="number" id="triSize" value="40">
347
+ <label for="triFriction">Friction:</label>
348
+ <input type="number" id="triFriction" value="0.1" step="0.01" min="0" max="1">
349
+ <label for="triRestitution">Restitution:</label>
350
+ <input type="number" id="triRestitution" value="0.3" step="0.1" min="0" max="1">
351
+ <label for="triColor">Color:</label>
352
+ <input type="color" id="triColor" value="#e74c3c">
353
+ `;
354
  }
355
  subOptionsContainer.innerHTML = html;
356
  }
 
468
  block.customOptions = { x, y, width, height, friction, restitution, color, isSystemRoot: false };
469
  World.add(world, block);
470
  }
471
+ function addRotatingRectangle(){
472
+ const x = parseFloat(document.getElementById("rotRectInitX").value);
473
+ const y = parseFloat(document.getElementById("rotRectInitY").value);
474
+ const width = parseFloat(document.getElementById("rotRectWidth").value);
475
+ const height = parseFloat(document.getElementById("rotRectHeight").value);
476
+ const rotationSpeed = parseFloat(document.getElementById("rotRectRotationSpeed").value);
477
+ const friction = parseFloat(document.getElementById("rotRectFriction").value);
478
+ const restitution = parseFloat(document.getElementById("rotRectRestitution").value);
479
+ const color = document.getElementById("rotRectColor").value || getRandomColor();
480
+ const rect = Bodies.rectangle(x, y, width, height, { friction, restitution, render: { fillStyle: color } });
481
+ rect.elementType = "rotatingRectangle";
482
+ rect.customOptions = { x, y, width, height, rotationSpeed, friction, restitution, color, isSystemRoot: false };
483
+ // Continuously rotate the rectangle
484
+ Events.on(engine, "beforeUpdate", function(){
485
+ Body.rotate(rect, rotationSpeed);
486
+ });
487
+ World.add(world, rect);
488
+ }
489
+ function addTriangle(){
490
+ const x = parseFloat(document.getElementById("triInitX").value);
491
+ const y = parseFloat(document.getElementById("triInitY").value);
492
+ const radius = parseFloat(document.getElementById("triSize").value);
493
+ const friction = parseFloat(document.getElementById("triFriction").value);
494
+ const restitution = parseFloat(document.getElementById("triRestitution").value);
495
+ const color = document.getElementById("triColor").value || getRandomColor();
496
+ const tri = Bodies.polygon(x, y, 3, radius, { friction, restitution, render: { fillStyle: color } });
497
+ tri.elementType = "triangle";
498
+ tri.customOptions = { x, y, radius, friction, restitution, color, isSystemRoot: false };
499
+ World.add(world, tri);
500
+ }
501
  document.getElementById('addElement').addEventListener('click', function(){
502
  const selected = document.getElementById('simulationSelect').value;
503
  if(selected === 'bouncingBall') addBouncingBall();
 
506
  else if(selected === 'inclinedPlane') addInclinedPlane();
507
  else if(selected === 'springMass') addSpringMass();
508
  else if(selected === 'rectangleBlock') addRectangleBlock();
509
+ else if(selected === 'rotatingRectangle') addRotatingRectangle();
510
+ else if(selected === 'triangle') addTriangle();
511
  });
512
+ // "Connect Systems" button to initiate attach mode (for connecting elements or systems)
513
  document.getElementById('connectSystems').addEventListener('click', function(){
514
  attachMode = true;
515
+ attachFrom = null;
516
  connectionPanel.style.display = "block";
 
517
  document.getElementById('connMode').value = "system";
518
  alert("System connection mode activated. Click on the first system element (must be marked as system root) then the target system element.");
519
  });
 
529
  hideConnectionPanel();
530
  hideConstraintEditPanel();
531
  });
532
+
533
+ /* ---------- Edit Panel Functions (Double-click on element) ---------- */
534
  function openEditPanel(body) {
535
  selectedBody = body;
536
  let html = `<h3>Edit Element</h3>
 
553
  <label for="editBallFriction">Friction:</label>
554
  <input type="number" id="editBallFriction" value="${opts.friction}" step="0.001" min="0" max="1">`;
555
  }
556
+ // Additional type-specific options can be added similarly
557
  html += `<div style="margin-top:10px;">
558
  <button id="updateElement">Update</button>
559
  <button id="deleteElement">Delete</button>
 
579
  Body.setPosition(selectedBody, { x: newX, y: newY });
580
  Body.setAngle(selectedBody, newAngle);
581
  selectedBody.render.fillStyle = newColor;
 
582
  const isSysRoot = document.getElementById("editSystemRoot").checked;
583
  selectedBody.customOptions.isSystemRoot = isSysRoot;
584
  if(selectedBody.elementType === "bouncingBall"){
 
596
  }
597
  hideEditPanel();
598
  }
 
 
599
  render.canvas.addEventListener("dblclick", function(event){
600
  const rect = render.canvas.getBoundingClientRect();
601
  const mousePos = { x: event.clientX - rect.left, y: event.clientY - rect.top };
 
607
  hideEditPanel();
608
  }
609
  });
610
+
611
+ /* ---------- Connection / Attachment ---------- */
 
612
  Events.on(mouseConstraint, 'mouseup', function(event){
613
  const mousePos = event.mouse.position;
614
  const bodies = Matter.Composite.allBodies(world);
 
617
  const clickedBody = clickedBodies[0];
618
  if(attachMode){
619
  if(!attachFrom){
 
620
  attachFrom = clickedBody;
621
  alert("First element selected. Now click on the target element.");
622
  return;
623
+ } else if(clickedBody === attachFrom){
624
  alert("Please select a different element.");
625
  return;
626
  }
 
627
  const connType = document.getElementById('connType').value;
628
  const connMode = document.getElementById('connMode').value;
629
+ if(connMode === "system"){
 
630
  if(!attachFrom.customOptions.isSystemRoot){
631
  alert("Source element is not marked as System Root. Please mark it in the edit panel.");
632
  attachMode = false;
 
641
  connectionPanel.style.display = "none";
642
  return;
643
  }
 
644
  }
 
645
  let sourceEndpoint = "mass";
646
  if(attachFrom.elementType === "springMass"){
647
+ sourceEndpoint = document.getElementById('sourceEndpoint').value;
648
  }
649
  let targetEndpoint = "mass";
650
  if(clickedBody.elementType === "springMass"){
651
+ targetEndpoint = prompt("For target springMass, choose 'fixed' or 'mass' (default: mass):", "mass") || "mass";
652
+ if(targetEndpoint !== "fixed") { targetEndpoint = "mass"; }
653
  }
 
654
  let pointA = { x: 0, y: 0 }, bodyA = attachFrom;
655
  if(attachFrom.elementType === "springMass" && sourceEndpoint === "fixed"){
656
  bodyA = null;
 
661
  bodyB = null;
662
  pointB = clickedBody.fixedPoint || { x: clickedBody.position.x, y: clickedBody.position.y };
663
  }
664
+ if(connMode === "system"){
 
665
  bodyA = attachFrom;
666
  bodyB = clickedBody;
667
  pointA = { x: 0, y: 0 };
 
688
  alert("Attachment created using " + connType + " connection in " + connMode + " mode.");
689
  }
690
  } else {
691
+ // Check if near a constraint for editing
692
  const constraints = Matter.Composite.allConstraints(world);
693
  for(let cons of constraints){
694
+ let posA = cons.bodyA ? { x: cons.bodyA.position.x + cons.pointA.x, y: cons.bodyA.position.y + cons.pointA.y } : cons.pointA;
695
+ let posB = cons.bodyB ? { x: cons.bodyB.position.x + cons.pointB.x, y: cons.bodyB.position.y + cons.pointB.y } : cons.pointB;
696
+ let dist = distanceToSegment(mousePos, posA, posB);
697
+ if(dist < 5){
698
+ showConstraintEditPanel(cons);
699
+ return;
700
+ }
701
  }
702
  hideConstraintEditPanel();
703
  }
704
  });
 
705
  function distanceToSegment(p, v, w){
706
  let l2 = (w.x - v.x)**2 + (w.y - v.y)**2;
707
  if(l2 === 0) return Math.hypot(p.x - v.x, p.y - v.y);
 
710
  let proj = { x: v.x + t*(w.x-v.x), y: v.y + t*(w.y-v.y) };
711
  return Math.hypot(p.x - proj.x, p.y - proj.y);
712
  }
713
+
714
  /* ---------- Constraint Edit Panel ---------- */
715
  const constraintEditPanel = document.getElementById('constraintEditPanel');
716
  function showConstraintEditPanel(cons){
 
748
  hideConstraintEditPanel();
749
  }
750
  });
751
+
752
  // Window resize: update renderer and boundaries
753
  window.addEventListener('resize', function(){
754
  Render.lookAt(render, { min: { x: 0, y: 0 }, max: { x: window.innerWidth, y: window.innerHeight } });