rewritten to use position based dynamics

main
Benjamin Kraft 1 year ago
parent c6a43ca622
commit b7491fa07f
  1. BIN
      public/data/images/favicon.ico
  2. 1
      public/data/images/pause.svg
  3. 1
      public/data/images/play.svg
  4. 1
      public/data/images/refresh.svg
  5. 38
      public/data/scripts/ts/init.ts
  6. 49
      public/data/scripts/ts/manager.ts
  7. 140
      public/data/scripts/ts/pendulum.ts
  8. 38
      public/data/scripts/ts/vector.ts
  9. 7
      public/data/settings/libraries.json
  10. 23
      public/data/settings/settings.json
  11. 59
      public/index.html
  12. 375
      public/package-lock.json
  13. 3
      public/package.json
  14. 44
      public/styles.css

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M531.616-226.463v-507.074h201.921v507.074H531.616Zm-305.153 0v-507.074h201.921v507.074H226.463Zm361.113-55.96h90.001v-395.154h-90.001v395.154Zm-305.153 0h90.001v-395.154h-90.001v395.154Zm0-395.154v395.154-395.154Zm305.153 0v395.154-395.154Z"/></svg>

After

Width:  |  Height:  |  Size: 346 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M345.655-245.041v-469.918L714.19-480 345.655-245.041ZM401.615-480Zm0 132.385L609.962-480 401.615-612.385v264.77Z"/></svg>

After

Width:  |  Height:  |  Size: 218 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M482.013-187.271q-123.018 0-208.265-85.065-85.247-85.066-85.247-207.634 0-122.568 85.247-207.664 85.247-85.095 208.291-85.095 69.654 0 131.461 31.038 61.808 31.039 102.231 88.193v-119.231h55.96v235.728H535.963v-55.96h153.96q-31.34-57.649-86.798-90.729-55.459-33.079-121.047-33.079-98.924 0-168.27 69.156t-69.346 167.731q0 98.574 69.221 167.613 69.221 69.038 168.29 69.038 75.906 0 137.024-43.192 61.119-43.192 85.883-114.385h59.119q-26.423 95.307-104.688 154.422-78.266 59.115-177.298 59.115Z"/></svg>

After

Width:  |  Height:  |  Size: 598 B

