add ui for adding pendulums

main
Benjamin Kraft 1 year ago
parent b7491fa07f
commit 9c4d93af7a
  1. 1
      public/data/images/add.svg
  2. 1
      public/data/images/delete.svg
  3. 46
      public/data/scripts/ts/manager.ts
  4. 10
      public/data/scripts/ts/pendulum.ts
  5. 2
      public/data/styles/range_input.css
  6. 76
      public/index.html
  7. 59
      public/styles.css

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M450.001-450.001h-230v-59.998h230v-230h59.998v230h230v59.998h-230v230h-59.998v-230Z"/></svg>

After

Width:  |  Height:  |  Size: 189 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m376-327.692 104-104 104 104L612.308-356l-104-104 104-104L584-592.308l-104 104-104-104L347.692-564l104 104-104 104L376-327.692ZM304.615-160Q277-160 258.5-178.5 240-197 240-224.615V-720h-40v-40h160v-30.77h240V-760h160v40h-40v495.385Q720-197 701.5-178.5 683-160 655.385-160h-350.77ZM680-720H280v495.385q0 9.23 7.692 16.923Q295.385-200 304.615-200h350.77q9.23 0 16.923-7.692Q680-215.385 680-224.615V-720Zm-400 0v520-520Z"/></svg>

After

Width:  |  Height:  |  Size: 523 B

@ -15,7 +15,7 @@ class Manager {
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.pendulums.push( this.pendulums.push(
new Pendulum([1, 1, 1, 1, 1, 1, 1, 1], [50, 50, 50, 50, 50, 50, 50, 100 + i / count / 1000000], color) new Pendulum([1, 1, 1, 1, 1, 1, 1, 1], [50, 50, 50, 50, 50, 50, 50, 100 + i / count / 10000], color)
); );
} }
p.colorMode(p.RGB); p.colorMode(p.RGB);
@ -34,11 +34,11 @@ class Manager {
}; };
}, },
watch: { watch: {
gravity(newGravity: string) { gravity(newGravity: number) {
manager.gravity = parseFloat(newGravity); manager.gravity = newGravity;
}, },
timescale(newScale: string){ timescale(newScale: number){
manager.timescale = parseFloat(newScale); manager.timescale = newScale;
} }
}, },
methods: { methods: {
@ -46,13 +46,45 @@ class Manager {
manager.playing = !manager.playing; manager.playing = !manager.playing;
this.playingBtn = manager.playing ? "pause" : "play"; this.playingBtn = manager.playing ? "pause" : "play";
} }
} },
name: "Simulation"
}).mount("#simulation"); }).mount("#simulation");
createApp({
data(){
return {
segmentCount: 1,
masses: [],
lengths: [],
startAngle: 90,
color: "#ffffff",
rainbow: false,
multiple: false,
pendulumCount: 10,
changeProperty: "angle",
changeAmount: 0.05,
changeIndex: 0
}
},
methods: {
add() {
},
},
mounted() {
for (let i = 0; i < 20; i++){
this.masses.push(1);
this.lengths.push(1);
}
},
name: "Preparation"
}).mount("#preparation");
} }
update(){ update(){
if (this.playing) { if (this.playing) {
const h = this.timescale / (p.frameRate() || 60); const h = this.timescale / Math.max(p.frameRate(), 1);
this.pendulums.forEach(p => p.update(h)); this.pendulums.forEach(p => p.update(h));
} }
} }

