|
|
|
class Manager {
|
|
|
|
|
|
|
|
pendula: Pendulum[] = []
|
|
|
|
|
|
|
|
timescale: number;
|
|
|
|
gravity: number;
|
|
|
|
|
|
|
|
playing = false;
|
|
|
|
|
|
|
|
static SubSteps: number;
|
|
|
|
static Size = 20;
|
|
|
|
|
|
|
|
init(){
|
|
|
|
// @ts-ignore
|
|
|
|
const {createApp} = Vue;
|
|
|
|
createApp({
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
gravity: 0,
|
|
|
|
timescale: 0,
|
|
|
|
subSteps: 0,
|
|
|
|
playingBtn: "play"
|
|
|
|
};
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
gravity(newGravity: number) {
|
|
|
|
manager.gravity = newGravity;
|
|
|
|
},
|
|
|
|
timescale(newScale: number){
|
|
|
|
manager.timescale = newScale;
|
|
|
|
},
|
|
|
|
subSteps(newSteps: number){
|
|
|
|
Manager.SubSteps = newSteps;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
togglePlay(){
|
|
|
|
manager.playing = !manager.playing;
|
|
|
|
this.playingBtn = manager.playing ? "pause" : "play";
|
|
|
|
},
|
|
|
|
resetSimulationControls(){
|
|
|
|
this.gravity = 9.81;
|
|
|
|
this.timescale = 1;
|
|
|
|
this.subSteps = 30;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
this.resetSimulationControls();
|
|
|
|
},
|
|
|
|
name: "Simulation"
|
|
|
|
}).mount("#simulation");
|
|
|
|
|
|
|
|
let app = createApp({
|
|
|
|
data(){
|
|
|
|
return {
|
|
|
|
segmentCount: 1,
|
|
|
|
maxSegmentCount: 30,
|
|
|
|
masses: [],
|
|
|
|
lengths: [],
|
|
|
|
startAngle: 90,
|
|
|
|
color: "#ffffff",
|
|
|
|
rainbow: false,
|
|
|
|
multiple: false,
|
|
|
|
pendulumCount: 10,
|
|
|
|
changeProperty: "angle",
|
|
|
|
changeAmount: 0.0005,
|
|
|
|
changeIndex: 0
|
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
add() {
|
|
|
|
if (this.multiple){
|
|
|
|
let changeAmount = this.changeAmount;
|
|
|
|
let changeIndex = this.changeIndex;
|
|
|
|
let color = p.color(this.color);
|
|
|
|
p.colorMode(p.HSB, 100);
|
|
|
|
for (let i = 0; i < this.pendulumCount; i++){
|
|
|
|
let M = this.masses.slice(0, this.segmentCount);
|
|
|
|
let L = this.lengths.slice(0, this.segmentCount);
|
|
|
|
let startAngle = this.startAngle;
|
|
|
|
let progress = i / this.pendulumCount - 0.5;
|
|
|
|
switch (this.changeProperty){
|
|
|
|
case "angle":
|
|
|
|
startAngle += progress * 360 * changeAmount;
|
|
|
|
break;
|
|
|
|
case "mass":
|
|
|
|
M[changeIndex] += progress * M[changeIndex] * changeAmount;
|
|
|
|
break;
|
|
|
|
case "length":
|
|
|
|
L[changeIndex] += progress * L[changeIndex] * changeAmount;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (this.rainbow){
|
|
|
|
let hue = (progress + 0.5) * 100;
|
|
|
|
color = p.color(hue, 100, 100);
|
|
|
|
}
|
|
|
|
let newPendulum = new Pendulum(M, L, color, startAngle);
|
|
|
|
manager.pendula.push(newPendulum);
|
|
|
|
}
|
|
|
|
p.colorMode(p.RGB);
|
|
|
|
} else {
|
|
|
|
let M = this.masses.slice(0, this.segmentCount);
|
|
|
|
let L = this.lengths.slice(0, this.segmentCount);
|
|
|
|
let color = p.color(this.color);
|
|
|
|
let newPendulum = new Pendulum(M, L, color, this.startAngle);
|
|
|
|
manager.pendula.push(newPendulum);
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
deleteAll(){
|
|
|
|
if (confirm("Delete all pendula?")){
|
|
|
|
manager.pendula.splice(0);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
resetMasses(){
|
|
|
|
for (let i = 0; i < this.maxSegmentCount; i++)
|
|
|
|
this.masses[i] = 1;
|
|
|
|
},
|
|
|
|
resetLengths() {
|
|
|
|
for (let i = 0; i < this.maxSegmentCount; i++)
|
|
|
|
this.lengths[i] = 1;
|
|
|
|
},
|
|
|
|
normalize(){
|
|
|
|
let L: [number] = this.lengths;
|
|
|
|
let sum = L.slice(0, this.segmentCount).reduce((p, n) => p + n);
|
|
|
|
let maxLength = Manager.Size / 2;
|
|
|
|
let factor = maxLength / sum;
|
|
|
|
|
|
|
|
for (let i = 0; i < this.segmentCount; i++)
|
|
|
|
this.lengths[i] *= factor;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
this.resetMasses();
|
|
|
|
this.resetLengths();
|
|
|
|
},
|
|
|
|
name: "Add Pendula"
|
|
|
|
});
|
|
|
|
app.config.globalProperties.$filters = {
|
|
|
|
round(value: number, n: number){
|
|
|
|
if (!value)
|
|
|
|
return "0";
|
|
|
|
return +value.toFixed(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
app.mount("#preparation");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
update(){
|
|
|
|
if (this.playing) {
|
|
|
|
const h = this.timescale / Math.max(p.frameRate(), 30);
|
|
|
|
this.pendula.forEach(p => p.update(h));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
draw(){
|
|
|
|
p.push()
|
|
|
|
p.translate(p.width / 2, p.height / 2);
|
|
|
|
this.pendula.forEach(p => p.draw());
|
|
|
|
p.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|