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(); } }