Published
Edited
Mar 22, 2021
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
constants = ({
TAU: Math.PI * 2,
})
Insert cell
Insert cell
uniforms = ({
R: { type: 'slider', min: 0, max: 1, value: 0.3, step: 0.001 },
r: { type: 'slider', min: 0, max: 0.5, value: 0.07, step: 0.0001 },
a: { type: 'slider', min: 0, max: 0.5, step: 0.0001 },
frame: { type: 'frame' }
// frame: { type: 'slider', min: 0, max: 1000, value: 0, step: 1 }
})
Insert cell
Insert cell
expressions = [{
color: [1, 0, 1],
locals: {
// For convenience and performance, these get turned into local variables like:
// float NAME = EXPR;
// before the actual expression is computed
F: '5.0',
t: 'frame / 180.0',
r_wave: 'r * (1.0 + a*sin(F * TAU * (u + t)))',
A: 'R + r_wave * cos(TAU * v)',
},
expr: `(A * sin(TAU * u), r_wave * sin(TAU * v), A * cos(TAU * u))`
}, {
// disabled: true,
color: [.3, 0, 1],
locals: {
F: '10.0',
t: 'frame / 300.0',
r_wave: 'r * (1.0 + a*sin(F * TAU * (u + t)))',
A: 'R + r_wave * cos(TAU * v)'
},
expr: `(r_wave * sin(TAU * v), A * cos(TAU * u), A * sin(TAU * u) - R)`
}]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
uniformInfo = {
const uniformDeclarations = [];
const uniformLocals = [];
const uniformPropGetters = {};
for (const k in uniforms) {
const uniformName = `u_slider_${k}`;
uniformPropGetters[uniformName] = (_, props) => {
return props[k];
}
uniformLocals.push(`float ${k} = ${uniformName};`);
uniformDeclarations.push(`uniform float ${uniformName};`);
}
return {
uniformDeclarations,
uniformPropGetters,
uniformLocals
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function vertexShader(compiledExpression) {
return `
precision mediump float;

attribute vec2 a_position;

uniform mat4 projection, view;
uniform mat4 u_viewInverseTranspose;

// A "varying" is a value that we want to export to the fragment shader.
varying vec3 v_color;
varying vec3 v_normal;

${compiledExpression.glsl}

void main() {
vec3 pos = evaluate(a_position);
// calculate normal
vec3 normal = evaluateNormal(a_position, pos);

// Multiply our transformation matrices to set the vertex's position in screen space.
gl_Position = projection * view * vec4(pos, 1.0);

// Export the v_color varying to just be the color that we assigned for this vertex.
v_color = vec3(${compiledExpression.color.map(n => n.toFixed(3))});
v_normal = (mat3(u_viewInverseTranspose) * normal).xyz;
}
`}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
regl = createRegl({canvas: animationCanvas})
Insert cell
mutable loop = {
console.log('Rebuilding')
const specs = [];
for (const expression of compiled) {
specs.push(surfaceSpec(expression));
if (controls.showNormals) {
specs.push(normalsSpec(expression))
}
}
const camera = {
center: [0, 0, 0],
up: [0, 1, 0],
distance: 2,
theta: + Math.PI / 2 + Math.PI / 8,
phi: Math.PI / 8,
noScroll: true,
rotationSpeed: 1,
zoomSpeed: 3,
element: animationCanvas,
log: true
};
return new Draw(specs, {
camera,
regl,
canvas: animationCanvas
});
}
Insert cell
drawing = {
let frame = 1;
const MIN_FRAME_TIME = 1000 / 60;
const frameTimes = [];

const getProps = () => ({
...(mutable sliders.values),
...getFrameUniforms(frame),
controls: (mutable dynamicControls).values
});

try {
let last = Date.now();
while (true) {
const props = getProps();
loop.draw(props);
const now = Date.now();
const time = now - last;
last = now;
frameTimes.push(time);
if (frameTimes.length > 100) frameTimes.shift();
if (mutable playing) {
frame += 1;
}
yield { status: mutable playing ? 'running' : 'stopped', frame, fps: meanFPS(frameTimes), props};
}
} catch (e) {
yield { status: 'error', error: e, props: getProps() };
// throw e;
}
}
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