@ -1,23 +1,15 @@
'use strict'; 'use strict';
let debug = false, let debug = false,
font: any, font: any;
settings: any;
let socket: any;
let antiCacheQuery = '?_=' + new Date().getTime(); let antiCacheQuery = '?_=' + new Date().getTime();
let manager; let manager: Manager;
const p = new p5((p: p5) => { const p = new p5((p: p5) => {
p.preload = () => { p.preload = () => {
settings = p.loadJSON('data/settings/settings.json' + antiCacheQuery, {}, 'json', (json: any) => {
console.log('Local settings loaded: ', json);
}, (error: any) => {
console.log('Local settings failed: ', error);
});
font = p.loadFont('data/styles/fonts/Tajawal/Tajawal-Regular.ttf' + antiCacheQuery, (font: any) => { font = p.loadFont('data/styles/fonts/Tajawal/Tajawal-Regular.ttf' + antiCacheQuery, (font: any) => {
console.log('Local font loaded: ', font); console.log('Local font loaded: ', font);
@ -27,13 +19,11 @@ const p = new p5((p: p5) => {
} }
p.setup = () => { p.setup = () => {
interfaceSetup();
canvasSetup(); canvasSetup();
eventsSetup(); eventsSetup();
loadDynamicScripts().then(() => { manager = new Manager();
//Load other stuff manager.init();
manager = new Manager(); interfaceSetup();
});
} }
p.draw = () => { p.draw = () => {
@ -52,9 +42,8 @@ function debugInformation(){
} }
function interfaceSetup(){ function interfaceSetup(){
} }
function canvasSetup(){ function canvasSetup(){
@ -65,19 +54,4 @@ function canvasSetup(){
let canvas = p.createCanvas(w, h); let canvas = p.createCanvas(w, h);
canvas.parent('canvas_holder'); canvas.parent('canvas_holder');
p.textFont(font); p.textFont(font);
}
async function loadDynamicScripts(){
const json = await p.httpGet('data/settings/libraries.json' + antiCacheQuery, 'json') as Object;
let requests = [];
for (let script in json) {
if (json[script]) {
let url = '/lib/benjocraeft/' + script + '.js';
requests.push($.getScript(url, () => {
console.log('Successfully loaded script: ', url);
}));
}
}
await $.when(...requests);
console.log('All dynamic scripts have been loaded!');
} }

@ -1,31 +1,66 @@
class Manager { class Manager {
nPendula: NPendulum[] = [] pendulums: Pendulum[] = []
h = 0.07 timescale = 1;
gravity = 9.81;
playing = false;
constructor() { constructor() {
p.colorMode(p.HSB, 100); p.colorMode(p.HSB, 100);
let count = 500; let count = 100;
for (let i = 0; i < count; i++){ for (let i = 0; i < count; i++){
let rad = i / count / 1e3 + p.PI * 1.05; let rad = i / count / 1e3 + p.PI * 1.05;
let hue = i / count * 100; let hue = i / count * 100;
let color = p.color(hue, 100, 100); let color = p.color(hue, 100, 100);
this.nPendula.push( this.pendulums.push(
new NPendulum([200, 200], [2, 2], rad, color) new Pendulum([1, 1, 1, 1, 1, 1, 1, 1], [50, 50, 50, 50, 50, 50, 50, 100 + i / count / 1000000], color)
); );
} }
p.colorMode(p.RGB); p.colorMode(p.RGB);
}
init(){
// @ts-ignore
const {createApp} = Vue;
createApp({
data() {
return {
gravity: manager.gravity,
timescale: manager.timescale,
playingBtn: "play"
};
},
watch: {
gravity(newGravity: string) {
manager.gravity = parseFloat(newGravity);
},
timescale(newScale: string){
manager.timescale = parseFloat(newScale);
}
},
methods: {
togglePlay(){
manager.playing = !manager.playing;
this.playingBtn = manager.playing ? "pause" : "play";
}
}
}).mount("#simulation");
} }
update(){ update(){
this.nPendula.forEach(p => p.update(this.h)); if (this.playing) {
const h = this.timescale / (p.frameRate() || 60);
this.pendulums.forEach(p => p.update(h));
}
} }
draw(){ draw(){
p.push() p.push()
p.translate(p.width / 2, p.height / 2); p.translate(p.width / 2, p.height / 2);
this.nPendula.forEach(p => p.draw()); this.pendulums.forEach(p => p.draw());
p.pop(); p.pop();
} }

@ -1,100 +1,88 @@
const g = 9.81 class Pendulum {
class NPendulum {
pendula: Pendulum[] = []
constructor(lengths, masses, startRad, color) {
switch (lengths.length) {
case 1:
this.pendula.push(new Pendulum(lengths[0], masses[0], startRad, color));
break;
case 2:
let p1 = new Pendulum(lengths[0], masses[0], startRad, color);
let p2 = new Pendulum(lengths[1], masses[1], startRad, color);
p1.calcAcc = function(pendula){
let p2 = pendula[1];
return -g / this.l * p.sin(this.rad) - p2.l * p2.m / this.l / (this.m + p2.m)
* (p.cos(this.rad - p2.rad) * p2.acc + p.sin(this.rad - p2.rad) * p.pow(p2.vel, 2));
}
p2.calcAcc = function (pendula){
let p1 = pendula[0];
return -g / this.l * p.sin(this.rad) - p1.l / this.l
* (p.cos(p1.rad - this.rad) * p1.acc - p.sin(p1.rad - this.rad) * p.pow(p1.vel, 2));
}
this.pendula.push(p1, p2);
break;
}
this.pendula[0].origin = p.createVector(0, 0);
}
updateOrigins(){ X: Vector[] = [];
this.pendula.forEach((p, i) => { V: Vector[] = [];
if (i > 0){
let before = this.pendula[i - 1];
p.origin = p5.Vector.add(before.origin, before.pos);
}
});
}
update(h){ size: number;
this.pendula.forEach((p, i) => {
p.update(h, this.pendula);
});
this.updateOrigins();
}
draw(){ constructor(readonly M: number[], readonly L: number[], readonly color: p5.Color) {
this.pendula.forEach(p => p.draw()); console.assert(M.length === L.length, M, L, "Masses and Lengths are not of equal length!");
this.size = M.length;
let currentPosition = new Vector(0, 0);
for (let i = 0; i < this.size; i++){
let a = Math.sqrt(L[i] * L[i] / 2);
currentPosition.addC(-L[i], 0);
this.X.push(currentPosition.copy());
this.V.push(new Vector(0, 0));
}
} }
} // using position based dynamics
update(h: number) {
const subSteps = 50;
h /= subSteps;
class Pendulum { for (let k = 0; k < subSteps; k++) {
l: number let previousP = new Vector(0, 0);
m: number
acc: number = 0 for (let i = 0; i < this.size; i++) {
vel: number = 0 // apply external force (gravity)
rad: number this.V[i].addC(0, manager.gravity * h * 50);
origin: p5.Vector // euler step
let currentP = Vector.Add(this.X[i],Vector.Mult(this.V[i], h));
color: p5.Color // solve distance constraint
let w1 = i === 0 ? 0 : 1 / this.M[i - 1];
let w2 = 1 / this.M[i];
let s = Vector.Sub(previousP, currentP);
let n = s.copy();
n.normalize();
let l = s.mag();
constructor(l, m, rad, color) { let deltaP1 = Vector.Mult(n, -w1 / (w1 + w2) * (l - this.L[i]));
this.l = l; let deltaP2 = Vector.Mult(n, w2 / (w1 + w2) * (l - this.L[i]));
this.m = m;
this.rad = rad;
this.color = color;
}
calcAcc(pendula){ previousP.add(deltaP1);
return -g / this.l * p.sin(this.rad); currentP.add(deltaP2);
}
// integrate
if (i > 0){
this.V[i - 1] = Vector.Mult(Vector.Sub(previousP, this.X[i - 1]), 1 / h);
this.X[i - 1] = previousP;
}
update(h, pendula = []){ previousP = currentP;
this.acc = this.calcAcc(pendula); }
this.vel += this.acc * h;
this.rad += this.vel * h; this.V[this.size - 1] = Vector.Mult(Vector.Sub(previousP, this.X[this.size - 1]), 1 / h);
this.X[this.size - 1] = previousP;
}
} }
draw(){ draw(){
let pos = this.pos;
p.push(); p.push();
p.translate(this.origin);
p.stroke(this.color); p.stroke(this.color);
p.strokeWeight(3); p.strokeWeight(2);
p.line(0, 0, pos.x, pos.y); p.fill(255);
p.ellipse(pos.x, pos.y, this.m * 5, this.m * 5);
p.pop(); let p1 = new Vector(0, 0);
} for (let p2 of this.X){
p.line(p1.x, p1.y, p2.x, p2.y);
p1 = p2.copy();
}
get pos(){ for (let p2 of this.X){
return p5.Vector.mult(p.createVector(p.sin(this.rad), p.cos(this.rad)), this.l); p.ellipse(p2.x, p2.y, 10, 10);
}
p.pop();
} }
} }

@ -0,0 +1,38 @@
class Vector {
constructor(public x: number, public y: number) {}
copy(){
return new Vector(this.x, this.y);
}
static Add(v1: Vector, v2: Vector){
return new Vector(v1.x + v2.x, v1.y + v2.y);
}
static Sub(v1: Vector, v2: Vector){
return new Vector(v1.x - v2.x, v1.y - v2.y);
}
static Mult(v: Vector, n: number){
return new Vector(v.x * n, v.y * n);
}
add(v: Vector){
this.x += v.x;
this.y += v.y;
}
addC(x: number, y: number){
this.x += x;
this.y += y;
}
mult(n: number){
this.x *= n;
this.y *= n;
}
mag(){
return Math.sqrt(this.x * this.x + this.y * this.y);
}
normalize(){
this.mult(1 / this.mag());
}
}

@ -1,7 +0,0 @@
{
"collision": false,
"colorPicker": false,
"cookie": false,
"prototypes": false,
"technical": false
}

@ -1,23 +0,0 @@
{
"project": {
"name": "pendulum",
"author": "BenjoCraeft",
"version": "0.0.0",
"playerCounts": [],
"online": {
"iceServers": [
{"urls": "stun:stun.l.google.com:19302"},
{
"urls": "turn:numb.viagenie.ca",
"credential": "muazkh",
"username": "webrtc@live.com"
}
]
}
},
"frameWork": {
"frameRate": 60,
"width": null,
"height": null
}
}

@ -4,6 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js" type="text/javascript"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js" type="text/javascript"></script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js" type="text/javascript"></script> <script src="https://code.jquery.com/jquery-3.6.4.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="data/scripts/js/main.js" type="text/javascript"></script> <script src="data/scripts/js/main.js" type="text/javascript"></script>
<link href="styles.css" rel="stylesheet"> <link href="styles.css" rel="stylesheet">
<link href="data/styles/color_picker.css" rel="stylesheet"> <link href="data/styles/color_picker.css" rel="stylesheet">
@ -13,47 +14,23 @@
<body> <body>
<div id="p5_loading"></div> <div id="p5_loading"></div>
<div id="content"> <div id="content">
<div id="options" style="display: none"> <div id="options">
<label> <fieldset id="preparation">
<input type="radio" name="type"> <legend>Preparation</legend>
Single pendulum
</label> </fieldset>
<br> <fieldset id="simulation">
<label> <legend>Simulation</legend>
<input type="radio" name="type" checked> <label>
Double pendulum Gravity: {{ gravity }} <sup>N</sup>&frasl;<sub>kg</sub>
</label> <input type="range" v-model="gravity" min="0" max="30" step=".01" autocomplete="off">
<br> </label>
<label> <label>
Count: Timescale: x{{ timescale }}
<input type="number" min="1" max="500" value="1"> <input type="range" v-model="timescale" min="0.01" max="3" step=".01" autocomplete="off">
</label> </label>
<br> <button @click="togglePlay" id="play_btn" :class="playingBtn"></button>
<label> </fieldset>
L1:
<input type="range" min="50" max="400" value="200">
<span>200</span>
</label>
<br>
<label>
L2:
<input type="range" min="50" max="400" value="200">
<span>200</span>
</label>
<br>
<label>
M1:
<input type="range" min="1" max="10" value="1">
<span>1</span>
</label>
<br>
<label>
M2:
<input type="range" min="1" max="10" value="1">
<span>1</span>
</label>
<br>
<input type="button" value="Add">
</div> </div>
<div id="canvas_holder"></div> <div id="canvas_holder"></div>
</div> </div>

@ -7,11 +7,30 @@
"": { "": {
"name": "pendulum", "name": "pendulum",
"version": "1.0.0", "version": "1.0.0",
"dependencies": {
"vue": "^3.3.4"
},
"devDependencies": { "devDependencies": {
"@types/jquery": "^3.5.16", "@types/jquery": "^3.5.16",
"typescript": "^5.0.2" "typescript": "^5.0.2"
} }
}, },
"node_modules/@babel/parser": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.15.tgz",
"integrity": "sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA==",
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"node_modules/@types/jquery": { "node_modules/@types/jquery": {
"version": "3.5.16", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.16.tgz", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.16.tgz",
@ -27,6 +46,186 @@
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
"dev": true "dev": true
}, },
"node_modules/@vue/compiler-core": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
"integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
"dependencies": {
"@babel/parser": "^7.21.3",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
"integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
"dependencies": {
"@vue/compiler-core": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz",
"integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==",
"dependencies": {
"@babel/parser": "^7.20.15",
"@vue/compiler-core": "3.3.4",
"@vue/compiler-dom": "3.3.4",
"@vue/compiler-ssr": "3.3.4",
"@vue/reactivity-transform": "3.3.4",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.0",
"postcss": "^8.1.10",
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz",
"integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==",
"dependencies": {
"@vue/compiler-dom": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/reactivity": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
"integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
"dependencies": {
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/reactivity-transform": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz",
"integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==",
"dependencies": {
"@babel/parser": "^7.20.15",
"@vue/compiler-core": "3.3.4",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.0"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz",
"integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==",
"dependencies": {
"@vue/reactivity": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz",
"integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==",
"dependencies": {
"@vue/runtime-core": "3.3.4",
"@vue/shared": "3.3.4",
"csstype": "^3.1.1"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz",
"integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==",
"dependencies": {
"@vue/compiler-ssr": "3.3.4",
"@vue/shared": "3.3.4"
},
"peerDependencies": {
"vue": "3.3.4"
}
},
"node_modules/@vue/shared": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
},
"node_modules/csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"node_modules/magic-string": {
"version": "0.30.3",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz",
"integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
},
"engines": {
"node": ">=12"
}
},
"node_modules/nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"node_modules/postcss": {
"version": "8.4.29",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
"integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz",
@ -39,9 +238,31 @@
"engines": { "engines": {
"node": ">=12.20" "node": ">=12.20"
} }
},
"node_modules/vue": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
"integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==",
"dependencies": {
"@vue/compiler-dom": "3.3.4",
"@vue/compiler-sfc": "3.3.4",
"@vue/runtime-dom": "3.3.4",
"@vue/server-renderer": "3.3.4",
"@vue/shared": "3.3.4"
}
} }
}, },
"dependencies": { "dependencies": {
"@babel/parser": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.15.tgz",
"integrity": "sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA=="
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"@types/jquery": { "@types/jquery": {
"version": "3.5.16", "version": "3.5.16",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.16.tgz", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.16.tgz",
@ -57,11 +278,165 @@
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
"dev": true "dev": true
}, },
"@vue/compiler-core": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
"integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
"requires": {
"@babel/parser": "^7.21.3",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
}
},
"@vue/compiler-dom": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
"integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
"requires": {
"@vue/compiler-core": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"@vue/compiler-sfc": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz",
"integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==",
"requires": {
"@babel/parser": "^7.20.15",
"@vue/compiler-core": "3.3.4",
"@vue/compiler-dom": "3.3.4",
"@vue/compiler-ssr": "3.3.4",
"@vue/reactivity-transform": "3.3.4",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.0",
"postcss": "^8.1.10",
"source-map-js": "^1.0.2"
}
},
"@vue/compiler-ssr": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz",
"integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==",
"requires": {
"@vue/compiler-dom": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"@vue/reactivity": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
"integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
"requires": {
"@vue/shared": "3.3.4"
}
},
"@vue/reactivity-transform": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz",
"integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==",
"requires": {
"@babel/parser": "^7.20.15",
"@vue/compiler-core": "3.3.4",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.0"
}
},
"@vue/runtime-core": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz",
"integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==",
"requires": {
"@vue/reactivity": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"@vue/runtime-dom": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz",
"integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==",
"requires": {
"@vue/runtime-core": "3.3.4",
"@vue/shared": "3.3.4",
"csstype": "^3.1.1"
}
},
"@vue/server-renderer": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz",
"integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==",
"requires": {
"@vue/compiler-ssr": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"@vue/shared": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
},
"csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
"estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"magic-string": {
"version": "0.30.3",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz",
"integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==",
"requires": {
"@jridgewell/sourcemap-codec": "^1.4.15"
}
},
"nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA=="
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"postcss": {
"version": "8.4.29",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
"integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==",
"requires": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
},
"typescript": { "typescript": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz",
"integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==",
"dev": true "dev": true
},
"vue": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
"integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==",
"requires": {
"@vue/compiler-dom": "3.3.4",
"@vue/compiler-sfc": "3.3.4",
"@vue/runtime-dom": "3.3.4",
"@vue/server-renderer": "3.3.4",
"@vue/shared": "3.3.4"
}
} }
} }
} }

@ -4,5 +4,8 @@
"devDependencies": { "devDependencies": {
"@types/jquery": "^3.5.16", "@types/jquery": "^3.5.16",
"typescript": "^5.0.2" "typescript": "^5.0.2"
},
"dependencies": {
"vue": "^3.3.4"
} }
} }

@ -24,10 +24,14 @@ button:hover{cursor: pointer;}
:root{ :root{
--width: 100vw; --width: 100vw;
--height: 100vh; --height: 100vh;
--opt-width: 400px;
--opt-padding: 10px;
--opt-border: 5px;
--canvas-width: calc(100vw - (var(--opt-width) + var(--opt-padding) * 2 + var(--opt-border)))
} }
body { body {
overflow: hidden; overflow: auto;
} }
/** /**
@ -36,7 +40,7 @@ body {
#canvas_holder{ #canvas_holder{
position: relative; position: relative;
width: calc(var(--width)); /* -325px);*/ width: var(--canvas-width);
height: var(--height); height: var(--height);
float: left; float: left;
} }
@ -55,18 +59,46 @@ body {
#options { #options {
float: left; float: left;
width: 300px; width: var(--opt-width);
height: 100vh; height: calc(100vh - var(--opt-padding) * 2);
padding: 10px; padding: var(--opt-padding);
border-right: 5px solid rgb(150, 150, 150); border-right: var(--opt-border) solid rgb(150, 150, 150);
background-color: rgb(40, 40, 40); background-color: rgb(40, 40, 40);
} }
#options *:not(input[type=number], input[type=button]){ #options *:not(input[type=number], input[type=button]){
color: white; color: white;
} }
fieldset {
border-radius: 5px;
}
input { input {
margin: 5px; margin: 5px;
} }
input[type=button]{
border: 3px solid #0060df;
border-radius: 5px;
line-height: 30px;
font-size: 30px;
}
#play_btn {
width: 100%;
border: 4px solid cornflowerblue;
border-radius: 5px;
height: 50px;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
#play_btn.play {
background-image: url("data/images/play.svg");
}
#play_btn.pause {
background-image: url("data/images/pause.svg");
}

Loading…
Cancel
Save