var Vec3 = require('./Vec3');
Path of points
var points = [
new Vec3(-1.5, -1.0, 0),
new Vec3(-0.5, -0.7, 0),
new Vec3( 0.5, 0.7, 0),
new Vec3( 1.5, 1.0, 0)
]
var path = new Path(points)
var Vec3 = require('./Vec3');
points
- Array of points { Array of Vec3 }closed
- is it a closed path or not? { Boolean }
function Path(points, closed) {
this.points = points || [];
this.dirtyLength = true;
this.closed = closed || false;
this.samplesCount = 1000;
}
Path.prototype.addPoint = function(p) {
return this.points.push(p);
shouldnt this return this
?
};
Path.prototype.getPoint = function(t, debug) {
var point = t * (this.points.length - 1);
var intPoint = Math.floor(point);
var weight = point - intPoint;
var c0 = intPoint;
var c1 = intPoint + 1;
if (intPoint === this.points.length - 1) {
c0 = intPoint;
c1 = intPoint;
}
var vec = new Vec3();
vec.x = this.points[c0].x + (this.points[c1].x - this.points[c0].x) * weight;
vec.y = this.points[c0].y + (this.points[c1].y - this.points[c0].y) * weight;
vec.z = this.points[c0].z + (this.points[c1].z - this.points[c0].z) * weight;
return vec;
};
Path.prototype.getPointAt = function(d) {
if (!this.closed) {
d = Math.max(0, Math.min(d, 1));
}
if (this.dirtyLength) {
this.precalculateLength();
}
var k = 0;
for (var i=0; i<this.accumulatedLengthRatios.length; i++) {
if (this.accumulatedLengthRatios[i] > d - 1/this.samplesCount) {
k = this.accumulatedRatios[i];
break;
}
}
return this.getPoint(k, true);
};
naive implementation
Finds closest point to given pointpoint
- point as a { Vec3 }
returns point as a { Vec3 }
Path.prototype.getClosestPoint = function(point) {
if (this.dirtyLength) {
this.precalculateLength();
}
var closesPoint = this.precalculatedPoints.reduce(function(best, p) {
var dist = point.squareDistance(p);
if (dist < best.dist) {
return { dist: dist, point: p };
}
else return best;
}, { dist: Infinity, point: null });
return closesPoint.point;
}
Path.prototype.getClosestPointRatio = function(point) {
if (this.dirtyLength) {
this.precalculateLength();
}
var closesPoint = this.precalculatedPoints.reduce(function(best, p, pIndex) {
var dist = point.squareDistance(p);
if (dist < best.dist) {
return { dist: dist, point: p, index: pIndex };
}
else return best;
}, { dist: Infinity, point: null, index: -1 });
return this.accumulatedLengthRatios[closesPoint.index];
}
Path.prototype.close = function() {
return this.closed = true;
};
Path.prototype.isClosed = function() {
return this.closed;
};
Path.prototype.reverse = function() {
this.points = this.points.reverse();
return this.dirtyLength = true;
};
Path.prototype.precalculateLength = function() {
this.accumulatedRatios = [];
this.accumulatedLengthRatios = [];
this.accumulatedLengths = [];
this.precalculatedPoints = [];
var step = 1 / this.samplesCount;
var k = 0;
var totalLength = 0;
var point = null;
var prevPoint = null;
for (var i=0; i<this.samplesCount; i++) {
prevPoint = point;
point = this.getPoint(k);
if (i > 0) {
totalLength += point.dup().sub(prevPoint).length();;
}
this.accumulatedRatios.push(k);
this.accumulatedLengths.push(totalLength);
this.precalculatedPoints.push(point);
k += step;
}
for (var i=0; i<this.accumulatedLengths.length - 1; i++) {
this.accumulatedLengthRatios.push(this.accumulatedLengths[i] / totalLength);
}
this.length = totalLength;
return this.dirtyLength = false;
};
module.exports = Path;