Published
Edited
Mar 28, 2022
1 fork
15 stars
Insert cell
Insert cell
// render.getCanvas() is not responsive
{
var canvas = render.getCanvas();
setTimeout(function() { canvas.width = w, canvas.height = h; }, 100);
return canvas;
}
Insert cell
Insert cell
Insert cell
Insert cell
GPU = require("gpu.js@2.15.2")
Insert cell
function applyRotation(rotatex, rotatey, rotatez, lambda, phi, which) {
var degrees = 57.29577951308232;
lambda = lambda / degrees;
phi = phi / degrees;

var cosphi = Math.cos(phi),
x = Math.cos(lambda) * cosphi,
y = Math.sin(lambda) * cosphi,
z = Math.sin(phi);

// inverse rotation
var deltaLambda = rotatex / degrees; // rotate[0]
var deltaPhi = -rotatey / degrees; // rotate[1]
var deltaGamma = -rotatez / degrees; // rotate[2]

var cosDeltaPhi = Math.cos(deltaPhi),
sinDeltaPhi = Math.sin(deltaPhi),
cosDeltaGamma = Math.cos(deltaGamma),
sinDeltaGamma = Math.sin(deltaGamma);

var k = z * cosDeltaGamma - y * sinDeltaGamma;

lambda = Math.atan2(
y * cosDeltaGamma + z * sinDeltaGamma,
x * cosDeltaPhi + k * sinDeltaPhi
) - deltaLambda;
k = k * cosDeltaPhi - x * sinDeltaPhi;

phi = Math.asin(k);

lambda *= degrees;
phi *= degrees;
// return [lambda,phi]; // fails so we need to call this function twice
// and ask first for lambda, then for phi
if (which == 0) return lambda;
else return phi;
}
Insert cell
function frac(n) {
return n - Math.floor(n);
}
Insert cell
// the kernel runs for each pixel, with:
// - this.thread.x = horizontal position in pixels from the left edge
// - this.thread.y = vertical position in pixels from the bottom edge (*opposite of canvas*)
kernel = function(pixels, rotate0, rotate1, rotate2, scale) {

// azimuthal equal area
function radius(rho) {
return 2.0 * Math.asin(rho / 2.0);
}
// orthographic
function __radius(rho) {
return Math.asin(rho);
}

// equirectangular projection (reads the (lon,lat) color from the base image)
function pixelx(lon, srcw) {
lon = frac((lon + 180) / 360);
return Math.floor(lon * srcw);
}
function pixely(lat, srch) {
lat = frac((lat + 90) / 180);
return Math.floor(lat * srch);
}
const x = (this.thread.x / this.constants.w - 1 / 2) / scale,
y = ((this.thread.y - this.constants.h / 2) / this.constants.w) / scale;

// inverse projection
const rho = Math.sqrt(x * x + y * y) + 1e-12;
const c = radius(rho),
sinc = Math.sin(c),
cosc = Math.cos(c);
// x, y : pixel coordinates if rotation was null
const lambda = Math.atan2(x * sinc, rho * cosc) * 57.29577951308232;
const z = y * sinc / rho;
if (Math.abs(z) < 1) {
const phi = Math.asin(z) * 57.29577951308232;

// apply rotation
// [lambda, phi] = applyRotation(centerx, centery, centerz, lambda, phi); // TODO
const lambdan = applyRotation(rotate0, rotate1, rotate2, lambda, phi, 0);
const phin = applyRotation(rotate0, rotate1, rotate2, lambda, phi, 1);
//var n = n0(lambda, phi, this.constants.srcw, this.constants.srch);
//this.color(pixels[n]/256, pixels[n+1]/256,pixels[n+2]/256,1);
const pixel = pixels[pixely(phin, this.constants.srch)][pixelx(lambdan, this.constants.srcw)];
this.color(pixel[0], pixel[1], pixel[2], 1);
}
}
Insert cell
Insert cell
Insert cell
gpu = new GPU.GPU() // try {mode: "cpu"} to go slow
Insert cell
render = gpu
.createKernel(kernel, { functions: [applyRotation, frac] })
.setConstants({ w, h, srcw: image.width, srch: image.height })
.setOutput([w, h])
.setGraphical(true)
Insert cell
compute = {
var fpsTime = performance.now(), fps = 60;
do {
let r0 = (-Date.now() / 30) % 360,
r1 = 35 * Math.sin((-Date.now() / 1030) % 360),
r2 = 0,
scale = 0.49;
render(image, r0, r1, r2, scale);
fps = (1 + fps) * (1 + 0.000984 * (fpsTime - (fpsTime = performance.now())));
yield fps;
} while (true)
}
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