-
Notifications
You must be signed in to change notification settings - Fork 0
/
ui_events.js
217 lines (183 loc) · 7.94 KB
/
ui_events.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// Description: This file contains all the event handlers for the UI elements
class UIEvents {
static documentLoaded() {
// Simulation speed slider
this.simulationSpeedSlider = document.getElementById(
"simulationSpeedSlider"
);
// Elasticity value slider
this.elasticityValueSlider = document.getElementById(
"elasticityValueSlider"
);
// Circle number input box
this.circlesNumInput = document.getElementById("circlesNumInput");
// Body data layout template
this.bodyDataLayoutTemplate = document.getElementById("bodyDataLayout");
// Number of bodies input box
this.bodiesNumberInput = document.getElementById("circlesNumInput");
// Data page
this.dataPage = document.getElementById("dataPage");
}
// Event handler for when the user clicks the play/pause button
static playPauseBtnClicked() {
// Toggle play/pause
play = !play;
// Define play/pause icons
const playIcon = document.getElementById("playIcon");
const pauseIcon = document.getElementById("pauseIcon");
// Update play/pause button icon
if (play) {
playIcon.classList.add("hidden");
pauseIcon.classList.remove("hidden");
} else {
playIcon.classList.remove("hidden");
pauseIcon.classList.add("hidden");
}
}
// Event handler for when the user clicks the forward one frame button
static forwardOneFrameBtnClicked() {
// Play one frame for certain amount of time
play = !play;
setTimeout(() => {
play = !play;
}, 1000 / 60);
}
// Event handler for when the user changes the simulation speed slider
static simulationSpeedChanged() {
simSpeed = parseFloat(this.simulationSpeedSlider.value);
}
// Event handler for when the user changes the elasticity value slider
static elasticityValueChanged() {
elasticity = parseInt(this.elasticityValueSlider.value);
}
// Event handler for when the user changes the number of bodies
static numCirclesChanged() {
let element = this.circlesNumInput;
if (element == null || element == '') return; // If the element is null or empty, return
// Get the new number of circles
let newCirclesNum = parseFloat(element.value);
newCirclesNum = newCirclesNum < 1 ? 1 : newCirclesNum;
newCirclesNum = newCirclesNum > 5 ? 5 : newCirclesNum;
// Update the number of circles
if (newCirclesNum < circlesNum) {
this.removeCircle(newCirclesNum);
} else if (newCirclesNum > circlesNum) {
this.addCircle(newCirclesNum);
}
}
// Event handler for when the user clicks the add circle button
static addCircle(num) {
for (let i = circlesNum; i < num; i++) {
// Create a new circle
let colour = color(random(0, 255), random(0, 255), random(0, 255));
circles.push(
new Circle(
random(0, walls[2]),
random(0, walls[3]),
random(-10, 10),
random(-10, 10),
random(1, 10),
colour
)
);
// Update the data panel for the new circle
this.updateDataPanel(circles[i]);
}
// Update the number of circles
circlesNum = num;
}
// Event handler for when the user clicks the remove circle button
static removeCircle(num) {
if (num >= circlesNum) {
return; // Nothing to remove if the number is not valid just in case
}
// Remove circles from the array
circles.splice(num, circlesNum - num);
let controls = this.dataPage.querySelectorAll(".bodyDataControl");
this.dataPage.removeChild(controls[controls.length - 1]);
// Update the number of circles
circlesNum = num;
}
// Event handler for adding data to the data page
static updateDataPanel(circle) {
// Create a new data panel element for the circle
const dataPanel = this.bodyDataLayoutTemplate.cloneNode(true);
dataPanel.classList.remove("hidden");
// Update the data panel with the circle's information
const circleIndex = circles.indexOf(circle);
dataPanel.querySelector(".collapse-title-label").textContent = `Body ${circleIndex + 1} Data`;
dataPanel.querySelector(".mass").value = circle.mass;
dataPanel.querySelector(".px").value = circle.x.toFixed(0);
dataPanel.querySelector(".py").value = circle.y.toFixed(0);
dataPanel.querySelector(".vx").value = circle.velx.toFixed(2);
dataPanel.querySelector(".vy").value = circle.vely.toFixed(2);
dataPanel.querySelector(".ke").value = circle.ke.toFixed(2);
dataPanel.querySelector(".momentum").value = circle.momentum.toFixed(2);
dataPanel.querySelector(".body-circle").style.backgroundColor = circle.colour;
// Append the data panel to the right-side data panel container
const dataContainer = document.getElementById("dataPage");
dataContainer.appendChild(dataPanel);
// Remove existing event listeners
dataPanel.querySelectorAll("input").forEach((input) => {
input.removeEventListener("input", UIEvents.updateCircleData);
});
// Add an event listener to update circle data when user edits input fields
dataPanel.querySelectorAll("input").forEach((input) => {
input.addEventListener("input", () => {
if (input.value == '') return;
UIEvents.updateCircleData(circleIndex, input);
});
});
}
// Event handler for updating circle data
static updateCircleData(circleIndex, inputElement) {
// Update the corresponding circle's data based on the input element
const circle = circles[circleIndex];
const className = inputElement.className;
// Update the circle's data based on the input element
if (className.includes("mass")) {
let x = parseFloat(inputElement.value);
x = x < 1 ? 1 : x; // Limit the mass to 1
x = x > 10 ? 10 : x; // Limit the mass to 10
circle.mass = x;
} else if (className.includes("px")) {
circle.x = parseFloat(inputElement.value);
} else if (className.includes("py")) {
circle.y = parseFloat(inputElement.value);
} else if (className.includes("vx")) {
circle.velx = parseFloat(inputElement.value);
} else if (className.includes("vy")) {
circle.vely = parseFloat(inputElement.value);
}
}
// Event handler for updating the data page in realtime
static updateDataPage() {
//Update the "number of bodies" input box, if the user is not editing it
if (document.activeElement != this.bodiesNumberInput) {
this.bodiesNumberInput.value = circles.length;
}
//Update the bodydatacontrols with the mass, position, velocity of their corresponding bodies
let bodyDataControls = this.dataPage.querySelectorAll(".bodyDataControl");
for (let i = 0; i < circles.length; i++) {
let bodyDataControl = bodyDataControls[i];
let body = circles[i];
//Get relevant controls to update
let massInput = bodyDataControl.querySelector(".mass");
let posXInput = bodyDataControl.querySelector(".px");
let posYInput = bodyDataControl.querySelector(".py");
let velocityXInput = bodyDataControl.querySelector(".vx");
let velocityYInput = bodyDataControl.querySelector(".vy");
let keLabel = bodyDataControl.querySelector(".ke");
let momentumLabel = bodyDataControl.querySelector(".momentum");
//Update control values if the user is not editing them (aka they do not have focus)
massInput.value = document.activeElement != massInput ? body.mass.toFixed(0) : massInput.value;
posXInput.value = document.activeElement != posXInput ? body.x.toFixed(0) : posXInput.value;
posYInput.value = document.activeElement != posYInput ? body.y.toFixed(0) : posYInput.value;
velocityXInput.value = document.activeElement != velocityXInput ? body.velx.toFixed(2) : velocityXInput.value;
velocityYInput.value = document.activeElement != velocityYInput ? body.vely.toFixed(2) : velocityYInput.value;
bodyDataControl.querySelector(".body-circle").style.backgroundColor = body.colour;
keLabel.value = body.ke.toFixed(2);
momentumLabel.value = body.momentum.toFixed(2);
}
}
}