Published
Edited
Mar 19, 2021
1 star
Insert cell
Insert cell
Insert cell
function buildGeometry() {
// Build a simple octahedron geometry
const basePoints = [
[1, 0, 0],
[0, 0, 1],
[-1, 0, 0],
[0, 0, -1]
];
const positions = [];
const faces = [];
const pushTriangle = (A, B, C) => {
const nextIndex = positions.length;
// add the vertex coordinates
positions.push(A, B, C);
// add a triple of indices of the three vertices we just added to represent a triangle
faces.push([nextIndex, nextIndex + 1, nextIndex + 2]);
}
for (let i = 0; i < basePoints.length; i++) {
const A = basePoints[i];
const B = basePoints[(i + 1) % basePoints.length];
pushTriangle(A, B, [0, 1, 0]);
pushTriangle(A, B, [0, -1, 0]);
}
return {
positions,
faces
}
}
Insert cell
// (note, "name = {...}" is syntactic sugar in this notebook format, but it's basically just Javscript)
geometry = buildGeometry()
Insert cell
// assign a color for each vertex
colors = {
const C1 = [1,0,0];
const C2 = [0,0,1];
const faceColors = [C1, C2, C2, C1, C1, C2, C2, C1];
const result = [];
for (let i = 0; i < faceColors.length; i++) {
for (const vertexIndex of geometry.faces[i]) {
result[vertexIndex] = faceColors[i];
}
}
if (result.length !== geometry.positions.length) {
throw new Error(`colors.length=${result.length} != vertex count=${geometry.positions.length}`);
}
return result;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// Putting this together into the data and shaders that WebGL (via the regl wrapper lib)
// will need to do its stuff.
function tetrahedronSpec(regl) {
return {
attributes: {
a_position: geometry.positions,
a_color: colors
},
uniforms: {
// regl.prop says that we'll take these as parameters at draw time
u_world: regl.prop('world'),
u_projection: regl.prop('projection')
},
elements: geometry.faces,
vert: vertexShader,
frag: fragmentShader
}
}
Insert cell
{
const draw = new Draw(tetrahedronSpec);
draw.draw({
world,
projection
});
return draw.canvas;
}
Insert cell
Insert cell
function calculateWorldMatrix(frame) {
const rotationSpeed = 5; // degrees per frame
const rad = rotationSpeed * frame * Math.PI / 180;
const m = transform({
translate: [0,0,-1.75],
scale: [0.7, 0.7, 0.7],
rotate: {
rad: 10 * Math.PI / 180,
axis: [1, 0, 0]
}
});
transform({
rotate: {
rad,
axis: [0, 1, 0]
}
}, m, m)
return m;
}
Insert cell
Insert cell
{
const loop = new Draw(tetrahedronSpec, animationCanvas);
let frame = 0;
loop.draw({ world: calculateWorldMatrix(frame), projection });
while (playing) {
loop.draw({ world: calculateWorldMatrix(frame), projection });
await Promises.delay(1000 / 30);
yield ++frame;
}
yield frame;
}
Insert cell
Insert cell
Insert cell
Insert cell
{
let i = 0;
const frameRate = 45;
while (playing) {
mutable frame = i;
await Promises.delay(1000 / frameRate);
yield ++i;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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