Published
Edited
Jan 13, 2020
2 stars
Insert cell
Insert cell
output = {
const size = 300;
const ctx = DOM.context2d(size, size);
// ctx.canvas.style.transform = `scale(${256 / size})`;
ctx.canvas.style.transformOrigin = 'top left';
const div = html`<div style="height: ${size}px">${ctx.canvas}`;
let x = 0;
let dx = 1;
const q = new Queue(workers.length);
while (true) {
x += dx;
if (x >= 100 || x < 0) {
dx = -dx;
}

await q.add(
sendMessage({
x: x / 10,
size,
density: window.devicePixelRatio
}),
img => ctx.putImageData(img, 0, 0)
);
console.log('∂', q.avgDuration);
yield Promises.tick(Math.ceil(q.avgDuration / workers.length), div);
}
}
Insert cell
class Queue {
constructor(max = 10) {
this.tasks = [];
this.max = max;
this.elapsed = 0;
this.taskCount = 0;
}
get avgDuration() {
if (this.taskCount) {
return this.elapsed / this.taskCount;
} else {
return 100;
}
}
async add(p, cb) {
if (this.tasks.length >= this.max) {
await this.get();
}
const task = {
start: performance.now(),
p: p.then(res => {
task.done = true;
task.value = res;
task.duration = performance.now() - task.start;
}),
cb
};
this.tasks.push(task);
}
async get() {
if (!this.tasks[0].done) {
await this.tasks[0].p;
}
const task = this.tasks.shift();
// debugger;
this.elapsed += task.duration;
this.taskCount++;
task.cb(task.value);
}
}
Insert cell
scene = value => ({
things: [
$.Plane($.Vector(0.0, 1.0, 0.0), 0.0, 'checkerboard'),
$.Sphere($.Vector(0.0, 1.0, -0.25), 1.0, 'matteCheckerboard'),
$.Sphere($.Vector(-1.0, value / 5 + 0.5, 1.5), 0.5, 'shiny')
],
lights: [
{ pos: $.Vector(-2.0, 2.5, 0.0), color: $.Color(0.49, 0.07, 0.07) },
{ pos: $.Vector(1.5, 2.5, 1.5), color: $.Color(0.07, 0.07, 0.49) },
{
pos: $.Vector(1.5, 2.5, -1.5),
color: $.Color(0.07, 0.49, 0.071)
},
{ pos: $.Vector(0.0, 3.5, 0.0), color: $.Color(0.21, 0.21, 0.35) }
],
camera: $.Camera($.Vector(3.0, 2.0, 4.0), $.Vector(-1.0, 0.5, 0.0))
})
Insert cell
$ = new Proxy(
{},
{
get(target, key) {
if (typeof key === 'string' && key[0].match(/[A-Z]/)) {
return (...args) => ({ type: key, args });
}
return target[key];
}
}
)
Insert cell
function sendMessage(data) {
let resolve, reject;

if (!sendMessage.id) sendMessage.id = 0;
const messageId = sendMessage.id++;

const worker = workers[messageId % workers.length];
const handler = ({ data: { id, data, err } }) => {
if (id === messageId) {
if (err) {
const error = new Error(err.message);
error.stack = err.stack;
reject(error);
} else {
resolve(data);
}
worker.removeEventListener('message', handler);
}
};
worker.addEventListener('message', handler);
worker.postMessage({ id: messageId, data });

return new Promise((res, rej) => {
resolve = res;
reject = rej;
});
}
Insert cell
workers = [
worker1,
worker2,
worker3,
worker4,
worker5,
worker6,
worker7,
worker8
]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
workerSource = `
"use strict";
${rayTracer.source}
${messageHandler.source}
`
Insert cell
Insert cell
Insert cell
getSource = cb => ({ source: dedent(cb.toString().slice(8, -1)) })
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more