Files
scriptc/benchmarks/runtime/workloads/value-objects.ts
T

133 lines
4.1 KiB
TypeScript

// Small immutable value objects: Vec3 records built by helper functions,
// a Complex class whose methods return new instances, and an immutable
// particle update. Most temporaries never leave the expression that
// creates them.
interface Vec3 {
x: number;
y: number;
z: number;
}
function vec(x: number, y: number, z: number): Vec3 {
return { x, y, z };
}
function add(a: Vec3, b: Vec3): Vec3 {
return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
}
function sub(a: Vec3, b: Vec3): Vec3 {
return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
}
function scale(a: Vec3, k: number): Vec3 {
return { x: a.x * k, y: a.y * k, z: a.z * k };
}
function dot(a: Vec3, b: Vec3): number {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
function cross(a: Vec3, b: Vec3): Vec3 {
return { x: a.y * b.z - a.z * b.y, y: a.z * b.x - a.x * b.z, z: a.x * b.y - a.y * b.x };
}
function normalize(a: Vec3): Vec3 {
const len = Math.sqrt(dot(a, a));
return len === 0 ? a : scale(a, 1 / len);
}
interface Sphere {
center: Vec3;
radius: number;
shade: number;
}
function hit(origin: Vec3, dir: Vec3, sphere: Sphere): number {
const oc = sub(origin, sphere.center);
const b = dot(oc, dir);
const c = dot(oc, oc) - sphere.radius * sphere.radius;
const disc = b * b - c;
if (disc < 0) return -1;
const t = -b - Math.sqrt(disc);
return t > 0 ? t : -1;
}
function trace(width: number, height: number, spheres: Sphere[]): number {
const eye = vec(0, 0, -4);
const light = normalize(vec(-1, 1, -1));
let total = 0;
for (let py = 0; py < height; py++) {
for (let px = 0; px < width; px++) {
const dir = normalize(vec((px - width / 2) / width, (py - height / 2) / height, 1));
let nearest = Infinity;
let shade = 0;
for (const sphere of spheres) {
const t = hit(eye, dir, sphere);
if (t > 0 && t < nearest) {
nearest = t;
const point = add(eye, scale(dir, t));
const normal = normalize(sub(point, sphere.center));
shade = sphere.shade * Math.max(0, dot(normal, light));
}
}
total += shade;
}
}
return total;
}
class Complex {
re: number;
im: number;
constructor(re: number, im: number) {
this.re = re;
this.im = im;
}
plus(o: Complex): Complex {
return new Complex(this.re + o.re, this.im + o.im);
}
times(o: Complex): Complex {
return new Complex(this.re * o.re - this.im * o.im, this.re * o.im + this.im * o.re);
}
norm(): number {
return this.re * this.re + this.im * this.im;
}
}
function mandelbrot(size: number, limit: number): number {
let inside = 0;
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
const c = new Complex((x / size) * 3 - 2, (y / size) * 3 - 1.5);
let z = new Complex(0, 0);
let n = 0;
while (n < limit && z.norm() <= 4) {
z = z.times(z).plus(c);
n++;
}
if (n === limit) inside++;
}
}
return inside;
}
function particles(count: number, steps: number): number {
const positions: Vec3[] = [];
const velocities: Vec3[] = [];
for (let i = 0; i < count; i++) {
positions.push(vec(i % 7, (i * 3) % 11, (i * 5) % 13));
velocities.push(vec(((i % 5) - 2) / 10, ((i % 3) - 1) / 10, 0.05));
}
const gravity = vec(0, -0.01, 0);
for (let s = 0; s < steps; s++) {
for (let i = 0; i < count; i++) {
const v = add(velocities[i]!, gravity);
velocities[i] = v;
positions[i] = add(positions[i]!, scale(v, 0.5));
}
}
let sum = 0;
for (const p of positions) sum += dot(p, cross(p, vec(1, 2, 3))) + p.y;
return sum;
}
const scaleArg = Number(process.argv[2] ?? "1");
const spheres: Sphere[] = [];
for (let i = 0; i < 8; i++)
spheres.push({ center: vec((i % 4) - 1.5, Math.floor(i / 4) - 0.5, 2 + (i % 3)), radius: 0.45, shade: 1 + i / 8 });
const image = trace(Math.floor(320 * scaleArg), Math.floor(240 * scaleArg), spheres);
const fractal = mandelbrot(Math.floor(220 * scaleArg), 200);
const motion = particles(Math.floor(2000 * scaleArg), 200);
console.log("trace", image.toFixed(6), "mandelbrot", fractal, "particles", motion.toFixed(6));