@ -27,9 +27,15 @@ class Pendulum {
for (let k = 0; k < subSteps; k++) { for (let k = 0; k < subSteps; k++) {
let previousP = new Vector(0, 0); // Classic PBD needs multiple loops
// Here, I can put all operations safely into one single loop,
// because the positions and velocities in X, V are sorted
// from the pendulum's origin to it's end which means
// that only direct neighbours affect each other
let previousP = new Vector(0, 0);
for (let i = 0; i < this.size; i++) { for (let i = 0; i < this.size; i++) {
// apply external force (gravity) // apply external force (gravity)
this.V[i].addC(0, manager.gravity * h * 50); this.V[i].addC(0, manager.gravity * h * 50);
@ -69,7 +75,7 @@ class Pendulum {
p.push(); p.push();
p.stroke(this.color); p.stroke(this.color);
p.strokeWeight(2); p.strokeWeight(1);
p.fill(255); p.fill(255);
let p1 = new Vector(0, 0); let p1 = new Vector(0, 0);

@ -1,6 +1,6 @@
input[type=range] { input[type=range] {
-webkit-appearance: none; -webkit-appearance: none;
margin: 18px 0; margin: 10px 0;
width: 100%; width: 100%;
background: none; background: none;
} }

@ -2,9 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<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="data/lib/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="data/lib/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/lib/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">
@ -17,17 +17,81 @@
<div id="options"> <div id="options">
<fieldset id="preparation"> <fieldset id="preparation">
<legend>Preparation</legend> <legend>Preparation</legend>
<label>
Segments: {{ segmentCount }}
<input type="range" v-model.number="segmentCount" min="1" max="20" step="1">
</label>
<div id="segment_view">
<span id="segment_header_mass">Mass</span><span id="segment_header_length">Length</span>
<template v-for="i in segmentCount">
<span class="segment_label">({{ i - 1 }})</span>
<span class="segment_label">{{ masses[i - 1] }}kg</span>
<input type="range" v-model.number="masses[i - 1]" min="0.1" max="10" step=".1">
<span class="segment_label">{{ lengths[i - 1] }}m</span>
<input type="range" v-model.number="lengths[i - 1]" min="0.1" max="5" step=".1">
</template>
</div>
<label>
Starting Angle: {{startAngle}}°
<input type="range" v-model.number="startAngle" min="0" max="360" step="1">
</label>
<label v-show="!multiple || !rainbow">
Color:
<input type="color" v-model="color">
</label>
<br>
<label>
<input type="checkbox" v-model="multiple">
Add multiple
</label>
<br>
<template v-if="multiple">
<label>
<input type="checkbox" v-model="rainbow">
Use rainbow coloring
</label>
<br><br>
<label>
Add Count: {{pendulumCount}}
<input type="range" v-model.number="pendulumCount" min="10" max="250" step="10">
</label>
<span>Property to change slightly:</span>
<br>
<label>
<input type="radio" value="angle" v-model="changeProperty">
Starting Angle
</label>
<br>
<label>
<input type="radio" value="mass" v-model="changeProperty">
Specific Mass
</label>
<br>
<label>
<input type="radio" value="length" v-model="changeProperty">
Specific Length
</label>
<br><br>
<label v-show="changeProperty === 'mass' || changeProperty === 'length'">
Index: {{ changeIndex }}
<input type="range" v-model.number="changeIndex" min="0" :max="segmentCount - 1" step="1">
</label>
<label>
Change Amount: {{ changeAmount * 100 }}%
<input type="range" v-model.number="changeAmount" min="0.0001" max="0.1" step="0.0001">
</label>
</template>
<button @click="add" id="add_btn"></button>
</fieldset> </fieldset>
<fieldset id="simulation"> <fieldset id="simulation">
<legend>Simulation</legend> <legend>Simulation</legend>
<label> <label>
Gravity: {{ gravity }} <sup>N</sup>&frasl;<sub>kg</sub> Gravity: {{ gravity }} <sup>N</sup>&frasl;<sub>kg</sub>
<input type="range" v-model="gravity" min="0" max="30" step=".01" autocomplete="off"> <input type="range" v-model.number="gravity" min="0" max="30" step=".01">
</label> </label>
<label> <label>
Timescale: x{{ timescale }} Timescale: x{{ timescale }}
<input type="range" v-model="timescale" min="0.01" max="3" step=".01" autocomplete="off"> <input type="range" v-model.number="timescale" min="0.01" max="3" step=".01">
</label> </label>
<button @click="togglePlay" id="play_btn" :class="playingBtn"></button> <button @click="togglePlay" id="play_btn" :class="playingBtn"></button>
</fieldset> </fieldset>

@ -31,7 +31,7 @@ button:hover{cursor: pointer;}
} }
body { body {
overflow: auto; overflow: hidden;
} }
/** /**
@ -64,6 +64,9 @@ body {
padding: var(--opt-padding); padding: var(--opt-padding);
border-right: var(--opt-border) 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);
display: flex;
flex-direction: column;
} }
#options *:not(input[type=number], input[type=button]){ #options *:not(input[type=number], input[type=button]){
color: white; color: white;
@ -71,29 +74,67 @@ body {
fieldset { fieldset {
border-radius: 5px; border-radius: 5px;
margin: 10px;
} }
fieldset > legend {
font-size: 25px;
}
input { input {
margin: 5px; margin: 5px;
} }
input[type=button]{ #preparation {
border: 3px solid #0060df; overflow-y: auto;
border-radius: 5px;
line-height: 30px;
font-size: 30px;
} }
#play_btn { #segment_view {
display: grid;
grid-template-columns: 10% 15% 30% 15% 30%;
}
#segment_header_mass {
grid-column: 2 / span 2;
}
#segment_header_length {
grid-column: 4 / span 2;
}
.segment_label {
display: inline-flex;
align-items: center;
justify-content: center;
}
button {
margin-top: 10px;
width: 100%; width: 100%;
border: 4px solid cornflowerblue;
border-radius: 5px;
height: 50px; height: 50px;
border: 3px solid black;
border-radius: 5px;
background-size: contain; background-size: contain;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
button:hover{
filter: brightness(80%);
}
button:active {
filter: brightness(60%);
}
#add_btn{
border-color: #007a00;
background-color: #d3ffd3;
background-image: url("data/images/add.svg");
}
#play_btn {
border-color: #0000c2;
background-color: #b1b1ff;
}
#play_btn.play { #play_btn.play {
background-image: url("data/images/play.svg"); background-image: url("data/images/play.svg");

Loading…
Cancel
Save