Published
Edited
Feb 2, 2018
1 fork
18 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
createMesh = () => {
const subdivisions = 64;
const geometry = new THREE.PlaneGeometry(2, 2, subdivisions, 1);
const shader = new THREE.ShaderMaterial({
vertexShader: vertex(),
fragmentShader: fragment(),
uniforms: uniforms(subdivisions),
side: THREE.BackSide,
extensions: { derivatives: true }
});
const mesh = new THREE.Mesh(geometry, shader);
return mesh;
}
Insert cell
Insert cell
uniforms = (subdivisions) => ({
subdivisions: { value: subdivisions },
start: { value: new THREE.Vector2(-0.75, -0.75) },
end: { value: new THREE.Vector2(0.75, 0.75) },
control: { value: new THREE.Vector2() },
thickness: { value: 0.025 }
})
Insert cell
Insert cell
vertex = () => `
uniform float subdivisions;
uniform float thickness;
uniform vec2 start;
uniform vec2 end;
uniform vec2 control;
varying vec2 vCoord;

#define PI 3.14

vec3 sample (float t) {
// We can also adjust the per-vertex curve thickness by modifying this 0..1 number
float volume = 1.0;

// Try replacing the above with:
// float volume = 1.0 * sin(t * PI);

// Solve the quadratic curve with the start, control and end points:
float dt = (1.0 - t);
float dtSq = dt * dt;
float tSq = t * t;
float x = dtSq * start.x + 2.0 * dt * t * control.x + tSq * end.x;
float y = dtSq * start.y + 2.0 * dt * t * control.y + tSq * end.y;
return vec3(x, y, volume);

// Alternatively, you can replace the above with a linear mix() operation
// This will produce a straight line between the start and end points
// return vec3(mix(start, end, t), volume);
}

void main () {
// Get the "arc length" in 0..1 space
float arclen = (position.x * 0.5 + 0.5);

// How far to offset the line thickness for this vertex in -1..1 space
float extrusion = position.y;

// Find next sample along curve
float nextArclen = arclen + (1.0 / subdivisions);

// Sample the curve in two places
// XY is the 2D position, and the Z component is the thickness at that vertex
vec3 current = sample(arclen);
vec3 next = sample(nextArclen);

// Now find the 2D perpendicular to form our line segment
vec2 direction = normalize(next.xy - current.xy);
vec2 perpendicular = vec2(-direction.y, direction.x);

// Extrude
float computedExtrusion = extrusion * (thickness / 2.0) * current.z;
vec3 offset = current.xyz + vec3(perpendicular.xy, 0.0) * computedExtrusion;

// Compute final position
gl_Position = projectionMatrix * modelViewMatrix * vec4(offset.xyz, 1.0);

// Pass along the coordinates for texturing/effects
vCoord = position.xy;
}
`
Insert cell
Insert cell
fragment = () => `
varying vec2 vCoord;

float aastep (float threshold, float value) {
float afwidth = fwidth(value) * 0.5;
return smoothstep(threshold - afwidth, threshold + afwidth, value);
}

void main () {
// How many dashes to show
float repeat = 10.0;

// How big is the gap between each dash
float gapSize = 0.25;

// Create a dashed line
float dash = abs(fract(vCoord.x * repeat) - 0.5);

// Smooth the dashed line to sharp/crisp anti-aliasing
dash = 1.0 - aastep(gapSize, dash);

gl_FragColor = vec4(vec3(dash), 1.0);
}
`
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