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

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