Public
Edited
Jul 23, 2023
Insert cell
Insert cell
class Engine {
constructor(row, col, iter) {
this._row = row;
this._col = col;
this._iter = iter;
}
row = () => this._row;
col = () => this._col;
iter = () => this._iter;
nx = () => {
return 0.04 + this.col() * 0.56 + this.row() * 0.15;
};
ny = () => {
return 0.85 + this.row() * -0.52;
}
nz = () => {
return 0.74 + this.col() * -0.27 + this.row() * 0.30;
}
n = () => {
return Math.sqrt(this.nx() ** 2 + this.ny() ** 2 + this.nz() ** 2) * 1.6;
}
x = () => {
return -0.61 + this.prevV() * this.nx() / this.n();
}
y = () => {
return -0.90 + this.prevV() * this.ny() / this.n();
}
z = () => {
return -1.24 + this.prevV() * this.nz() / this.n();
}
prevV = () => {
if (this.iter() <= 0) return 0;
if (typeof this._prevV === 'undefined') {
this._prevV = new Engine(this.row(), this.col(), this.iter() - 1).v();
}
return this._prevV;
}
sdf = () => {
return Math.max(Math.abs(this.x()), Math.abs(this.y()), Math.abs(this.z())) - 0.3;
}
v = () => {
return this.prevV() + this.sdf();
}
brightness = () => {
return (this.v() - 1.75) / 1.70;
}
}
Insert cell
class Engine0 {
constructor(row, col) {
this._row = row;
this._col = col;
}
row = () => this._row;
col = () => this._col;
nx = () => {
return 0.04 + this.col() * 0.56 + this.row() * 0.15;
};
ny = () => {
return 0.85 + this.row() * -0.52;
}
nz = () => {
return 0.74 + this.col() * -0.27 + this.row() * 0.30;
}
n = () => {
return Math.sqrt(this.nx() ** 2 + this.ny() ** 2 + this.nz() ** 2) * 1.6;
}
v = () => {
let res = 0;
for (let i=0; i<100; i++) {
const x = -0.61 + res * this.nx() / this.n();
const y = -0.90 + res * this.ny() / this.n();
const z = -1.24 + res * this.nz() / this.n();
const sdf = Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) - 0.3;
res += sdf;
}
return res;
}
brightness = () => {
return (this.v() - 1.75) / 1.70;
}
}
Insert cell
m = (t, name, fn) => {
const propName = '_' + name;
return () => {
if (typeof t[propName] === 'undefined') {
t[propName] = fn();
}
return t[propName];
}
}
Insert cell
class Engine2 {
constructor(row, col, iter) {
this._row = row;
this._col = col;
this._iter = iter;
}
row = () => this._row;
col = () => this._col;
iter = () => this._iter;
alpha = () => alpha;
beta = () => beta;
fov = () => fov;
dist = () => dist;
rows = () => rows;
cols = () => cols;
cosAlpha = () => Math.cos(this.alpha() * Math.PI / 180);
sinAlpha = () => Math.sin(this.alpha() * Math.PI / 180);
cosBeta = () => Math.cos(this.beta() * Math.PI / 180);
sinBeta = () => Math.sin(this.beta() * Math.PI / 180);
camX = () => this.dist() * this.cosAlpha() * this.cosBeta();
camY = () => this.dist() * this.sinBeta();
camZ = () => this.dist() * this.sinAlpha() * this.cosBeta();
pixelSize = () => Math.tan(this.fov() * Math.PI / 180 / 2) / ((this.rows() - 1) / 2);
ux = () => -this.pixelSize() * this.sinAlpha();
uz = () => +this.pixelSize() * this.cosAlpha();
vx = () => +this.pixelSize() * this.cosAlpha() * this.sinBeta();
vy = () => -this.pixelSize() * this.cosBeta();
vz = () => +this.pixelSize() * this.sinAlpha() * this.sinBeta();
x0 = () => -this.cosAlpha() * this.cosBeta();
y0 = () => -this.sinBeta();
z0 = () => -this.sinAlpha() * this.cosBeta();
matX0 = () => this.x0() - this.cols() / 2 * this.ux() - this.rows() / 2 * this.vx() + 0.02;
matX1 = () => this.ux() * this.rows();
matX2 = () => this.vx() * this.cols();
matY0 = () => this.y0() - this.rows() / 2 * this.vy() + 0.05;
matY1 = () => 0;
matY2 = () => this.vy() * this.cols();
matZ0 = () => this.z0() - this.cols() / 2 * this.uz() - this.rows() / 2 * this.vz();
matZ1 = () => this.uz() * this.rows();
matZ2 = () => this.vz() * this.cols();
nx = () => {
return this.matX0() + this.col() * this.matX1() + this.row() * this.matX2();
};
ny = () => {
return this.matY0() + this.row() * this.matY2();
}
nz = () => {
return this.matZ0() + this.col() * this.matZ1() + this.row() * this.matZ2();
}
n = () => {
return Math.sqrt(this.nx() ** 2 + this.ny() ** 2 + this.nz() ** 2) * 1.6;
}
x = () => {
return this.camX() + this.prevV() * this.nx() / this.n();
}
y = () => {
return this.camY() + this.prevV() * this.ny() / this.n();
}
z = () => {
return this.camZ() + this.prevV() * this.nz() / this.n();
}
prevV = () => {
if (this.iter() <= 0) return 0;
if (typeof this._prevV === 'undefined') {
this._prevV = new Engine2(this.row(), this.col(), this.iter() - 1).v();
}
return this._prevV;
}
sdf = () => {
return Math.max(Math.abs(this.x()), Math.abs(this.y()), Math.abs(this.z())) - 0.3;
}
v = () => {
return this.prevV() + this.sdf();
}
brightness = () => {
return (this.v() - 1.75) / 1.70;
}
}
Insert cell
getPixel = (row, col) => new Engine02(row, col, 100).brightness();
Insert cell
class Engine02 {
constructor(row, col, iter) {
this._row = row;
this._col = col;
this._iter = iter;
}
row = () => this._row;
col = () => this._col;
iter = () => this._iter;
alpha = () => alpha;
beta = () => beta;
fov = () => fov;
dist = () => dist;
rows = () => rows;
cols = () => cols;
cosAlpha = () => Math.cos(this.alpha() * Math.PI / 180);
sinAlpha = () => Math.sin(this.alpha() * Math.PI / 180);
cosBeta = () => Math.cos(this.beta() * Math.PI / 180);
sinBeta = () => Math.sin(this.beta() * Math.PI / 180);
camX = m(this, 'camX', () => this.dist() * this.cosAlpha() * this.cosBeta());
camY = m(this, 'camY', () => this.dist() * this.sinBeta());
camZ = m(this, 'camZ', () => this.dist() * this.sinAlpha() * this.cosBeta());
pixelSize = m(this, 'pixelSize', () => Math.tan(this.fov() * Math.PI / 180 / 2) / ((this.rows() - 1) / 2));
ux = () => -this.pixelSize() * this.sinAlpha();
uz = () => +this.pixelSize() * this.cosAlpha();
vx = () => +this.pixelSize() * this.cosAlpha() * this.sinBeta();
vy = () => -this.pixelSize() * this.cosBeta();
vz = () => +this.pixelSize() * this.sinAlpha() * this.sinBeta();
x0 = () => -this.cosAlpha() * this.cosBeta();
y0 = () => -this.sinBeta();
z0 = () => -this.sinAlpha() * this.cosBeta();
matX0 = m(this, 'matX0', () => this.x0() - this.cols() / 2 * this.ux() - this.rows() / 2 * this.vx() + 0.02);
matX1 = m(this, 'matX1', () => this.ux() * this.rows());
matX2 = m(this, 'matX2', () => this.vx() * this.cols());
matY0 = m(this, 'matY0', () => this.y0() - this.rows() / 2 * this.vy() + 0.05);
matY1 = m(this, 'matY1', () => 0);
matY2 = m(this, 'matY2', () => this.vy() * this.cols());
matZ0 = m(this, 'matZ0', () => this.z0() - this.cols() / 2 * this.uz() - this.rows() / 2 * this.vz());
matZ1 = m(this, 'matZ1', () => this.uz() * this.rows());
matZ2 = m(this, 'matZ2', () => this.vz() * this.cols());
nx = m(this, 'nx', () => {
return this.matX0() + this.col() * this.matX1() + this.row() * this.matX2();
});
ny = m(this, 'ny', () => {
return this.matY0() + this.row() * this.matY2();
});
nz = m(this, 'nz', () => {
return this.matZ0() + this.col() * this.matZ1() + this.row() * this.matZ2();
});
n = m(this, 'n', () => {
return Math.sqrt(this.nx() ** 2 + this.ny() ** 2 + this.nz() ** 2) * 1.6;
});
v = () => {
let res = 0;
for (let i=0; i<100; i++) {
const x = this.camX() + res * this.nx() / this.n();
const y = this.camY() + res * this.ny() / this.n();
const z = this.camZ() + res * this.nz() / this.n();
const sdf = Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) - 0.3;
res += sdf;
}
return res;
}
brightness = () => {
return (this.v() - 1.75) / 1.70;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
grid(range(rows + 1).map(r => range(cols + 1).map(c => getPixel(1 - r / rows, c / cols))), rows, cols)
Insert cell
viewof x = Inputs.select(['a', 'b', 'c'], {value: 'c', label: "fov_in"})
Insert cell
rows = 100
Insert cell
cols = 100
Insert cell
function range(n) {
const res = [];
for (let i=0; i<n; i++) {
res.push(i);
}
return res;
}
Insert cell
function grid(data, rows, cols, maxVal=1) {
const gamma = 0.6; // gamma correction
const ctx = DOM.context2d(width / 2, width / 2);
ctx.fillStyle = 'lightgrey';
ctx.fillRect(0, 0, width, width);
ctx.scale(width / 2 / cols, width / 2 / rows);
for (let row=0; row<rows; row++) {
for (let col=0; col<cols; col++) {
const grey = Math.round(Math.max(0, Math.min(1, data[row][col] / maxVal)) ** gamma * 255);
ctx.fillStyle = `rgba(${grey}, ${grey}, ${grey})`;
ctx.fillRect(col, row, 1, 1);
}
}
return ctx.canvas;
}
Insert cell
import { number, slider, radio } from '@jashkenas/inputs';
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