aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rwxr-xr-xbuild.sh35
-rw-r--r--src/display.c19
-rw-r--r--src/display.h9
-rw-r--r--src/main.c55
-rw-r--r--src/shell.html109
6 files changed, 196 insertions, 33 deletions
diff --git a/README.md b/README.md
index 92bf865..90819a5 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
### Pendulum
-This is a simple pendulum c program that uses raylib
+This is a simple pendulum simulation in C that uses raylib. [Demo](https://batnako.net/demos/pendulum.html)
diff --git a/build.sh b/build.sh
index 95ea230..aec8362 100755
--- a/build.sh
+++ b/build.sh
@@ -7,9 +7,13 @@ FLAGS_DISPLAY="-lraylib -lm -lpthread -lGL -ldl -lrt -lX11"
SRC="src"
BIN="bin"
OBJ="obj"
+
RUN=0
VALGRIND=""
+WEB=0
+RAYLIB_SRC="/usr/local/src/raylib/src" #for web only
+
function __run__ {
RUN=1
}
@@ -25,6 +29,10 @@ function __clean__ {
kill $( ps -q $$ -o pgid= ) # exit
}
+function __web__ {
+ WEB=1
+}
+
set -xe
if ! { [[ $# -eq 0 ]]; } 2> /dev/null
@@ -36,11 +44,26 @@ fi
mkdir -p $BIN
mkdir -p $OBJ
-gcc -c -o $OBJ/main.o $SRC/main.c
-gcc -c -o $OBJ/display.o $SRC/display.c
-gcc -o $BIN/main $OBJ/main.o $OBJ/display.o $FLAGS $FLAGS_DISPLAY
-
-if ! { [[ $RUN -eq 0 ]]; } 2> /dev/null
+if { [[ $WEB -eq 0 ]]; } 2> /dev/null
then
- $BIN/main
+ gcc -c -o $OBJ/main.o $SRC/main.c
+ gcc -c -o $OBJ/display.o $SRC/display.c
+ gcc -o $BIN/main $OBJ/main.o $OBJ/display.o $FLAGS $FLAGS_DISPLAY
+
+ if ! { [[ $RUN -eq 0 ]]; } 2> /dev/null
+ then
+ $BIN/main
+ fi
+else
+ source emsdk_env.sh
+
+ emcc -o $BIN/pendulum.html $SRC/main.c $SRC/display.c \
+ $FLAGS -DPLATFORM_WEB -DRAYLIB_SRC="\"$RAYLIB_SRC/raylib.h\"" \
+ $RAYLIB_SRC/libraylib.a \
+ -I$RAYLIB_SRC/raylib.h \
+ -s USE_GLFW=3 -s ASYNCIFY \
+ --shell-file $SRC/shell.html
+
+ # delete e line that blocks backspace and tab
+ sed -i $(($(grep "keyCode === 8" $BIN/pendulum.js -n | cut -d : -f 1)+1))d $BIN/pendulum.js > /dev/null
fi
diff --git a/src/display.c b/src/display.c
index 2a46493..9ecb6cb 100644
--- a/src/display.c
+++ b/src/display.c
@@ -1,4 +1,9 @@
-#include <raylib.h>
+#ifdef PLATFORM_WEB
+ #include RAYLIB_SRC
+#else
+ #include <raylib.h>
+#endif
+
#include "display.h"
static dspl_createinfo info;
@@ -7,7 +12,7 @@ void dspl_create(dspl_createinfo createinfo)
{
info = createinfo;
}
-void dspl_start()
+void dspl_start(void)
{
InitWindow(info.width, info.height, info.name);
SetTargetFPS(info.fps);
@@ -22,21 +27,17 @@ void dspl_start()
}
}
-void dspl_destroy()
+void dspl_destroy(void)
{
CloseWindow();
}
void dspl_draw_circle(int cx, int cy, int r)
{
- int offsetx = (info.width/2);
- int offsety = (info.height/4);
- DrawCircle(offsetx + cx, offsety + cy, r, RED);
+ DrawCircle(OFFSETX + cx, OFFSETY + cy, r, RED);
}
void dspl_draw_line(int x1, int y1, int x2, int y2, int c)
{
- int offsetx = (info.width/2);
- int offsety = (info.height/4);
- DrawLine(offsetx + x1, offsety + y1, offsetx + x2, offsety + y2 , (c == 0) ? BLUE : RED);
+ DrawLine(OFFSETX + x1, OFFSETY + y1, OFFSETX + x2, OFFSETY + y2 , (c == 0) ? BLUE : RED);
}
diff --git a/src/display.h b/src/display.h
index 088feb7..1dadb0f 100644
--- a/src/display.h
+++ b/src/display.h
@@ -6,12 +6,15 @@ typedef struct dspl_createinfo {
int height;
char *name;
int fps;
- void (*update_func)();
+ void (*update_func)(void);
} dspl_createinfo;
+#define OFFSETX (info.width/2)
+#define OFFSETY (info.height/8)
+
void dspl_create(dspl_createinfo info);
-void dspl_start();
-void dspl_destroy();
+void dspl_start(void);
+void dspl_destroy(void);
void dspl_draw_line(int x1, int y1, int x2, int y2, int c);
void dspl_draw_circle(int cx, int cy, int r);
diff --git a/src/main.c b/src/main.c
index 4742ab8..dd37021 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,27 +1,36 @@
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
#include <math.h>
#include "display.h"
-#define W 1600
-#define H 900
-#define FPS 1000
-#define SCALE 1000
-#define SWING_SCALE 15
+#define W 800
+#define H 600
+
+#ifndef PLATFORM_WEB
+ #define FPS 120
+#else
+ #define FPS 60
+#endif
+
+#define SCALE 800
+#define CYCLES 5
#define RFPS 1/FPS
#define PI 3.141592654
+#define g -9.81f * SCALE
-const float s = (PI/180 * 25); // rad
-const float l = 0.5 * SCALE; // m
-const int m = 7; // kg
-const float g = -9.81f * SCALE; // m/s^2
+float s = 25; // starting agnle
+float l = 0.5; // lenght
+float m = 0.01; // mass in kg
float t;
float v;
float a;
float secs = 0.0f;
-int swings = 2 * SWING_SCALE;
+int swings = 2 * CYCLES;
int has_reached_starting_amp(float old_t)
{
@@ -34,17 +43,22 @@ int has_reached_starting_amp(float old_t)
void update()
{
+ if(secs == 0.0f) {
+ printf("\nStarting angle: %f", s/(PI/180));
+ printf("\nLenght: %f", l/SCALE);
+ printf("\nMass: %f\n", m/SCALE);
+ }
+
float old_t = t;
a = (g/l) * sinf(t);
v += a * RFPS;
t += v * RFPS;
if(has_reached_starting_amp(old_t)) {
- puts("new swing");
swings += 1;
}
- if(swings >= 2 * SWING_SCALE) {
- printf("period: %f\n", secs/SWING_SCALE);
+ if(swings >= 2 * CYCLES) {
+ printf("period: %f\n", secs/CYCLES);
secs = 0.0f;
swings = 0;
}
@@ -57,8 +71,21 @@ void update()
dspl_draw_circle(px, py, m);
}
-int main(void)
+int main(int argc, char **argv)
{
+ if(argc != 1 && argc != 4) {
+ puts("Please provide either 0 or 3 arguments:\n\t1st is starting angle in degrees\n\t2nd is lenght in meters\n\t3rd is mass in kilograms");
+ return 1;
+ }
+
+ float arg_s = strtof(argv[1], NULL);
+ float arg_l = strtof(argv[2], NULL);
+ float arg_m = strtof(argv[3], NULL);
+
+ s = ((arg_s == 0.0f) ? s : arg_s) * PI/180;
+ l = ((arg_l == 0.0f) ? l : arg_l) * SCALE;
+ m = ((arg_m == 0.0f) ? m : arg_m) * SCALE;
+
t = s;
v = 0;
a = 0;
diff --git a/src/shell.html b/src/shell.html
new file mode 100644
index 0000000..f8e31a8
--- /dev/null
+++ b/src/shell.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Pendulum</title>
+ <style>
+ .el { margin: 5px; }
+ div.el { float: left; }
+ textarea.el { font-family: monospace; width: 100%; }
+ canvas.el { border: 0px none; background-color: black; }
+ </style>
+ </head>
+ <body>
+ <div class="el" id="status">Downloading...</div>
+
+ <div class="el">
+ <canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
+ </div>
+
+ <div class="el">
+ <label>Angle (deg):</label> <br>
+ <input type="text" id="s_input"> <br>
+ <label>Lenght (m):</label> <br>
+ <input type="text" id="l_input"> <br>
+ <label>Mass (kg):</label> <br>
+ <input type="text" id="m_input"> <br>
+ <button onclick="loadParams();">Submit</button>
+ </div>
+
+ <textarea class="el" id="output" rows="12"></textarea>
+
+
+ <script type='text/javascript'>
+ let params = new URLSearchParams(location.search);
+ let s = params.get('s'); s = (s == null) ? 0 : s;
+ let l = params.get('l'); l = (l == null) ? 0 : l;
+ let m = params.get('m'); m = (m == null) ? 0 : m;
+
+ document.getElementById('s_input').value = s;
+ document.getElementById('l_input').value = l;
+ document.getElementById('m_input').value = m;
+
+ function loadParams() {
+ let ns = document.getElementById('s_input').value;
+ let nl = document.getElementById('l_input').value;
+ let nm = document.getElementById('m_input').value;
+ window.location.href = `?s=${ns}&l=${nl}&m=${nm}`;
+ }
+
+ var statusElement = document.getElementById('status');
+
+ var Module = {
+ attachGLFWEventsToCanvas: true,
+ preRun: [],
+ postRun: [],
+ arguments: [s, l, m],
+ print: (function() {
+ var element = document.getElementById('output');
+ if (element) element.value = ''; // clear browser cache
+ return function(text) {
+ if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+ // These replacements are necessary if you render to raw HTML
+ //text = text.replace(/&/g, "&amp;");
+ //text = text.replace(/</g, "&lt;");
+ //text = text.replace(/>/g, "&gt;");
+ //text = text.replace('\n', '<br>', 'g');
+ console.log(text);
+ if (element) {
+ element.value += text + "\n";
+ element.scrollTop = element.scrollHeight; // focus on bottom
+ }
+ };
+ })(),
+ canvas: (function() {
+ var canvas = document.getElementById('canvas');
+
+ // As a default initial behavior, pop up an alert when webgl context is lost. To make your
+ // application robust, you may want to override this behavior before shipping!
+ // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+ canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
+
+ return canvas;
+ })(),
+ setStatus: function(text) {
+ if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
+ if (text === Module.setStatus.last.text) return;
+ var now = Date.now();
+ Module.setStatus.last.time = now;
+ Module.setStatus.last.text = text;
+ statusElement.innerHTML = text;
+ },
+ totalDependencies: 0,
+ monitorRunDependencies: function(left) {
+ this.totalDependencies = Math.max(this.totalDependencies, left);
+ Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
+ }
+ };
+ Module.setStatus('Downloading...');
+ window.onerror = function() {
+ Module.setStatus('Exception thrown, see JavaScript console');
+ Module.setStatus = function(text) {
+ if (text) console.error('[post-exception status] ' + text);
+ };
+ };
+ </script>
+ {{{ SCRIPT }}}
+ </body>
+</html>