Skip to content

Instantly share code, notes, and snippets.

@KeanW
Created September 11, 2018 10:08
Show Gist options
  • Save KeanW/1e001e51a4c7207c7f42fa4862905b7b to your computer and use it in GitHub Desktop.
Save KeanW/1e001e51a4c7207c7f42fa4862905b7b to your computer and use it in GitHub Desktop.
Streamline implementation
import * as MeshLine from './THREE.MeshLine.js';
export class Streamline extends THREE.Mesh {
public vertices: THREE.Vector3[];
private _meshLine: any;
private _lineGeometry: THREE.Geometry;
private _vis: number;
constructor(
verts: THREE.Vector3[],
color: THREE.Color,
width: number,
opacity: number,
growTime: number,
displayTime: number,
resolution: THREE.Vector2,
remove?: (s: Streamline) => void
) {
super();
this._meshLine = null;
this._vis = 0;
this.createMeshLine(verts, color, width, opacity, resolution);
// Fade the line's visibility to make it grow across the space
// and then shrink in the same direction
this.fadeIn(
growTime,
(s) => {
setTimeout(() => {
this.fadeOut(growTime, remove);
}, displayTime);
}
);
}
private createMeshLine(
verts: THREE.Vector3[],
color: THREE.Color,
width: number,
opacity: number,
resolution: THREE.Vector2
) {
if (!this._lineGeometry) {
this._lineGeometry = new THREE.Geometry();
}
this._meshLine = this.createMeshLineGeometry(this._meshLine, verts, this._lineGeometry, true);
this.material = this.createMeshLineMaterial(this._meshLine, width, opacity, color, resolution);
this.geometry = this._meshLine.geometry;
}
private createMeshLineGeometry(
ml: any,
verts: THREE.Vector3[],
geom: THREE.Geometry,
fit: boolean
): any {
if (fit) {
this.createCurve(verts, geom);
} else {
for (let vert of verts) {
geom.vertices.push(vert);
}
geom.verticesNeedUpdate = true;
}
if (!ml) {
ml = new MeshLine.MeshLine();
}
// Set the width at each vertex based on its position - which gives a taper -
// and then subtract the current visibility setting. This means the taper will follow
// the start of the line, rather than it being truncated
// (this.vis only gets set for streamlines as they're shrinking)
ml.setGeometry(geom, (p: number) => p - this._vis);
return ml;
}
private createCurve(verts: THREE.Vector3[], geom: THREE.Geometry): void {
// Create a spline fitting the vertices
// and get 100 points along its length
let curve = new THREE.SplineCurve3(verts);
let points = curve.getPoints(100);
for (let pt of points) {
geom.vertices.push(pt);
}
geom.verticesNeedUpdate = true;
}
private createMeshLineMaterial(
ml: any,
width: number,
opacity: number,
color: THREE.Color,
resolution: THREE.Vector2
): any {
let mat: any = new MeshLine.MeshLineMaterial({
color: color,
lineWidth: width,
opacity: opacity,
transparent: true,
visibility: 0, // default is invisible, we will grow outwards
resolution: resolution,
attributes: {
previous: ml.previous,
next: ml.next,
side: ml.side,
width: ml.width,
counters: ml.counters
}
});
// Disable depthTest for decent performance
mat.depthTest = false;
return mat;
}
private fadeIn(growTime: number, callback?: (s) => void): void {
// Not needed, as it's the default in the material definition
// (<any>this.material).uniforms.visibility.value = 0.0;
this.fadeVisibility(1, 0.1, growTime, undefined, callback);
}
private fadeOut(growTime: number, callback? : (s) => void): void {
(<any>this.material).uniforms.visibility.value = -1.0;
this.fadeVisibility(0, 0.1, growTime, (vis: any) => {
// Set the visibility level as we shrink, so that the per-vertex
// linewidth calculation function can maintain the taper at the
// start point. The visibility is negative and > -1, so add 1
// to put it in the right range for when we pick it up later
this._vis = vis.value + 1;
// Call meshLine.process() to force a per-vertex linewidth recalc
this._meshLine.process();
}, callback);
}
private fadeVisibility(
target: number,
increment: number,
growTime: number,
process?: (vis: any) => void,
callback?: (s: Streamline) => void
): void {
setTimeout(() => {
let vis = (<any>this.material).uniforms.visibility;
if (vis.value < target) {
vis.value += increment;
// Optional processing call that can be used (for instance)
// to store the visibility value and recalculate per-vertex linewidths
if (process) {
process(vis);
}
// Recurse
this.fadeVisibility(target, increment, growTime, process, callback);
} else {
if (callback) {
callback(this);
}
}
}, growTime);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment