Published
Edited
Nov 1, 2018
12 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function draw_shape(canvas, xf, shape, alpha) {
const ctx = canvas.getContext("2d");
const w = dimension;
const h = dimension;
ctx.save();
ctx.translate(w / 2, h / 2);
ctx.scale(w / 2, h / 2);
const points3d = xform_points(xf, shape[0]);
const points = points3d.map(p => [p[0] / p[2], p[1] / p[2]]);
const lines = shape[1];
ctx.strokeStyle = "#fff";
ctx.lineWidth = 1 / (w / 2);
ctx.beginPath();
lines.forEach(line => {
const [a, b] = line;
const p1 = points[a];
const p2 = points[b];
ctx.moveTo(p1[0], p1[1]);
ctx.lineTo(p2[0], p2[1]);
});
ctx.stroke();
ctx.restore();
}
Insert cell
translate = (x, y, z) => [[1, 0, 0, x], [0, 1, 0, y], [0, 0, 1, z]]
Insert cell
identity = () => translate(0, 0, 0)
Insert cell
rotate = theta => {
const s = Math.sin(theta);
const c = Math.cos(theta);
return [[c, -s, 0, 0], [s, c, 0, 0], [0, 0, 1, 0]];
}
Insert cell
transpose_axes = (a, b) => {
let rv = identity();
let tmp = rv[a];
rv[a] = rv[b];
rv[b] = tmp;
return rv;
}
Insert cell
concat = (x1, x2) => {
const rv = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
for (let ii = 0; ii < 3; ii++) {
rv[ii][3] = x2[ii][3];
for (let jj = 0; jj < 3; jj++) {
rv[ii][3] += x1[jj][3] * x2[ii][jj];
for (let kk = 0; kk < 3; kk++) {
rv[ii][jj] += x1[kk][jj] * x2[ii][kk];
}
}
}
return rv;
}
Insert cell
concat_n = xforms => xforms.reduce((memo, xf) => concat(memo, xf), identity())
Insert cell
function xform(xform, p) {
let result_vec = [];
for (let ii = 0; ii < 3; ii++) {
let rv = xform[ii][3];
for (var jj = 0; jj < 3; jj++) rv += xform[ii][jj] * p[jj];
result_vec.push(rv);
}
return result_vec;
}
Insert cell
xform_points = (xf, points) => points.map(point => xform(xf, point))
Insert cell
xform_shape = (xf, shape) => [
xform_points(xf, shape[0]),
shape[1].slice(),
shape[2].slice()
]
Insert cell
function augment(shape1, shape2) {
var s1p = shape1[0];
var off = s1p.length;
s1p.push.apply(s1p, shape2[0]);
var s2ll = shape2[1].length;
for (var ii = 0; ii < s2ll; ii++)
shape1[1].push([shape2[1][ii][0] + off, shape2[1][ii][1] + off]);
var s2pl = shape2[2].length;
for (var ii = 0; ii < s2pl; ii++) {
let tri = shape2[2][ii];
shape1[2].push([tri[0] + off, tri[1] + off, tri[2] + off]);
}
}
Insert cell
function extrude_shape(xf, shape, n) {
if (n == null) n = 1;
let new_part = shape;
let old_line_base = 0; // where the lines to attach the triangles start
for (let ii = 0; ii < n; ii++) {
new_part = xform_shape(xf, new_part);
let shape_length = shape[0].length;
let new_line_base = shape[1].length; // for triangles later
augment(shape, new_part);
var new_part_length = new_part[0].length;
// connect corresponding points
for (let jj = 0; jj < new_part_length; jj++) {
shape[1].push([shape_length + jj - new_part_length, shape_length + jj]);
}
// make triangles
var nlines = new_part[1].length;
// var old_line_base = new_line_base - nlines
for (let jj = 0; jj < nlines; jj++) {
const old_line = shape[1][old_line_base + jj];
const new_line = shape[1][new_line_base + jj];
shape[2].push([old_line[0], old_line[1], new_line[0]]);
shape[2].push([new_line[1], new_line[0], old_line[1]]);
}
old_line_base = new_line_base;
}
}
Insert cell
point_shape = (x, y, z) => [[[x, y, z]], [], []]
Insert cell
function circle(r, n) {
const shape = point_shape(r, 0, 0);
extrude_shape(rotate((Math.atan(1) * 8) / n), shape, n);
return shape;
}
Insert cell
function cls(canvas) {
const ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
Insert cell
function make_torus(r1, r2, n1, n2) {
var c = xform_shape(translate(r2, 0, 0), circle(r1, n1));
extrude_shape(
concat_n([
transpose_axes(1, 2),
rotate((Math.atan(1) * 8) / n2),
transpose_axes(1, 2)
]),
c,
n2
);
return c;
}
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more