Skip to content

Instantly share code, notes, and snippets.

@tbfleming
Last active July 8, 2021 19:53
Show Gist options
  • Save tbfleming/e4c2cc66a6606aa92ff099c27ee2bd02 to your computer and use it in GitHub Desktop.
Save tbfleming/e4c2cc66a6606aa92ff099c27ee2bd02 to your computer and use it in GitHub Desktop.
cib demo: canvas: bouncing lines
#include <math.h>
#include <stdio.h>
#include <string>
#include <vector>
using namespace std;
extern "C" double width();
extern "C" double height();
extern "C" void fillStyle(const char *);
extern "C" void strokeStyle(const char *);
extern "C" void clearRect(double x, double y, double width, double height);
extern "C" void fillRect(double x, double y, double width, double height);
extern "C" void beginPath();
extern "C" void stroke();
extern "C" void moveTo(double x, double y);
extern "C" void lineTo(double x, double y);
struct Point {
double x;
double y;
};
struct Line {
Point p1;
Point p2;
};
struct Color {
double r;
double g;
double b;
};
void bounce(double &val, double &delta, double max) {
val += delta;
if (val < 0) {
val = 0;
delta = abs(delta);
} else if (val > max) {
val = max;
delta = -abs(delta);
}
}
void bounce(Point &p, Point &delta, double maxX, double maxY) {
bounce(p.x, delta.x, maxX);
bounce(p.y, delta.y, maxY);
}
void bounce(Line &l, Line &delta, double maxX, double maxY) {
bounce(l.p1, delta.p1, maxX, maxY);
bounce(l.p2, delta.p2, maxX, maxY);
}
void bounce(Color &c, Color &delta) {
bounce(c.r, delta.r, 1);
bounce(c.g, delta.g, 1);
bounce(c.b, delta.b, 1);
}
void drawLine(const Line &l) {
moveTo(l.p1.x, l.p1.y);
lineTo(l.p2.x, l.p2.y);
}
std::string colorStr(const Color &c) {
char result[10];
snprintf(result, sizeof(result), "#%02x%02x%02x", int(c.r * 255),
int(c.g * 255), int(c.b * 255));
return result;
}
Line line{Point{100, 300}, Point{300, 10}};
Line lineDelta{Point{4, 3}, Point{-2, 5}};
Color color{.2, .8, 0};
Color colorDelta{.04, -.01, .02};
std::vector<Line> lines;
extern "C" void drawFrame() {
auto w = width();
auto h = height();
clearRect(0, 0, w, h);
fillStyle("blue");
fillRect(0, 0, 10, 10);
fillRect(w - 10, 0, 10, 10);
fillRect(0, h - 10, 10, 10);
fillRect(w - 10, h - 10, 10, 10);
auto c = color;
auto cd = colorDelta;
for (auto &line : lines) {
strokeStyle(colorStr(c).c_str());
beginPath();
drawLine(line);
stroke();
bounce(c, cd);
}
bounce(line, lineDelta, w, h);
lines.push_back(line);
bounce(color, colorDelta);
if (lines.size() > 300) {
lines.erase(lines.begin());
bounce(color, colorDelta);
}
}
void moveToward(Point &p, Point &delta, double x, double y) {
auto dist = sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
if (dist) {
delta.x = (x - p.x) / dist * 6;
delta.y = (y - p.y) / dist * 6;
}
}
extern "C" void mousemove(double x, double y) {
moveToward(line.p2, lineDelta.p2, x, y);
}
int main() {}
%init-scripts%
<style>
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
}
</style>
<canvas id="c" width="400" height="400"></canvas>
<script>
'use strict';
let canvas = document.getElementById('c');
let ctx = canvas.getContext('2d');
wasmImports = {
width() { return canvas.width },
height() { return canvas.height },
fillStyle(s) { ctx.fillStyle = emModule.UTF8ToString(s) },
strokeStyle(s) { ctx.strokeStyle = emModule.UTF8ToString(s) },
clearRect: ctx.clearRect.bind(ctx),
fillRect: ctx.fillRect.bind(ctx),
beginPath: ctx.beginPath.bind(ctx),
stroke: ctx.stroke.bind(ctx),
moveTo: ctx.moveTo.bind(ctx),
lineTo: ctx.lineTo.bind(ctx),
}
function update() {
let bounds = canvas.getBoundingClientRect();
canvas.width = bounds.width * devicePixelRatio;
canvas.height = bounds.height * devicePixelRatio;
if (wasmExports.drawFrame)
wasmExports.drawFrame();
requestAnimationFrame(update);
}
update();
canvas.addEventListener("mousemove", e => {
if (wasmExports.mousemove)
wasmExports.mousemove(
e.clientX * devicePixelRatio,
e.clientY * devicePixelRatio);
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment