commit 03dd97a034227481236b907cf2b823f6be9c075d
parent ebf2c9b7cd65f0c3b3936d8999669688ae001aaa
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date: Wed, 14 Jul 2021 17:04:39 -0700
Add simulation information pages
Diffstat:
15 files changed, 643 insertions(+), 280 deletions(-)
diff --git a/images/info.svg b/images/info.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-info"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
+\ No newline at end of file
diff --git a/images/x.svg b/images/x.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
+\ No newline at end of file
diff --git a/simulations/atwood-machine.html b/simulations/atwood-machine.html
@@ -15,6 +15,8 @@
<div id="app">
<header>
<a title="Home" href="../" class="icon"><img alt="" src="../images/home.svg"></a>
+ <button v-show="!infoVisible" title="About" class="icon" @click="infoVisible=true"><img alt="" src="../images/info.svg"></button>
+ <button v-show="infoVisible" title="Close" class="icon" @click="infoVisible=false"><img alt="" src="../images/x.svg"></button>
<h1>Atwood Machine</h1>
</header>
@@ -22,56 +24,86 @@
<p>This simulation requires JavaScript</p>
</noscript>
- <div id="input" hidden>
- <section>
- <label for="mass1Input"><b>Mass 1:</b> {{ mass1.toFixed(1) }} Kg</label>
- <input type="range" min="1" max="10" step="0.1" v-model.number="mass1" @input="reset" @dblclick="mass1=1" :disabled="active" id="mass1Input">
- </section>
- <section>
- <label for="mass2Input"><b>Mass 2:</b> {{ mass2.toFixed(1) }} Kg</label>
- <input type="range" min="1" max="10" step="0.1" v-model.number="mass2" @input="reset" @dblclick="mass2=10" :disabled="active" id="mass2Input">
- </section>
- <section>
- <label for="angleInput"><b>Angle:</b> {{ angle.toFixed(0) }}<sup>o</sup></label>
- <input type="range" min="0" max="90" step="1" v-model.number="angle" @input="reset" @dblclick="angle=90" :disabled="active" id="angleInput">
- </section>
- <section>
- <label for="gravityInput"><b>Gravity:</b> {{ gravity.toFixed(1) }} m/s<sup>2</sup></label>
- <input type="range" min="1" max="10" step="0.1" v-model.number="gravity" @input="reset" @dblclick="gravity=9.8" :disabled="active" id="gravityInput">
- </section>
- </div>
-
- <div id="output" hidden>
- <div id="simControls">
- <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')" :disabled="Math.abs(displacement) === 3">
- <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
- </button>
- <button @click="update" class="icon" title="Step Forward" :disabled="Math.abs(displacement) === 3 || active">
- <img alt="" src="../images/step-forward.svg">
- </button>
- <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
- <img alt="" src="../images/reset.svg">
- </button>
+ <div id="simulation" v-show="!infoVisible">
+ <div id="input" hidden>
+ <section>
+ <label for="mass1Input"><b>Mass 1:</b> {{ mass1.toFixed(1) }} Kg</label>
+ <input type="range" min="1" max="10" step="0.1" v-model.number="mass1" @input="reset" @dblclick="mass1=1" :disabled="active" id="mass1Input">
+ </section>
+ <section>
+ <label for="mass2Input"><b>Mass 2:</b> {{ mass2.toFixed(1) }} Kg</label>
+ <input type="range" min="1" max="10" step="0.1" v-model.number="mass2" @input="reset" @dblclick="mass2=10" :disabled="active" id="mass2Input">
+ </section>
+ <section>
+ <label for="angleInput"><b>Angle:</b> {{ angle.toFixed(0) }}<sup>o</sup></label>
+ <input type="range" min="0" max="90" step="1" v-model.number="angle" @input="reset" @dblclick="angle=90" :disabled="active" id="angleInput">
+ </section>
+ <section>
+ <label for="gravityInput"><b>Gravity:</b> {{ gravity.toFixed(1) }} m/s<sup>2</sup></label>
+ <input type="range" min="1" max="10" step="0.1" v-model.number="gravity" @input="reset" @dblclick="gravity=9.8" :disabled="active" id="gravityInput">
+ </section>
</div>
- <svg width="400px" viewBox="-0.1 -0.1 8.3 8.1">
- <!-- Pulley -->
- <circle cx="1.5" cy="1.5" r="1" stroke-width="0.1" stroke="#808080" fill="#404040"></circle>
- <!-- 1st weight -->
- <line x1="0.5" y1="1.5" x2="0.5" :y2="4.5 + displacement" stroke-width="0.1" stroke="#808080"></line>
- <circle cx="0.5" :cy="4.5 + displacement" :r="0.03*mass1+0.2" fill="#ff0000"></circle>
+ <div id="output" hidden>
+ <div id="simControls">
+ <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')" :disabled="Math.abs(displacement) === 3">
+ <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
+ </button>
+ <button @click="update" class="icon" title="Step Forward" :disabled="Math.abs(displacement) === 3 || active">
+ <img alt="" src="../images/step-forward.svg">
+ </button>
+ <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
+ <img alt="" src="../images/reset.svg">
+ </button>
+ </div>
+ <svg width="400px" viewBox="-0.1 -0.1 8.3 8.1">
+ <!-- Pulley -->
+ <circle cx="1.5" cy="1.5" r="1" stroke-width="0.1" stroke="#808080" fill="#404040"></circle>
+
+ <!-- 1st weight -->
+ <line x1="0.5" y1="1.5" x2="0.5" :y2="4.5 + displacement" stroke-width="0.1" stroke="#808080"></line>
+ <circle cx="0.5" :cy="4.5 + displacement" :r="0.03*mass1+0.2" fill="#ff0000"></circle>
+
+ <!-- 2nd weight -->
+ <line :x1="positionVector[0] + 1.5" :y1="positionVector[1] + 1.5" :x2="positionVector[2] + 1.5" :y2="positionVector[3] + 1.5" stroke-width="0.1" stroke="#808080"></line>
+ <circle :cx="positionVector[2] + 1.5" :cy="positionVector[3] + 1.5" :r="0.03*mass2+0.2" fill="#0000ff"></circle>
+ </svg>
+ </div>
- <!-- 2nd weight -->
- <line :x1="positionVector[0] + 1.5" :y1="positionVector[1] + 1.5" :x2="positionVector[2] + 1.5" :y2="positionVector[3] + 1.5" stroke-width="0.1" stroke="#808080"></line>
- <circle :cx="positionVector[2] + 1.5" :cy="positionVector[3] + 1.5" :r="0.03*mass2+0.2" fill="#0000ff"></circle>
- </svg>
+ <div id="data" hidden>
+ <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
+ <label><b>Displacement:</b> {{ Math.abs(displacement).toFixed(2) }} m</label>
+ <label><b>Velocity:</b> {{ velocity.toFixed(2) }} m/s</label>
+ <label><b>Acceleration:</b> {{ acceleration.toFixed(2) }} m/s<sup>2</sup></label>
+ </div>
</div>
- <div id="data" hidden>
- <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
- <label><b>Displacement:</b> {{ Math.abs(displacement).toFixed(2) }} m</label>
- <label><b>Velocity:</b> {{ velocity.toFixed(2) }} m/s</label>
- <label><b>Acceleration:</b> {{ acceleration.toFixed(2) }} m/s<sup>2</sup></label>
+ <div id="info" v-show="infoVisible" hidden>
+ <h2>Simulation Information</h2>
+ <p>This simulation consists of two weights connected by a massless string over an ideal massless pulley.</p>
+
+ <p>You can control the simulation using these variables:</p>
+ <ul>
+ <li><b>Mass 1:</b> The mass of the red weight</li>
+ <li><b>Mass 2:</b> The mass of the blue weight</li>
+ <li><b>Angle:</b> The angle of the incline that the blue weight sits on</li>
+ <li><b>Gravity:</b> The acceleration due to gravity</li>
+ </ul>
+
+ <p>The simulation also contains these output measurements:</p>
+ <ul>
+ <li><b>Time:</b> The time that has passed</li>
+ <li><b>Displacement:</b> The current displacement of the blue weight</li>
+ <li><b>Velocity:</b> The current velocity of the blue weight</li>
+ <li><b>Acceleration:</b> The current acceleration of the blue weight</li>
+ </ul>
+
+ <p>Controls:</p>
+ <ul>
+ <li>Use the start <img alt="" src="../images/play.svg"> and stop <img alt="" src="../images/pause.svg"> buttons to start and stop the simulation</li>
+ <li>Use the step <img alt="" src="../images/step-forward.svg"> button to advance the simulation by 0.01 seconds</li>
+ <li>Use the reset <img alt="" src="../images/reset.svg"> button to restore the simulation to its initial state</li>
+ </ul>
</div>
</div>
</body>
diff --git a/simulations/atwood-machine.js b/simulations/atwood-machine.js
@@ -9,6 +9,7 @@ const App = {
active: false, // Whether the simulation is active
refreshRate: 0.01, // The simulation refresh rate (s)
intervalId: null, // The value returned by setInterval
+ infoVisible: false,
}
},
computed: {
@@ -59,6 +60,17 @@ const App = {
},
methods: {
/**
+ * Handle a keyup event (implements keyboard shortcuts)
+ * @param {object} e - The event args
+ */
+ keyup: function(e) {
+ if (e.key === "Escape") {
+ if (this.infoVisible) this.infoVisible = false;
+ else window.location.href = "../";
+ }
+ },
+
+ /**
* Toggle whether the simulation is active
*/
toggle: function() {
@@ -101,6 +113,14 @@ const App = {
return Math.cos(angle / 360 * 2 * Math.PI) * distance;
},
},
+ created: function() {
+ // Add keyup handler
+ window.addEventListener("keyup", this.keyup);
+ },
+ destroyed: function() {
+ // Remove keyup handler
+ window.removeEventListener("keyup", this.keyup);
+ },
}
@@ -114,4 +134,5 @@ function createApp() {
document.getElementById("input").hidden = false;
document.getElementById("output").hidden = false;
document.getElementById("data").hidden = false;
+ document.getElementById("info").hidden = false;
}
diff --git a/simulations/circular-motion.html b/simulations/circular-motion.html
@@ -15,6 +15,8 @@
<div id="app">
<header>
<a title="Home" href="../" class="icon"><img alt="" src="../images/home.svg"></a>
+ <button v-show="!infoVisible" title="About" class="icon" @click="infoVisible=true"><img alt="" src="../images/info.svg"></button>
+ <button v-show="infoVisible" title="Close" class="icon" @click="infoVisible=false"><img alt="" src="../images/x.svg"></button>
<h1>Circular Motion</h1>
</header>
@@ -22,55 +24,83 @@
<p>This simulation requires JavaScript</p>
</noscript>
- <div id="input" hidden>
- <section>
- <label for="massInput"><b>Mass:</b> {{ mass.toFixed(1) }} Kg</label>
- <input type="range" min="1" max="10" step="0.1" v-model.number="mass" @input="reset" @dblclick="mass=5" :disabled="active" id="massInput">
- </section>
- <section>
- <label for="forceInput"><b>Centripetal Force:</b> {{ force.toFixed(1) }} N</label>
- <input type="range" min="1" max="10" step="0.1" v-model.number="force" @input="reset" @dblclick="force=5" :disabled="active" id="forceInput">
- </section>
- <section>
- <label for="radiusInput"><b>Radius:</b> {{ radius.toFixed(2) }} m</label>
- <input type="range" min="0.1" max="1" step="0.01" v-model.number="radius" @input="reset" @dblclick="radius=1" :disabled="active" id="radiusInput">
- </section>
- </div>
-
- <div id="output" hidden>
- <div id="simControls">
- <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')">
- <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
- </button>
- <button @click="update" class="icon" title="Step Forward" :disabled="active">
- <img alt="" src="../images/step-forward.svg">
- </button>
- <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
- <img alt="" src="../images/reset.svg">
- </button>
+ <div id="simulation" v-show="!infoVisible">
+ <div id="input" hidden>
+ <section>
+ <label for="massInput"><b>Mass:</b> {{ mass.toFixed(1) }} Kg</label>
+ <input type="range" min="1" max="10" step="0.1" v-model.number="mass" @input="reset" @dblclick="mass=5" :disabled="active" id="massInput">
+ </section>
+ <section>
+ <label for="forceInput"><b>Centripetal Force:</b> {{ force.toFixed(1) }} N</label>
+ <input type="range" min="1" max="10" step="0.1" v-model.number="force" @input="reset" @dblclick="force=5" :disabled="active" id="forceInput">
+ </section>
+ <section>
+ <label for="radiusInput"><b>Radius:</b> {{ radius.toFixed(2) }} m</label>
+ <input type="range" min="0.1" max="1" step="0.01" v-model.number="radius" @input="reset" @dblclick="radius=1" :disabled="active" id="radiusInput">
+ </section>
</div>
- <svg width="400px" viewBox="-1.5 -1.5 3 3">
- <!-- Circle outline -->
- <circle cx="0" cy="0" :r="radius" stroke="#808080" stroke-width="0.01" fill="none" stroke-dasharray="0.05,0.05"/>
- <circle cx="0" cy="0" r="0.025" fill="#000000"></circle>
- <!-- Velocity vector-->
- <line :x1="position[0]" :y1="position[1]" :x2="velocityVector[0]" :y2="velocityVector[1]" stroke="black" stroke-width="0.025"/>
- <path :d="`M${velocityVector[2]} ${velocityVector[3]} L${velocityVector[0]} ${velocityVector[1]} L${velocityVector[4]} ${velocityVector[5]} Z`" stroke="#000000" stroke-width="0.025" fill="#000000"/>
+ <div id="output" hidden>
+ <div id="simControls">
+ <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')">
+ <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
+ </button>
+ <button @click="update" class="icon" title="Step Forward" :disabled="active">
+ <img alt="" src="../images/step-forward.svg">
+ </button>
+ <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
+ <img alt="" src="../images/reset.svg">
+ </button>
+ </div>
+ <svg width="400px" viewBox="-1.5 -1.5 3 3">
+ <!-- Circle outline -->
+ <circle cx="0" cy="0" :r="radius" stroke="#808080" stroke-width="0.01" fill="none" stroke-dasharray="0.05,0.05"/>
+ <circle cx="0" cy="0" r="0.025" fill="#000000"></circle>
+
+ <!-- Velocity vector-->
+ <line :x1="position[0]" :y1="position[1]" :x2="velocityVector[0]" :y2="velocityVector[1]" stroke="black" stroke-width="0.025"/>
+ <path :d="`M${velocityVector[2]} ${velocityVector[3]} L${velocityVector[0]} ${velocityVector[1]} L${velocityVector[4]} ${velocityVector[5]} Z`" stroke="#000000" stroke-width="0.025" fill="#000000"/>
- <!-- Centripetal force vector-->
- <line :x1="position[0]" :y1="position[1]" :x2="forceVector[0]" :y2="forceVector[1]" stroke="gray" stroke-width="0.025"/>
- <path :d="`M${forceVector[2]} ${forceVector[3]} L${forceVector[0]} ${forceVector[1]} L${forceVector[4]} ${forceVector[5]} Z`" stroke="#808080" stroke-width="0.025" fill="#808080"/>
+ <!-- Centripetal force vector-->
+ <line :x1="position[0]" :y1="position[1]" :x2="forceVector[0]" :y2="forceVector[1]" stroke="gray" stroke-width="0.025"/>
+ <path :d="`M${forceVector[2]} ${forceVector[3]} L${forceVector[0]} ${forceVector[1]} L${forceVector[4]} ${forceVector[5]} Z`" stroke="#808080" stroke-width="0.025" fill="#808080"/>
+
+ <!-- Object -->
+ <circle :cx="position[0]" :cy="position[1]" :r="(0.005*mass)+0.025" fill="#ff0000"/>
+ </svg>
+ </div>
- <!-- Object -->
- <circle :cx="position[0]" :cy="position[1]" :r="(0.005*mass)+0.025" fill="#ff0000"/>
- </svg>
+ <div id="data" hidden>
+ <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
+ <label><b>Speed:</b> {{ speed.toFixed(2) }} m/s</label>
+ <label><b>Period:</b> {{ period.toFixed(2) }} s</label>
+ </div>
</div>
- <div id="data" hidden>
- <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
- <label><b>Speed:</b> {{ speed.toFixed(2) }} m/s</label>
- <label><b>Period:</b> {{ period.toFixed(2) }} s</label>
+ <div id="info" v-show="infoVisible" hidden>
+ <h2>Simulation Information</h2>
+ <p>This simulation consists of an object that is spun in a circle by a centripetal force.</p>
+
+ <p>You can control the simulation using these variables:</p>
+ <ul>
+ <li><b>Mass:</b> The mass of the object</li>
+ <li><b>Centripetal Force:</b> The magnitude of the centripetal force</li>
+ <li><b>Radius:</b> The radius of the circle</li>
+ </ul>
+
+ <p>The simulation also contains these output measurements:</p>
+ <ul>
+ <li><b>Time:</b> The time that has passed</li>
+ <li><b>Speed:</b> The current speed of the object</li>
+ <li><b>Period:</b> The period of the object</li>
+ </ul>
+
+ <p>Controls:</p>
+ <ul>
+ <li>Use the start <img alt="" src="../images/play.svg"> and stop <img alt="" src="../images/pause.svg"> buttons to start and stop the simulation</li>
+ <li>Use the step <img alt="" src="../images/step-forward.svg"> button to advance the simulation by 0.01 seconds</li>
+ <li>Use the reset <img alt="" src="../images/reset.svg"> button to restore the simulation to its initial state</li>
+ </ul>
</div>
</div>
</body>
diff --git a/simulations/circular-motion.js b/simulations/circular-motion.js
@@ -8,6 +8,7 @@ const App = {
active: false, // Whether the simulation is active
refreshRate: 0.01, // The simulation refresh rate (s)
intervalId: null, // The value returned by setInterval
+ infoVisible: false,
}
},
computed: {
@@ -84,6 +85,17 @@ const App = {
},
methods: {
/**
+ * Handle a keyup event (implements keyboard shortcuts)
+ * @param {object} e - The event args
+ */
+ keyup: function(e) {
+ if (e.key === "Escape") {
+ if (this.infoVisible) this.infoVisible = false;
+ else window.location.href = "../";
+ }
+ },
+
+ /**
* Toggle whether the simulation is active
*/
toggle: function() {
@@ -126,6 +138,14 @@ const App = {
return -Math.cos(angle * 2 * Math.PI) * distance;
},
},
+ created: function() {
+ // Add keyup handler
+ window.addEventListener("keyup", this.keyup);
+ },
+ destroyed: function() {
+ // Remove keyup handler
+ window.removeEventListener("keyup", this.keyup);
+ },
}
@@ -139,4 +159,5 @@ function createApp() {
document.getElementById("input").hidden = false;
document.getElementById("output").hidden = false;
document.getElementById("data").hidden = false;
+ document.getElementById("info").hidden = false;
}
diff --git a/simulations/horizontal-motion.html b/simulations/horizontal-motion.html
@@ -15,6 +15,8 @@
<div id="app">
<header>
<a title="Home" href="../" class="icon"><img alt="" src="../images/home.svg"></a>
+ <button v-show="!infoVisible" title="About" class="icon" @click="infoVisible=true"><img alt="" src="../images/info.svg"></button>
+ <button v-show="infoVisible" title="Close" class="icon" @click="infoVisible=false"><img alt="" src="../images/x.svg"></button>
<h1>Horizontal Motion</h1>
</header>
@@ -22,61 +24,92 @@
<p>This simulation requires JavaScript</p>
</noscript>
- <div id="input" hidden>
- <section>
- <label for="massInput"><b>Mass:</b> {{ mass.toFixed(1) }} Kg</label>
- <input type="range" min="1" max="10" step="0.1" v-model.number="mass" @dblclick="mass=5" id="massInput">
- </section>
- <section>
- <label for="forceInput"><b>Applied Force:</b> {{ force.toFixed(1) }} N</label>
- <input type="range" min="-10" max="10" step="0.1" v-model.number="force" @dblclick="force=0" id="forceInput">
- </section>
- <section>
- <label for="staticFrictionInput"><b>Static Friction:</b> {{ staticFriction.toFixed(2) }}</label>
- <input type="range" min="0" max="1" step="0.01" v-model.number="staticFriction" @dblclick="staticFriction=0" id="staticFrictionInput">
- </section>
- <section>
- <label for="kineticFrictionInput"><b>Kinetic Friction:</b> {{ kineticFriction.toFixed(2) }}</label>
- <input type="range" min="0" max="1" step="0.01" v-model.number="kineticFriction" @dblclick="kineticFriction=0" id="kineticFrictionInput">
- </section>
- <section>
- <label for="gravityInput"><b>Gravity:</b> {{ gravity.toFixed(1) }} m/s<sup>2</sup></label>
- <input type="range" min="0" max="10" step="0.1" v-model.number="gravity" @dblclick="gravity=9.8" id="gravityInput">
- </section>
- </div>
-
- <div id="output" hidden>
- <div id="simControls">
- <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')">
- <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
- </button>
- <button @click="update" class="icon" title="Step Forward" :disabled="active">
- <img alt="" src="../images/step-forward.svg">
- </button>
- <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
- <img alt="" src="../images/reset.svg">
- </button>
+ <div id="simulation" v-show="!infoVisible">
+ <div id="input" hidden>
+ <section>
+ <label for="massInput"><b>Mass:</b> {{ mass.toFixed(1) }} Kg</label>
+ <input type="range" min="1" max="10" step="0.1" v-model.number="mass" @dblclick="mass=5" id="massInput">
+ </section>
+ <section>
+ <label for="forceInput"><b>Applied Force:</b> {{ force.toFixed(1) }} N</label>
+ <input type="range" min="-10" max="10" step="0.1" v-model.number="force" @dblclick="force=0" id="forceInput">
+ </section>
+ <section>
+ <label for="staticFrictionInput"><b>Static Friction:</b> {{ staticFriction.toFixed(2) }}</label>
+ <input type="range" min="0" max="1" step="0.01" v-model.number="staticFriction" @dblclick="staticFriction=0" id="staticFrictionInput">
+ </section>
+ <section>
+ <label for="kineticFrictionInput"><b>Kinetic Friction:</b> {{ kineticFriction.toFixed(2) }}</label>
+ <input type="range" min="0" max="1" step="0.01" v-model.number="kineticFriction" @dblclick="kineticFriction=0" id="kineticFrictionInput">
+ </section>
+ <section>
+ <label for="gravityInput"><b>Gravity:</b> {{ gravity.toFixed(1) }} m/s<sup>2</sup></label>
+ <input type="range" min="0" max="10" step="0.1" v-model.number="gravity" @dblclick="gravity=9.8" id="gravityInput">
+ </section>
</div>
- <svg width="800px" viewBox="0 0 10 2">
- <!-- Ruler marks -->
- <line v-for="(_,n) in 101" :x1="references[1] + n*0.1" y1="1.7" :x2="references[1] + n*0.1" y2="1.9" stroke-width="0.01" stroke="#000000"></line>
- <line v-for="(_,n) in 11" :x1="references[0] + n" y1="1.6" :x2="references[0] + n" y2="2.0" stroke-width="0.02" stroke="#000000"></line>
- <!-- Force vector -->
- <line x1="5" :y1="1.2 - 0.05*mass+0.1" :x2="5+0.3*force" :y2="1.2 - 0.05*mass+0.1" stroke-width="0.1" stroke="#808080"></line>
- <path v-show="force>0" :d="`M${5+0.3*force} ${1.3 - 0.05*mass+0.1} L${5.1+0.3*force} ${1.2 - 0.05*mass+0.1} L${5+0.3*force} ${1.1 - 0.05*mass+0.1} Z`" stroke="#808080" stroke-width="0.025" fill="#808080"/>
- <path v-show="force<0" :d="`M${5+0.3*force} ${1.3 - 0.05*mass+0.1} L${4.9+0.3*force} ${1.2 - 0.05*mass+0.1} L${5+0.3*force} ${1.1 - 0.05*mass+0.1} Z`" stroke="#808080" stroke-width="0.025" fill="#808080"/>
+ <div id="output" hidden>
+ <div id="simControls">
+ <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')">
+ <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
+ </button>
+ <button @click="update" class="icon" title="Step Forward" :disabled="active">
+ <img alt="" src="../images/step-forward.svg">
+ </button>
+ <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
+ <img alt="" src="../images/reset.svg">
+ </button>
+ </div>
+ <svg width="800px" viewBox="0 0 10 2">
+ <!-- Ruler marks -->
+ <line v-for="(_,n) in 101" :x1="references[1] + n*0.1" y1="1.7" :x2="references[1] + n*0.1" y2="1.9" stroke-width="0.01" stroke="#000000"></line>
+ <line v-for="(_,n) in 11" :x1="references[0] + n" y1="1.6" :x2="references[0] + n" y2="2.0" stroke-width="0.02" stroke="#000000"></line>
+
+ <!-- Force vector -->
+ <line x1="5" :y1="1.2 - 0.05*mass+0.1" :x2="5+0.3*force" :y2="1.2 - 0.05*mass+0.1" stroke-width="0.1" stroke="#808080"></line>
+ <path v-show="force>0" :d="`M${5+0.3*force} ${1.3 - 0.05*mass+0.1} L${5.1+0.3*force} ${1.2 - 0.05*mass+0.1} L${5+0.3*force} ${1.1 - 0.05*mass+0.1} Z`" stroke="#808080" stroke-width="0.025" fill="#808080"/>
+ <path v-show="force<0" :d="`M${5+0.3*force} ${1.3 - 0.05*mass+0.1} L${4.9+0.3*force} ${1.2 - 0.05*mass+0.1} L${5+0.3*force} ${1.1 - 0.05*mass+0.1} Z`" stroke="#808080" stroke-width="0.025" fill="#808080"/>
+
+ <!-- Object -->
+ <circle cx="5" :cy="1.2 - 0.05*mass+0.1" :r="0.05*mass+0.1" fill="#ff0000"></circle>
+ </svg>
+ </div>
- <!-- Object -->
- <circle cx="5" :cy="1.2 - 0.05*mass+0.1" :r="0.05*mass+0.1" fill="#ff0000"></circle>
- </svg>
+ <div id="data" hidden>
+ <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
+ <label><b>Position:</b> {{ position.toFixed(2) }} m</label>
+ <label><b>Velocity:</b> {{ velocity.toFixed(2) }} m/s</label>
+ <label><b>Acceleration:</b> {{ acceleration.toFixed(2) }} m/s<sup>2</sup></label>
+ </div>
</div>
- <div id="data" hidden>
- <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
- <label><b>Position:</b> {{ position.toFixed(2) }} m</label>
- <label><b>Velocity:</b> {{ velocity.toFixed(2) }} m/s</label>
- <label><b>Acceleration:</b> {{ acceleration.toFixed(2) }} m/s<sup>2</sup></label>
+ <div id="info" v-show="infoVisible" hidden>
+ <h2>Simulation Information</h2>
+ <p>This simulation consists an object that can be accelerated horizontally along a surface.</p>
+
+ <p>You can control the simulation using these variables:</p>
+ <ul>
+ <li><b>Mass:</b> The mass of the object</li>
+ <li><b>Applied Force:</b> The force applied on the ball</li>
+ <li><b>Static Friction:</b> The coefficient of static friction</li>
+ <li><b>Kinetic Friction:</b> The coefficient of kinetic friction</li>
+ <li><b>Gravity:</b> The acceleration due to gravity</li>
+ </ul>
+
+ <p>The simulation also contains these output measurements:</p>
+ <ul>
+ <li><b>Time:</b> The time that has passed</li>
+ <li><b>Position:</b> The current displacement of the object</li>
+ <li><b>Velocity:</b> The current velocity of the object</li>
+ <li><b>Acceleration:</b> The current acceleration of the object</li>
+ </ul>
+
+ <p>Controls:</p>
+ <ul>
+ <li>Use the start <img alt="" src="../images/play.svg"> and stop <img alt="" src="../images/pause.svg"> buttons to start and stop the simulation</li>
+ <li>Use the step <img alt="" src="../images/step-forward.svg"> button to advance the simulation by 0.01 seconds</li>
+ <li>Use the reset <img alt="" src="../images/reset.svg"> button to restore the simulation to its initial state</li>
+ </ul>
</div>
</div>
</body>
diff --git a/simulations/horizontal-motion.js b/simulations/horizontal-motion.js
@@ -17,6 +17,7 @@ const App = {
active: false, // Whether the simulation is active
refreshRate: 0.01, // The simulation refresh rate (s)
intervalId: null, // The value returned by setInterval
+ infoVisible: false,
}
},
computed: {
@@ -73,6 +74,17 @@ const App = {
},
methods: {
/**
+ * Handle a keyup event (implements keyboard shortcuts)
+ * @param {object} e - The event args
+ */
+ keyup: function(e) {
+ if (e.key === "Escape") {
+ if (this.infoVisible) this.infoVisible = false;
+ else window.location.href = "../";
+ }
+ },
+
+ /**
* Toggle whether the simulation is active
*/
toggle: function() {
@@ -96,7 +108,7 @@ const App = {
update: function() {
// Update time
this.time += this.refreshRate;
-
+
// Get updated velocity
let newVelocity = this.velocity + (this.acceleration * this.refreshRate)
@@ -119,6 +131,14 @@ const App = {
this.position += (this.velocity * this.refreshRate);
},
},
+ created: function() {
+ // Add keyup handler
+ window.addEventListener("keyup", this.keyup);
+ },
+ destroyed: function() {
+ // Remove keyup handler
+ window.removeEventListener("keyup", this.keyup);
+ },
}
@@ -132,4 +152,5 @@ function createApp() {
document.getElementById("input").hidden = false;
document.getElementById("output").hidden = false;
document.getElementById("data").hidden = false;
+ document.getElementById("info").hidden = false;
}
diff --git a/simulations/projectile-motion.html b/simulations/projectile-motion.html
@@ -17,6 +17,8 @@
<div id="app">
<header>
<a title="Home" href="../" class="icon"><img alt="" src="../images/home.svg"></a>
+ <button v-show="!infoVisible" title="About" class="icon" @click="infoVisible=true"><img alt="" src="../images/info.svg"></button>
+ <button v-show="infoVisible" title="Close" class="icon" @click="infoVisible=false"><img alt="" src="../images/x.svg"></button>
<h1>Projectile Motion</h1>
</header>
@@ -24,68 +26,100 @@
<p>This simulation requires JavaScript</p>
</noscript>
- <div id="input" hidden>
- <section>
- <label for="heightInput"><b>Height:</b> {{ height.toFixed(1) }} m</label>
- <input type="range" min="0" max="10" step="0.1" v-model.number="height" @input="reset" :disabled="active" @dblclick="height=1" id="heightInput">
- </section>
- <section>
- <label for="initialVelocityInput"><b>Velocity:</b> {{ initialVelocity.toFixed(1) }} m/s</label>
- <input type="range" min="0" max="10" step="0.1" v-model.number="initialVelocity" @input="reset" :disabled="active" @dblclick="initialVelocity=5" id="initialVelocityInput">
- </section>
- <section>
- <label for="angleInput"><b>Angle:</b> {{ angle.toFixed(0) }}<sup>o</sup></label>
- <input type="range" min="-90" max="90" step="1" v-model.number="angle" @input="reset" :disabled="active" @dblclick="angle=0" id="angleInput">
- </section>
- <section>
- <label for="gravityInput"><b>Gravity:</b> {{ gravity.toFixed(1) }} m/s<sup>2</sup></label>
- <input type="range" min="0" max="10" step="0.1" v-model.number="gravity" @input="reset" :disabled="active" @dblclick="gravity=9.8" id="gravityInput">
- </section>
- </div>
+ <div id="simulation" v-show="!infoVisible">
+ <div id="input" hidden>
+ <section>
+ <label for="heightInput"><b>Height:</b> {{ height.toFixed(1) }} m</label>
+ <input type="range" min="0" max="10" step="0.1" v-model.number="height" @input="reset" :disabled="active" @dblclick="height=1" id="heightInput">
+ </section>
+ <section>
+ <label for="initialVelocityInput"><b>Velocity:</b> {{ initialVelocity.toFixed(1) }} m/s</label>
+ <input type="range" min="0" max="10" step="0.1" v-model.number="initialVelocity" @input="reset" :disabled="active" @dblclick="initialVelocity=5" id="initialVelocityInput">
+ </section>
+ <section>
+ <label for="angleInput"><b>Angle:</b> {{ angle.toFixed(0) }}<sup>o</sup></label>
+ <input type="range" min="-90" max="90" step="1" v-model.number="angle" @input="reset" :disabled="active" @dblclick="angle=0" id="angleInput">
+ </section>
+ <section>
+ <label for="gravityInput"><b>Gravity:</b> {{ gravity.toFixed(1) }} m/s<sup>2</sup></label>
+ <input type="range" min="0" max="10" step="0.1" v-model.number="gravity" @input="reset" :disabled="active" @dblclick="gravity=9.8" id="gravityInput">
+ </section>
+ </div>
+
+ <div id="output" hidden>
+ <div id="simControls">
+ <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')" :disabled="time !== 0 && position.y <= 0">
+ <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
+ </button>
+ <button @click="update" class="icon" title="Step Forward" :disabled="(time !== 0 && position.y <= 0) || active">
+ <img alt="" src="../images/step-forward.svg">
+ </button>
+ <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
+ <img alt="" src="../images/reset.svg">
+ </button>
+ </div>
+ <div id="zoomControls">
+ <button @click="svg.zoomOut()" class="icon" title="Zoom Out">
+ <img alt="" src="../images/zoom-out.svg">
+ </button>
+ <button @click="svg.resetZoom();svg.resetPan()" class="icon" title="Reset Zoom">
+ <img alt="" src="../images/home.svg">
+ </button>
+ <button @click="svg.zoomIn()" class="icon" title="Zoom In">
+ <img alt="" src="../images/zoom-in.svg">
+ </button>
+ </div>
+ <svg width="400px" height="400px" viewBox="-1 -1 101 102">
+ <!-- Ground -->
+ <line x1="-10000" y1="101" x2="10000" y2="101" stroke-width="1" stroke="#404040"></line>
+
+ <!-- Path -->
+ <circle v-for="position in positions" :cx="10*position.x" :cy="100 - 10*position.y" r="0.5" fill="#808080"></circle>
- <div id="output" hidden>
- <div id="simControls">
- <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')" :disabled="time !== 0 && position.y <= 0">
- <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
- </button>
- <button @click="update" class="icon" title="Step Forward" :disabled="(time !== 0 && position.y <= 0) || active">
- <img alt="" src="../images/step-forward.svg">
- </button>
- <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
- <img alt="" src="../images/reset.svg">
- </button>
+ <!-- Launch vector -->
+ <line :x1="10*position.x" :y1="100 - 10*position.y" :x2="10*launchArrow[1].x" :y2="100 - 10*launchArrow[1].y" stroke-width="1" stroke="#808080"></line>
+ <path :d="`M${10*launchArrow[0].x} ${100 - 10*launchArrow[0].y} L${10*launchArrow[1].x} ${100 - 10*launchArrow[1].y} L${10*launchArrow[2].x} ${100 - 10*launchArrow[2].y} Z`" stroke="#808080" stroke-width="1" fill="#808080"/>
+
+ <!-- Projectile -->
+ <circle :cx="10*position.x" :cy="100 - 10*position.y" r="1" fill="#ff0000"></circle>
+ </svg>
</div>
- <div id="zoomControls">
- <button @click="svg.zoomOut()" class="icon" title="Zoom Out">
- <img alt="" src="../images/zoom-out.svg">
- </button>
- <button @click="svg.resetZoom();svg.resetPan()" class="icon" title="Reset Zoom">
- <img alt="" src="../images/home.svg">
- </button>
- <button @click="svg.zoomIn()" class="icon" title="Zoom In">
- <img alt="" src="../images/zoom-in.svg">
- </button>
+
+ <div id="data" hidden>
+ <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
+ <label><b>Position:</b> {{ position.x.toFixed(2) }} m, {{ position.y > 0 ? position.y.toFixed(2) : 0.0.toFixed(2) }} m</label>
+ <label><b>Velocity:</b> {{ velocity.total.toFixed(2) }} m/s at {{ velocity.angle.toFixed(1) }}<sup>o</sup></label>
</div>
- <svg width="400px" height="400px" viewBox="-1 -1 101 102">
- <!-- Ground -->
- <line x1="-10000" y1="101" x2="10000" y2="101" stroke-width="1" stroke="#404040"></line>
+ </div>
- <!-- Path -->
- <circle v-for="position in positions" :cx="10*position.x" :cy="100 - 10*position.y" r="0.5" fill="#808080"></circle>
+ <div id="info" v-show="infoVisible" hidden>
+ <h2>Simulation Information</h2>
+ <p>This simulation consists of a projectile that can be launched.</p>
- <!-- Launch vector -->
- <line :x1="10*position.x" :y1="100 - 10*position.y" :x2="10*launchArrow[1].x" :y2="100 - 10*launchArrow[1].y" stroke-width="1" stroke="#808080"></line>
- <path :d="`M${10*launchArrow[0].x} ${100 - 10*launchArrow[0].y} L${10*launchArrow[1].x} ${100 - 10*launchArrow[1].y} L${10*launchArrow[2].x} ${100 - 10*launchArrow[2].y} Z`" stroke="#808080" stroke-width="1" fill="#808080"/>
+ <p>You can control the simulation using these variables:</p>
+ <ul>
+ <li><b>Height:</b> The initial height of the projectile</li>
+ <li><b>Velocity:</b> The initial velocity of the projectile</li>
+ <li><b>Angle:</b> The angle to launch the projectile at</li>
+ <li><b>Gravity:</b> The acceleration due to gravity</li>
+ </ul>
- <!-- Projectile -->
- <circle :cx="10*position.x" :cy="100 - 10*position.y" r="1" fill="#ff0000"></circle>
- </svg>
- </div>
+ <p>The simulation also contains these output measurements:</p>
+ <ul>
+ <li><b>Time:</b> The time that has passed</li>
+ <li><b>Position:</b> The current position of the projectile</li>
+ <li><b>Velocity:</b> The current velocity of the projectile</li>
+ </ul>
- <div id="data" hidden>
- <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
- <label><b>Position:</b> {{ position.x.toFixed(2) }} m, {{ position.y > 0 ? position.y.toFixed(2) : 0.0.toFixed(2) }} m</label>
- <label><b>Velocity:</b> {{ velocity.total.toFixed(2) }} m/s at {{ velocity.angle.toFixed(1) }}<sup>o</sup></label>
+ <p>Controls:</p>
+ <ul>
+ <li>Use the start <img alt="" src="../images/play.svg"> and stop <img alt="" src="../images/pause.svg"> buttons to start and stop the simulation</li>
+ <li>Use the step <img alt="" src="../images/step-forward.svg"> button to advance the simulation by 0.01 seconds</li>
+ <li>Use the reset <img alt="" src="../images/reset.svg"> button to restore the simulation to its initial state</li>
+ <li>Click and drag the simulation to pan</li>
+ <li>Use the zoom in <img alt="" src="../images/zoom-in.svg"> and zoom out <img alt="" src="../images/zoom-out.svg"> buttons or the mouse wheel to zoom in and out</li>
+ <li>Use the reset zoom <img alt="" src="../images/home.svg"> button to undo all zooming and panning</li>
+ </ul>
</div>
</div>
</body>
diff --git a/simulations/projectile-motion.js b/simulations/projectile-motion.js
@@ -13,6 +13,7 @@ const App = {
active: false, // Whether the simulation is active
refreshRate: 0.01, // The simulation refresh rate (s)
intervalId: null, // The value returned by setInterval
+ infoVisible: false,
svg: null,
}
},
@@ -65,6 +66,17 @@ const App = {
},
methods: {
/**
+ * Handle a keyup event (implements keyboard shortcuts)
+ * @param {object} e - The event args
+ */
+ keyup: function(e) {
+ if (e.key === "Escape") {
+ if (this.infoVisible) this.infoVisible = false;
+ else window.location.href = "../";
+ }
+ },
+
+ /**
* Toggle whether the simulation is active
*/
toggle: function() {
@@ -125,6 +137,14 @@ const App = {
return Math.atan(opposite / adjacent) / (2 * Math.PI) * 360;
},
},
+ created: function() {
+ // Add keyup handler
+ window.addEventListener("keyup", this.keyup);
+ },
+ destroyed: function() {
+ // Remove keyup handler
+ window.removeEventListener("keyup", this.keyup);
+ },
}
@@ -138,6 +158,7 @@ function createApp() {
document.getElementById("input").hidden = false;
document.getElementById("output").hidden = false;
document.getElementById("data").hidden = false;
+ document.getElementById("info").hidden = false;
// Enable zooming
let mobileZoomPanHandler = {
diff --git a/simulations/simple-pendulum.html b/simulations/simple-pendulum.html
@@ -15,6 +15,8 @@
<div id="app">
<header>
<a title="Home" href="../" class="icon"><img alt="" src="../images/home.svg"></a>
+ <button v-show="!infoVisible" title="About" class="icon" @click="infoVisible=true"><img alt="" src="../images/info.svg"></button>
+ <button v-show="infoVisible" title="Close" class="icon" @click="infoVisible=false"><img alt="" src="../images/x.svg"></button>
<h1>Simple Pendulum</h1>
</header>
@@ -22,51 +24,80 @@
<p>This simulation requires JavaScript</p>
</noscript>
- <div id="input" hidden>
- <section>
- <label for="radiusInput"><b>Radius:</b> {{ radius.toFixed(1) }} m</label>
- <input type="range" min="1" max="10" step="0.1" v-model.number="radius" @input="reset" @dblclick="radius=5" :disabled="active" id="radiusInput">
- </section>
- <section>
- <label for="angleInput"><b>Initial Angle:</b> {{ initialAngle.toFixed(0) }}<sup>o</sup></label>
- <input type="range" min="-45" max="45" step="1" v-model.number="initialAngle" @input="reset" @dblclick="initialAngle=0" :disabled="active" id="angleInput">
- </section>
- <section>
- <label for="gravityInput"><b>Gravity:</b> {{ gravity.toFixed(1) }} m/s<sup>2</sup></label>
- <input type="range" min="0.1" max="10" step="0.1" v-model.number="gravity" @input="reset" @dblclick="gravity=9.8" :disabled="active" id="gravityInput">
- </section>
- </div>
-
- <div id="output" hidden>
- <div id="simControls">
- <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')">
- <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
- </button>
- <button @click="update" class="icon" title="Step Forward" :disabled="active">
- <img alt="" src="../images/step-forward.svg">
- </button>
- <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
- <img alt="" src="../images/reset.svg">
- </button>
+ <div id="simulation" v-show="!infoVisible">
+ <div id="input" hidden>
+ <section>
+ <label for="radiusInput"><b>Radius:</b> {{ radius.toFixed(1) }} m</label>
+ <input type="range" min="1" max="10" step="0.1" v-model.number="radius" @input="reset" @dblclick="radius=5" :disabled="active" id="radiusInput">
+ </section>
+ <section>
+ <label for="angleInput"><b>Initial Angle:</b> {{ initialAngle.toFixed(0) }}<sup>o</sup></label>
+ <input type="range" min="-45" max="45" step="1" v-model.number="initialAngle" @input="reset" @dblclick="initialAngle=0" :disabled="active" id="angleInput">
+ </section>
+ <section>
+ <label for="gravityInput"><b>Gravity:</b> {{ gravity.toFixed(1) }} m/s<sup>2</sup></label>
+ <input type="range" min="0.1" max="10" step="0.1" v-model.number="gravity" @input="reset" @dblclick="gravity=9.8" :disabled="active" id="gravityInput">
+ </section>
</div>
- <svg width="400px" viewBox="-8 -3 16 16">
- <!-- String -->
- <circle cx="0" c1="0" r="0.1" fill="#808080"></circle>
- <line x1="0" y1="0" :x2="position.x" :y2="-position.y" stroke="#808080" stroke-width="0.2"></line>
- <!-- Box -->
- <rect x="-2" y="-0.5" width="4" height="0.5" fill="#000000"></rect>
+ <div id="output" hidden>
+ <div id="simControls">
+ <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')">
+ <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
+ </button>
+ <button @click="update" class="icon" title="Step Forward" :disabled="active">
+ <img alt="" src="../images/step-forward.svg">
+ </button>
+ <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
+ <img alt="" src="../images/reset.svg">
+ </button>
+ </div>
+ <svg width="400px" viewBox="-8 -3 16 16">
+ <!-- String -->
+ <circle cx="0" c1="0" r="0.1" fill="#808080"></circle>
+ <line x1="0" y1="0" :x2="position.x" :y2="-position.y" stroke="#808080" stroke-width="0.2"></line>
+
+ <!-- Box -->
+ <rect x="-2" y="-0.5" width="4" height="0.5" fill="#000000"></rect>
+
+ <!-- Mass -->
+ <circle :cx="position.x" :cy="-position.y" r="0.5" fill="#ff0000"></circle>
+ </svg>
+ </div>
- <!-- Mass -->
- <circle :cx="position.x" :cy="-position.y" r="0.5" fill="#ff0000"></circle>
- </svg>
+ <div id="data" hidden>
+ <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
+ <label><b>Period:</b> {{ period.toFixed(2) }} s</label>
+ <label><b>Angle:</b> {{ angle.toFixed(0) }}<sup>o</sup></label>
+ <label><b>Acceleration:</b> {{ acceleration.toFixed(2) }} m/s<sup>2</sup></label>
+ </div>
</div>
- <div id="data" hidden>
- <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
- <label><b>Period:</b> {{ period.toFixed(2) }} s</label>
- <label><b>Angle:</b> {{ angle.toFixed(0) }}<sup>o</sup></label>
- <label><b>Acceleration:</b> {{ acceleration.toFixed(2) }} m/s<sup>2</sup></label>
+ <div id="info" v-show="infoVisible" hidden>
+ <h2>Simulation Information</h2>
+ <p>This simulation consists of a mass suspended from a frictionless pivot by a massless string.</p>
+
+ <p>You can control the simulation using these variables:</p>
+ <ul>
+ <li><b>Radius:</b> The length of the string</li>
+ <li><b>Initial Angle:</b> The initial angle of the pendulum</li>
+ <li><b>Gravity:</b> The acceleration due to gravity</li>
+ </ul>
+
+ <p>The simulation also contains these output measurements:</p>
+ <ul>
+ <li><b>Time:</b> The time that has passed</li>
+ <li><b>Period:</b> The period of the pendulum</li>
+ <li><b>Angle:</b> The current angle of the pendulum</li>
+ <li><b>Acceleration:</b> The current acceleration of the mass</li>
+ </ul>
+
+ <p>Controls:</p>
+ <ul>
+ <li>Use the start <img alt="" src="../images/play.svg"> and stop <img alt="" src="../images/pause.svg"> buttons to start and stop the simulation</li>
+ <li>Use the step <img alt="" src="../images/step-forward.svg"> button to advance the simulation by 0.01 seconds</li>
+ <li>Use the reset <img alt="" src="../images/reset.svg"> button to restore the simulation to its initial state</li>
+ </ul>
</div>
</div>
</body>
diff --git a/simulations/simple-pendulum.js b/simulations/simple-pendulum.js
@@ -8,6 +8,7 @@ const App = {
active: false, // Whether the simulation is active
refreshRate: 0.01, // The simulation refresh rate (s)
intervalId: null, // The value returned by setInterval
+ infoVisible: false,
}
},
computed: {
@@ -46,6 +47,17 @@ const App = {
},
methods: {
/**
+ * Handle a keyup event (implements keyboard shortcuts)
+ * @param {object} e - The event args
+ */
+ keyup: function(e) {
+ if (e.key === "Escape") {
+ if (this.infoVisible) this.infoVisible = false;
+ else window.location.href = "../";
+ }
+ },
+
+ /**
* Toggle whether the simulation is active
*/
toggle: function() {
@@ -68,6 +80,14 @@ const App = {
this.time += this.refreshRate;
},
},
+ created: function() {
+ // Add keyup handler
+ window.addEventListener("keyup", this.keyup);
+ },
+ destroyed: function() {
+ // Remove keyup handler
+ window.removeEventListener("keyup", this.keyup);
+ },
}
@@ -81,4 +101,5 @@ function createApp() {
document.getElementById("input").hidden = false;
document.getElementById("output").hidden = false;
document.getElementById("data").hidden = false;
+ document.getElementById("info").hidden = false;
}
diff --git a/simulations/spring-mass-system.html b/simulations/spring-mass-system.html
@@ -15,6 +15,8 @@
<div id="app">
<header>
<a title="Home" href="../" class="icon"><img alt="" src="../images/home.svg"></a>
+ <button v-show="!infoVisible" title="About" class="icon" @click="infoVisible=true"><img alt="" src="../images/info.svg"></button>
+ <button v-show="infoVisible" title="Close" class="icon" @click="infoVisible=false"><img alt="" src="../images/x.svg"></button>
<h1>Spring-Mass System</h1>
</header>
@@ -22,54 +24,91 @@
<p>This simulation requires JavaScript</p>
</noscript>
- <div id="input" hidden>
- <section>
- <label for="massInput"><b>Mass:</b> {{ mass.toFixed(1) }} Kg</label>
- <input type="range" min="0.1" max="10" step="0.1" v-model.number="mass" @input="reset" @dblclick="mass=5" :disabled="active" id="massInput">
- </section>
- <section>
- <label for="initialPositionInput"><b>Initial Position:</b> {{ initialPosition.toFixed(1) }} m</label>
- <input type="range" min="-10" max="10" step="0.1" v-model.number="initialPosition" @input="reset" @dblclick="initialPosition=0" :disabled="active" id="initialPositionInput">
- </section>
- <section>
- <label for="stiffnessInput"><b>Stiffness (k):</b> {{ stiffness.toFixed(0) }} N/m</label>
- <input type="range" min="1" max="100" step="1" v-model.number="stiffness" @input="reset" @dblclick="stiffness=5" :disabled="active" id="stiffnessInput">
- </section>
- </div>
-
- <div id="output" hidden>
- <div id="simControls">
- <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')">
- <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
- </button>
- <button @click="update" class="icon" title="Step Forward" :disabled="active">
- <img alt="" src="../images/step-forward.svg">
- </button>
- <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
- <img alt="" src="../images/reset.svg">
- </button>
+ <div id="simulation" v-show="!infoVisible">
+ <div id="input" hidden>
+ <section>
+ <label for="massInput"><b>Mass:</b> {{ mass.toFixed(1) }} Kg</label>
+ <input type="range" min="0.1" max="10" step="0.1" v-model.number="mass" @input="reset" @dblclick="mass=5" :disabled="active" id="massInput">
+ </section>
+ <section>
+ <label for="initialPositionInput"><b>Initial Position:</b> {{ initialPosition.toFixed(1) }} m</label>
+ <input type="range" min="-10" max="10" step="0.1" v-model.number="initialPosition" @input="reset" @dblclick="initialPosition=0" :disabled="active" id="initialPositionInput">
+ </section>
+ <section>
+ <label for="stiffnessInput"><b>Stiffness (k):</b> {{ stiffness.toFixed(0) }} N/m</label>
+ <input type="range" min="1" max="100" step="1" v-model.number="stiffness" @input="reset" @dblclick="stiffness=5" :disabled="active" id="stiffnessInput">
+ </section>
</div>
- <svg width="800px" viewBox="-11 0 22 4">
- <!-- Point of equilibrium -->
- <line x1="0" y1="0" :x2="0" y2="4" stroke="#808080" stroke-width="0.05" stroke-dasharray="0.1,0.1"></line>
- <!-- Spring -->
- <line x1="-11" y1="2" :x2="position" y2="2" stroke="#404040" stroke-width="0.1"></line>
- <rect x="-11" y="1" width="0.25" height="2" fill="#000000"></rect>
+ <div id="output" hidden>
+ <div id="simControls">
+ <button @click="toggle" class="icon" :title="active ? 'Pause' : (time === 0 ? 'Start' : 'Resume')">
+ <img alt="" :src="active ? '../images/pause.svg' : '../images/play.svg'">
+ </button>
+ <button @click="update" class="icon" title="Step Forward" :disabled="active">
+ <img alt="" src="../images/step-forward.svg">
+ </button>
+ <button @click="reset" class="icon" title="Reset" :disabled="time === 0">
+ <img alt="" src="../images/reset.svg">
+ </button>
+ </div>
+ <svg width="800px" viewBox="-11 0 22 4">
+ <!-- Point of equilibrium -->
+ <line x1="0" y1="0" :x2="0" y2="4" stroke="#808080" stroke-width="0.05" stroke-dasharray="0.1,0.1"></line>
+
+ <!-- Spring -->
+ <line x1="-11" y1="2" :x2="position" y2="2" stroke="#404040" stroke-width="0.1"></line>
+ <rect x="-11" y="1" width="0.25" height="2" fill="#000000"></rect>
+
+ <!-- Mass -->
+ <circle :cx="position" cy="2" :r="0.05*mass+0.1" fill="#ff0000"></circle>
+ </svg>
+ </div>
- <!-- Mass -->
- <circle :cx="position" cy="2" :r="0.05*mass+0.1" fill="#ff0000"></circle>
- </svg>
+ <div id="data" hidden>
+ <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
+ <label><b>Period:</b> {{ period.toFixed(2) }} s</label>
+ <label><b>Amplitude:</b> {{ Math.abs(initialPosition) }} m</label>
+ <label><b>Position:</b> {{ position.toFixed(2) }} m</label>
+ <label><b>Velocity:</b> {{ velocity.toFixed(2) }} m/s</label>
+ <label><b>Force:</b> {{ force.toFixed(2) }} N</label>
+ <label><b>Acceleration:</b> {{ acceleration.toFixed(2) }} m/s<sup>2</sup></label>
+ </div>
</div>
- <div id="data" hidden>
- <label><b>Time:</b> {{ time.toFixed(2) }} s</label>
- <label><b>Period:</b> {{ period.toFixed(2) }} s</label>
- <label><b>Amplitude:</b> {{ Math.abs(initialPosition) }} m</label>
- <label><b>Position:</b> {{ position.toFixed(2) }} m</label>
- <label><b>Velocity:</b> {{ velocity.toFixed(2) }} m/s</label>
- <label><b>Force:</b> {{ force.toFixed(2) }} N</label>
- <label><b>Acceleration:</b> {{ acceleration.toFixed(2) }} m/s<sup>2</sup></label>
+ <div id="info" v-show="infoVisible" hidden>
+ <h2>Simulation Information</h2>
+ <p>This simulation consists of an object attached to a spring that can stretch and contract.</p>
+
+ <p>You can control the simulation using these variables:</p>
+ <ul>
+ <li><b>Mass:</b> The mass of the object</li>
+ <li><b>Initial Position:</b> The initial displacement of the object relative to the point of equilibrium</li>
+ <li><b>Stiffness:</b> The stiffness coefficient of the spring</li>
+ </ul>
+
+ <p>The simulation also contains these output measurements:</p>
+ <ul>
+ <li><b>Time:</b> The time that has passed</li>
+ <li><b>Period:</b> The period of the spring-mass system</li>
+ <li><b>Amplitude:</b> The maximum displacement of the object relative to the point of equilibrium</li>
+ <li><b>Position:</b> The current displacement of the object relative to the point of equilibrium</li>
+ <li><b>Velocity:</b> The current velocity of the object</li>
+ <li><b>Force:</b> The force currently being exerted on the object by the spring</li>
+ <li><b>Acceleration:</b> The current acceleration of the object</li>
+ </ul>
+
+ <p>Controls:</p>
+ <ul>
+ <li>Use the start <img alt="" src="../images/play.svg"> and stop <img alt="" src="../images/pause.svg"> buttons to start and stop the simulation</li>
+ <li>Use the step <img alt="" src="../images/step-forward.svg"> button to advance the simulation by 0.01 seconds</li>
+ <li>Use the reset <img alt="" src="../images/reset.svg"> button to restore the simulation to its initial state</li>
+ </ul>
+
+ <p>Notes:</p>
+ <ul>
+ <li>The dotted line represents the point of equilibrium.</li>
+ </ul>
</div>
</div>
</body>
diff --git a/simulations/spring-mass-system.js b/simulations/spring-mass-system.js
@@ -8,6 +8,7 @@ const App = {
active: false, // Whether the simulation is active
refreshRate: 0.01, // The simulation refresh rate (s)
intervalId: null, // The value returned by setInterval
+ infoVisible: false,
}
},
computed: {
@@ -51,6 +52,17 @@ const App = {
},
methods: {
/**
+ * Handle a keyup event (implements keyboard shortcuts)
+ * @param {object} e - The event args
+ */
+ keyup: function(e) {
+ if (e.key === "Escape") {
+ if (this.infoVisible) this.infoVisible = false;
+ else window.location.href = "../";
+ }
+ },
+
+ /**
* Toggle whether the simulation is active
*/
toggle: function() {
@@ -73,6 +85,14 @@ const App = {
this.time += this.refreshRate;
},
},
+ created: function() {
+ // Add keyup handler
+ window.addEventListener("keyup", this.keyup);
+ },
+ destroyed: function() {
+ // Remove keyup handler
+ window.removeEventListener("keyup", this.keyup);
+ },
}
@@ -86,4 +106,5 @@ function createApp() {
document.getElementById("input").hidden = false;
document.getElementById("output").hidden = false;
document.getElementById("data").hidden = false;
+ document.getElementById("info").hidden = false;
}
diff --git a/simulations/styles.css b/simulations/styles.css
@@ -9,9 +9,6 @@ div[hidden] {
}
#app {
- display: flex;
- flex-direction: column;
- align-items: center;
touch-action: manipulation;
}
@@ -29,6 +26,10 @@ header a {
position: absolute;
left: 5px;
}
+header button {
+ position: absolute;
+ right: 5px;
+}
noscript {
color: #ff0000;
@@ -48,6 +49,12 @@ noscript {
cursor: default;
}
+#simulation {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
#input, #data {
display: flex;
flex-direction: row;
@@ -100,4 +107,31 @@ noscript {
position: static;
float: left;
}
+ header button.icon {
+ position: static;
+ float: right;
+ }
+}
+
+#info {
+ max-width: 750px;
+ margin: auto;
+ padding: 15px;
+ padding-top: 0px;
+}
+#info h2 {
+ text-align: center;
+ margin-bottom: 5px;
+}
+#info>* {
+ margin-bottom: 15px;
+}
+#info p + ul {
+ margin-top: -15px;
+}
+#info li {
+ margin-left: 30px;
+}
+#info img {
+ vertical-align: middle;
}