Skip to content

Instantly share code, notes, and snippets.

@erikparr
Last active September 22, 2019 20:26
Show Gist options
  • Save erikparr/a0bef15944534d6fe11451e7cf27fa5b to your computer and use it in GitHub Desktop.
Save erikparr/a0bef15944534d6fe11451e7cf27fa5b to your computer and use it in GitHub Desktop.
function NurbsRibbon(strokeTexture) {
this.loadedCurvePts = [];
this.nurbsKnots = [];
this.nurbsDegree = 3;
this.nurbsControlPoints = [];
this.resolution = 100;
this.drawSpeed = 50;
this.spline;
this.segmentPoints;
this.numCtrlPts = 5;
this.drawIndex = 0;
this.drawLoadedCurve = false;
this.linePts = [];
this.curveLength = 200;
this.isInited = false;
this.drawSpeedScalar = 0.17;
this.lineGroup;
this.nurbsGeometry;
this.nurbsLine;
this.interpolate = false;
this.numInterplPts = 5;
this.boundaryAreaSize = 0.5;
}
var colors = [
new THREE.Color(0xff66a8),
new THREE.Color(0xdf2dff),
new THREE.Color(0x6d1eff),
new THREE.Color(0xa72dff),
new THREE.Color(0xf0b5b3),
new THREE.Color(0x00ff00),
new THREE.Color(0x000000),
new THREE.Color(0xffffff)
];
NurbsRibbon.prototype.init = function(texture) {
this.material = new MeshLineMaterial({
map: texture,
useMap: true,
lineWidth: 0.075,
near: 1,
far: 100000,
depthTest: false,
opacity: 1,
blending: THREE.NormalBlending,
transparent: true,
side: THREE.DoubleSide,
resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
color: colors[Math.floor(Math.random()*colors.length)]
});
this.nurbsGeometry = new THREE.Geometry();
this.nurbsGeometry.setFromPoints(this.linePts);
var position = new THREE.Vector3();
for (var i = 0; i < this.curveLength; i++) {
// must initialize it to the number of positions it will keep or it will throw an error
this.nurbsGeometry.vertices.push(position.clone());
}
this.nurbsLine = new MeshLine();
this.nurbsLine.setGeometry(this.nurbsGeometry, function(p) {
return p;
});
//put the ribbon line in a group
this.lineGroup = new THREE.Group();
this.lineMesh = new THREE.Mesh(this.nurbsLine.geometry, this.material); // this syntax could definitely be improved!
this.lineMesh.frustumCulled = false;
this.lineGroup.add(this.lineMesh);
// init the nurbs array with some points
for (var i = 0; i < this.numCtrlPts; i++) {
var ranPt = new THREE.Vector4(
this.boundaryAreaSize * (Math.random() * 2 - 1),
this.boundaryAreaSize * (Math.random() * 2 - 1),
this.boundaryAreaSize * (Math.random() * 2 - 1),
1
);
this.nurbsControlPoints.push(ranPt);
}
// scene.add(this.lineGroup);
this.AddNewCurve();
};
NurbsRibbon.prototype.update = function() {
// linePts stores the windowed segments points for drawing the curve
var point = this.segmentPoints[this.drawIndex];
this.nurbsLine.advance(point);
this.linePts.push(point);
this.drawIndex++;
// new curve when finished drawing current curve
if (this.drawIndex >= this.resolution) {
this.drawIndex = 0;
if (this.drawLoadedCurve) {
console.log("draw curve from data");
this.addInterpolationPts();
this.AddNewCurve(this.loadedCurvePts);
} else {
this.AddNewCurve();
}
}
};
NurbsRibbon.prototype.AddNewCurve = function(curvePts) {
console.log("add new curve");
console.log("line group size: " + this.lineGroup.children.length);
var drawPoints = [];
//last two points used to calculate transition to new curve
var curPt = this.nurbsControlPoints[this.nurbsControlPoints.length - 1];
var lastPt = this.nurbsControlPoints[this.nurbsControlPoints.length - 2];
var lastMidPt = new THREE.Vector4(
(curPt.x + lastPt.x) / 2,
(curPt.y + lastPt.y) / 2,
(curPt.z + lastPt.z) / 2,
1 // strength of curve
);
//add first two points of new curve based on previous curve
this.nurbsControlPoints.length = 0; //reset
this.nurbsControlPoints.push(lastMidPt);
this.nurbsControlPoints.push(curPt);
//fill the control point list with curve data points or if none exist, random points
if (curvePts != null) {
// this.drawLoadedCurve = false;
for (var i = 0; i <= curvePts.length - 2; i++) {
this.nurbsControlPoints.push(curvePts[i]);
}
//calculate resolution (draw speed) of curve
this.resolution =
this.drawSpeed * this.drawSpeedScalar * this.nurbsControlPoints.length;
//remove any interpolation points added to our nurbs curve data
if (this.interpolate) this.removeInterpolationPts();
} else {
for (
var i = 0;
i <= this.numCtrlPts - this.nurbsControlPoints.length;
i++
) {
var ranCtrlPt = new THREE.Vector4(
this.boundaryAreaSize * (Math.random() * 2 - 1),
this.boundaryAreaSize * (Math.random() * 2 - 1),
this.boundaryAreaSize * (Math.random() * 2 - 1),
1
);
this.nurbsControlPoints.push(ranCtrlPt);
}
//calculate resolution (draw speed) of curve
this.resolution = this.drawSpeed * this.nurbsControlPoints.length;
}
// update knots if number of control points change
if (this.nurbsControlPoints.length != this.nurbsKnots.length - 4) {
this.generateKnots();
}
var nextEndPt = this.nurbsControlPoints[this.nurbsControlPoints.length - 1];
var nextLastPt = this.nurbsControlPoints[this.nurbsControlPoints.length - 2];
var midPt = new THREE.Vector4(
(nextEndPt.x + nextLastPt.x) / 2,
(nextEndPt.y + nextLastPt.y) / 2,
(nextEndPt.z + nextLastPt.z) / 2,
1
);
// fill array except for last element in curve, which is replaced with midPt
for (var i = 0; i < this.nurbsControlPoints.length - 1; i++) {
drawPoints.push(this.nurbsControlPoints[i]);
}
drawPoints.push(midPt);
this.spline = new THREE.NURBSCurve(
this.nurbsDegree,
this.nurbsKnots,
drawPoints
);
this.segmentPoints = this.spline.getPoints(this.resolution);
// drawCtrlPts(lastPt, lastMidPt, midPt, nextEndPt);
};
NurbsRibbon.prototype.generateKnots = function() {
this.nurbsKnots.length = 0; // reset array
//here we generate the knot array, should increment 0-1 with and array always begins with 0,0,0 and ends with 1,1,1
for (var i = 0; i <= this.nurbsDegree; i++) {
this.nurbsKnots.push(0); //0,0,0
}
for (var i = 0; i < this.nurbsControlPoints.length; i++) {
var knot = (i + 1) / (this.nurbsControlPoints.length - this.nurbsDegree);
this.nurbsKnots.push(THREE.Math.clamp(knot, 0, 1));
}
};
NurbsRibbon.prototype.addInterpolationPts = function() {
let lastPt = this.nurbsControlPoints[
this.nurbsControlPoints.length - 1
].clone();
let nextPt = this.loadedCurvePts[0];
//if the distance between last curve and new curve is greater than threshold amount, add extra points to smooth the transition
let dist = new THREE.Vector3(lastPt.x, lastPt.y, lastPt.z).distanceTo(
this.loadedCurvePts[0]
);
let smoothDistance = 2.0;
if (dist > smoothDistance) {
this.interpolate = true;
for (var i = 0; i < this.numInterplPts; i++) {
var interplPt = new THREE.Vector3().lerp(
lastPt,
nextPt,
i / this.numInterplPts
);
this.loadedCurvePts.unshift(interplPt);
}
}
};
NurbsRibbon.prototype.removeInterpolationPts = function() {
//remove the interpolation points from the loaded curve point array
for (var i = 0; i < this.numInterplPts; i++) {
this.loadedCurvePts.shift();
}
this.interpolate = false;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment