Copy files

This commit is contained in:
schmelczerandras 2020-09-16 16:03:01 +02:00
commit 36f01c6716
22 changed files with 500 additions and 0 deletions

4
src/helper/clamp.ts Normal file
View file

@ -0,0 +1,4 @@
export const clamp = (value: number, min: number, max: number): number =>
Math.min(max, Math.max(min, value));
export const clamp01 = (value: number): number => Math.min(1, Math.max(0, value));

View file

@ -0,0 +1,23 @@
export class DeltaTimeCalculator {
private previousTime: DOMHighResTimeStamp | null = null;
constructor() {
document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
}
public getNextDeltaTime(currentTime: DOMHighResTimeStamp): DOMHighResTimeStamp {
if (this.previousTime === null) {
this.previousTime = currentTime;
}
const delta = currentTime - this.previousTime;
this.previousTime = currentTime;
return delta;
}
private handleVisibilityChange() {
if (!document.hidden) {
this.previousTime = null;
}
}
}

3
src/helper/last.ts Normal file
View file

@ -0,0 +1,3 @@
export function last<T>(a: Array<T>): T | null {
return a.length > 0 ? a[a.length - 1] : null;
}

1
src/helper/mix.ts Normal file
View file

@ -0,0 +1 @@
export const mix = (from: number, to: number, q: number) => from + (to - from) * q;

18
src/helper/random.ts Normal file
View file

@ -0,0 +1,18 @@
// source:
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript
// Mulberry32
export abstract class Random {
private static _seed = 42;
public static set seed(value: number) {
Random._seed = value;
}
public static getRandom(): number {
let t = (Random._seed += 0x6d2b79f5);
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
}
}

1
src/helper/to-percent.ts Normal file
View file

@ -0,0 +1 @@
export const toPercent = (value: number) => `${Math.round(value * 100)}%`;

3
src/helper/wait.ts Normal file
View file

@ -0,0 +1,3 @@
export const wait = (ms: number): Promise<void> => {
return new Promise<void>((resolve, _) => setTimeout(resolve, ms));
};

14
src/index.html Normal file
View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Test</title>
</head>
<body>
<noscript>
<h1>Javascript is required for this website.</h1>
</noscript>
<div id="overlay"></div>
<canvas id="main"></canvas>
</body>
</html>

35
src/index.scss Normal file
View file

@ -0,0 +1,35 @@
html,
body,
canvas#main {
height: 100%;
width: 100%;
}
body {
position: relative;
margin: 0;
#overlay {
font-family: Helvetica, Arial, sans-serif;
font-size: 0.6em;
margin: 0.75em 1em;
position: absolute;
right: 0;
$outline-width: 0.5px;
text-shadow: -$outline-width -$outline-width 0 #000,
$outline-width -$outline-width 0 #000, -$outline-width $outline-width 0 #000,
$outline-width $outline-width 0 #000;
color: white;
white-space: pre;
@media (max-width: 800px) {
font-size: 0.55em;
}
}
canvas#main {
background-color: hotpink;
}
}

76
src/index.ts Normal file
View file

@ -0,0 +1,76 @@
import { glMatrix, vec2, vec3 } from 'gl-matrix';
import { compile, Renderer } from 'sdf-2d';
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
import './index.scss';
import { RainScene } from './scenes/rain-scene';
import { Scene } from './scenes/scene';
glMatrix.setMatrixArrayType(Array);
const deltaTimeCalculator = new DeltaTimeCalculator();
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const overlay = document.querySelector('#overlay') as HTMLDivElement;
const drawFrame = (
renderer: Renderer,
scene: Scene,
deltaTime: DOMHighResTimeStamp,
currentTime: DOMHighResTimeStamp
) => {
const { width, height } = canvas.getBoundingClientRect();
renderer.setViewArea(vec2.fromValues(0, height), vec2.fromValues(width, height));
renderer.autoscaleQuality(deltaTime);
overlay.innerText = JSON.stringify(
renderer.insights,
(_, v) => (v.toFixed ? Number(v.toFixed(2)) : v),
' '
);
scene.animate(currentTime, { width, height });
scene.drawables.forEach((d) => renderer.addDrawable(d));
renderer.renderDrawables();
};
const handleScene = async (scene: Scene) => {
const renderer = compile(
canvas,
scene.descriptors,
[vec3.fromValues(0.4, 0.35, 0.6), vec3.fromValues(0, 1, 0), vec3.fromValues(1, 1, 0)],
{ tileMultiplier: 8, enableStopwatch: false }
);
let triggerIsOver: () => void;
const isOver = new Promise((resolve) => (triggerIsOver = resolve));
let timeSinceStart = 0;
const handleFrame = (currentTime: DOMHighResTimeStamp) => {
const deltaTime = deltaTimeCalculator.getNextDeltaTime(currentTime);
drawFrame(renderer, scene, deltaTime, currentTime);
if ((timeSinceStart += deltaTime) > 200000 * 1000) {
triggerIsOver();
} else {
requestAnimationFrame(handleFrame);
}
};
requestAnimationFrame(handleFrame);
await isOver;
renderer.destroy();
};
const scenes = [new RainScene()];
const main = async () => {
let i = 0;
for (;;) {
await handleScene(scenes[i++ % scenes.length]);
}
};
main();

62
src/scenes/rain-scene.ts Normal file
View file

@ -0,0 +1,62 @@
import { vec2 } from 'gl-matrix';
import { CircleLight, Drawable, DrawableDescriptor, Flashlight, Tunnel } from 'sdf-2d';
import { Random } from '../helper/random';
import { Scene } from './scene';
export class RainScene implements Scene {
public droplets: Array<Droplet> = [];
public constructor() {
for (let i = 0; i < 100; i++) {
this.droplets.push(new Droplet());
}
}
public get descriptors(): Array<DrawableDescriptor> {
return [Tunnel.descriptor, CircleLight.descriptor, Flashlight.descriptor];
}
public animate(
currentTime: number,
viewAreaSize: { width: number; height: number }
): void {
this.droplets.forEach((d) => d.animate(currentTime, viewAreaSize));
}
public get drawables(): Array<Drawable> {
return this.droplets.map((d) => d.drawable);
}
}
class Droplet {
public readonly drawable: Tunnel;
private speed = Random.getRandom() * 0.2 + 0.1;
private position = vec2.fromValues(Random.getRandom(), Random.getRandom());
private length = Random.getRandom() * 30 + 8;
constructor() {
const size = Random.getRandom() * 10 + 5;
this.drawable = new Tunnel(
vec2.create(),
vec2.create(),
size + Random.getRandom() * 5 + 5,
size
);
}
public animate(
currentTime: number,
{ width, height }: { width: number; height: number }
) {
const heightOffset = 300;
vec2.set(
this.drawable.from,
this.position[0] * width,
height - ((this.speed * currentTime) % (height + heightOffset))
);
vec2.add(this.drawable.to, this.drawable.from, vec2.fromValues(0, this.length));
}
}

10
src/scenes/scene.ts Normal file
View file

@ -0,0 +1,10 @@
import { Drawable, DrawableDescriptor } from 'sdf-2d';
export interface Scene {
readonly descriptors: Array<DrawableDescriptor>;
animate(
currentTime: DOMHighResTimeStamp,
viewAreaSize: { width: number; height: number }
): void;
readonly drawables: Array<Drawable>;
}