commit c67aafe3de02f141cdf453095bac2c9b67015112
parent d712e0cd68c330e2cba2e672fb8eb1fc9997ed51
Author: AsherMorgan <59518073+AsherMorgan@users.noreply.github.com>
Date: Sun, 28 Mar 2021 19:15:25 -0700
Add simple pendulum simulation
Diffstat:
4 files changed, 159 insertions(+), 1 deletion(-)
diff --git a/index.html b/index.html
@@ -23,6 +23,7 @@
<li><a title="Circular Motion" href="simulations/circular-motion.html">Circular Motion</a></li>
<li><a title="Horizontal Motion" href="simulations/horizontal-motion.html">Horizontal Motion</a></li>
<li><a title="Projectile Motion" href="simulations/projectile-motion.html">Projectile Motion</a></li>
+ <li><a title="Simple Pendulum" href="simulations/simple-pendulum.html">Simple Pendulum</a></li>
<li><a title="Spring-Mass System" href="simulations/spring-mass-system.html">Spring-Mass System</a></li>
</ul>
</body>
diff --git a/simulations/simple-pendulum.html b/simulations/simple-pendulum.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>Simple Pendulum Simulation</title>
+ <meta name="Description" content="Simple pendulum physics simulation and calculator">
+ <meta name="viewport" content="width=device-width">
+ <link rel="icon" type="image/png" href="../images/favicon-32.png">
+ <link rel="apple-touch-icon" href="../images/favicon-180.png">
+ <script src="https://cdn.jsdelivr.net/npm/vue@3.0.6"></script>
+ <link rel="stylesheet" href="styles.css">
+ <script src="simple-pendulum.js"></script>
+ </head>
+ <body onload="createApp()">
+ <div id="app">
+ <header>
+ <a title="Home" href="../" class="icon"><img alt="" src="../images/home.svg"></a>
+ <h1>Simple Pendulum</h1>
+ </header>
+
+ <noscript>
+ <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>
+ <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>
+
+ <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>
+ </body>
+</html>
diff --git a/simulations/simple-pendulum.js b/simulations/simple-pendulum.js
@@ -0,0 +1,84 @@
+const App = {
+ data: function() {
+ return {
+ radius: 5, // The radius of the pendulum (m)
+ initialAngle: 5, // The initial angle of the pendulum (degrees)
+ gravity: 9.8, // The acceleration due to gravity (m/s/s)
+ time: 0, // The time (s)
+ active: false, // Whether the simulation is active
+ refreshRate: 0.01, // The simulation refresh rate (s)
+ intervalId: null, // The value returned by setInterval
+ }
+ },
+ computed: {
+ /**
+ * The period of the pendulum
+ */
+ period: function() {
+ if (this.initialAngle === 0) return 0;
+ else return 2 * Math.PI * Math.sqrt(this.radius / this.gravity);
+ },
+
+ /**
+ * The angle of the pendulum
+ */
+ angle: function() {
+ if (this.initialAngle === 0) return 0;
+ else return this.initialAngle * Math.cos(this.time * 2 * Math.PI / this.period);
+ },
+
+ /**
+ * The position of the pendulum mass
+ */
+ position: function() {
+ return {
+ x: Math.sin(this.angle * 2 * Math.PI / 360) * this.radius,
+ y: -Math.cos(this.angle * 2 * Math.PI / 360) * this.radius,
+ };
+ },
+
+ /**
+ * The acceleration of the pendulum mass
+ */
+ acceleration: function() {
+ return -Math.sin(this.angle * 2 * Math.PI / 360) * this.gravity;
+ },
+ },
+ methods: {
+ /**
+ * Toggle whether the simulation is active
+ */
+ toggle: function() {
+ this.active = !this.active;
+ if (this.active) this.intervalID = setInterval(this.update, this.refreshRate * 1000);
+ else clearInterval(this.intervalID);
+ },
+
+ /**
+ * Reset the simulation
+ */
+ reset: function() {
+ this.time = 0;
+ },
+
+ /**
+ * Update the simulation output
+ */
+ update: function() {
+ this.time += this.refreshRate;
+ },
+ },
+}
+
+
+
+// Create Vue app
+function createApp() {
+ // Create app
+ Vue.createApp(App).mount("#app");
+
+ // Unhide app divs
+ document.getElementById("input").hidden = false;
+ document.getElementById("output").hidden = false;
+ document.getElementById("data").hidden = false;
+}
diff --git a/simulations/spring-mass-system.html b/simulations/spring-mass-system.html
@@ -28,7 +28,7 @@
<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>Position:</b> {{ initialPosition.toFixed(1) }} m</label>
+ <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>