blob: c2985f74e17f19c294706e35da366f3d98f10941 [file] [log] [blame]
function bezFunction() {
var easingFunctions = [];
var math = Math;
function pointOnLine2D(x1, y1, x2, y2, x3, y3) {
var det1 = (x1*y2) + (y1*x3) + (x2*y3) - (x3*y2) - (y3*x1) - (x2*y1);
return det1 > -0.001 && det1 < 0.001;
}
function pointOnLine3D(x1, y1, z1, x2, y2, z2, x3, y3, z3) {
if(z1 === 0 && z2 === 0 && z3 === 0) {
return pointOnLine2D(x1, y1, x2, y2, x3, y3);
}
var dist1 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) + Math.pow(z2 - z1, 2));
var dist2 = Math.sqrt(Math.pow(x3 - x1, 2) + Math.pow(y3 - y1, 2) + Math.pow(z3 - z1, 2));
var dist3 = Math.sqrt(Math.pow(x3 - x2, 2) + Math.pow(y3 - y2, 2) + Math.pow(z3 - z2, 2));
var diffDist;
if(dist1 > dist2) {
if(dist1 > dist3) {
diffDist = dist1 - dist2 - dist3;
} else {
diffDist = dist3 - dist2 - dist1;
}
} else if(dist3 > dist2) {
diffDist = dist3 - dist2 - dist1;
} else {
diffDist = dist2 - dist1 - dist3;
}
return diffDist > -0.0001 && diffDist < 0.0001;
}
var getBezierLength = (function () {
return function (pt1, pt2, pt3, pt4) {
var curveSegments = defaultCurveSegments;
var k;
var i, len;
var ptCoord, perc, addedLength = 0;
var ptDistance;
var point = [], lastPoint = [];
var lengthData = bezier_length_pool.newElement();
len = pt3.length;
for(k=0;k<curveSegments;k+=1) {
perc = k/(curveSegments-1);
ptDistance = 0;
for(i=0;i<len;i+=1) {
ptCoord = bm_pow(1-perc, 3)*pt1[i]+3*bm_pow(1-perc, 2)*perc*pt3[i]+3*(1-perc)*bm_pow(perc, 2)*pt4[i]+bm_pow(perc, 3)*pt2[i];
point[i] = ptCoord;
if(lastPoint[i] !== null) {
ptDistance += bm_pow(point[i] - lastPoint[i], 2);
}
lastPoint[i] = point[i];
}
if(ptDistance) {
ptDistance = bm_sqrt(ptDistance);
addedLength += ptDistance;
}
lengthData.percents[k] = perc;
lengthData.lengths[k] = addedLength;
}
lengthData.addedLength = addedLength;
return lengthData;
};
}());
function getSegmentsLength(shapeData) {
var segmentsLength = segments_length_pool.newElement();
var closed = shapeData.c;
var pathV = shapeData.v;
var pathO = shapeData.o;
var pathI = shapeData.i;
var i, len = shapeData._length;
var lengths = segmentsLength.lengths;
var totalLength = 0;
for(i=0;i<len-1;i+=1) {
lengths[i] = getBezierLength(pathV[i], pathV[i+1], pathO[i], pathI[i+1]);
totalLength += lengths[i].addedLength;
}
if(closed && len) {
lengths[i] = getBezierLength(pathV[i], pathV[0], pathO[i], pathI[0]);
totalLength += lengths[i].addedLength;
}
segmentsLength.totalLength = totalLength;
return segmentsLength;
}
function BezierData(length) {
this.segmentLength = 0;
this.points = new Array(length);
}
function PointData(partial, point) {
this.partialLength = partial;
this.point = point;
}
var buildBezierData = (function () {
var storedData = {};
return function (pt1, pt2, pt3, pt4) {
var bezierName = (pt1[0]+'_'+pt1[1]+'_'+pt2[0]+'_'+pt2[1]+'_'+pt3[0]+'_'+pt3[1]+'_'+pt4[0]+'_'+pt4[1]).replace(/\./g, 'p');
if(!storedData[bezierName]) {
var curveSegments = defaultCurveSegments;
var k, i, len;
var ptCoord, perc, addedLength = 0;
var ptDistance;
var point, lastPoint = null;
if (pt1.length === 2 && (pt1[0] != pt2[0] || pt1[1] != pt2[1]) && pointOnLine2D(pt1[0], pt1[1], pt2[0], pt2[1], pt1[0]+pt3[0], pt1[1]+pt3[1]) && pointOnLine2D(pt1[0], pt1[1], pt2[0], pt2[1], pt2[0]+pt4[0], pt2[1]+pt4[1])) {
curveSegments = 2;
}
var bezierData = new BezierData(curveSegments);
len = pt3.length;
for (k = 0; k < curveSegments; k += 1) {
point = createSizedArray(len);
perc = k / (curveSegments - 1);
ptDistance = 0;
for (i = 0; i < len; i += 1) {
ptCoord = bm_pow(1-perc, 3)*pt1[i]+3*bm_pow(1-perc, 2)*perc*(pt1[i] + pt3[i])+3*(1-perc)*bm_pow(perc, 2)*(pt2[i] + pt4[i])+bm_pow(perc, 3)*pt2[i];
point[i] = ptCoord;
if(lastPoint !== null) {
ptDistance += bm_pow(point[i] - lastPoint[i], 2);
}
}
ptDistance = bm_sqrt(ptDistance);
addedLength += ptDistance;
bezierData.points[k] = new PointData(ptDistance, point);
lastPoint = point;
}
bezierData.segmentLength = addedLength;
storedData[bezierName] = bezierData;
}
return storedData[bezierName];
};
}());
function getDistancePerc(perc, bezierData) {
var percents = bezierData.percents;
var lengths = bezierData.lengths;
var len = percents.length;
var initPos = bm_floor((len-1)*perc);
var lengthPos = perc*bezierData.addedLength;
var lPerc = 0;
if(initPos === len - 1 || initPos === 0 || lengthPos === lengths[initPos]) {
return percents[initPos];
}else{
var dir = lengths[initPos] > lengthPos ? -1 : 1;
var flag = true;
while(flag) {
if(lengths[initPos] <= lengthPos && lengths[initPos+1] > lengthPos) {
lPerc = (lengthPos - lengths[initPos]) / (lengths[initPos+1] - lengths[initPos]);
flag = false;
}else{
initPos += dir;
}
if(initPos < 0 || initPos >= len - 1) {
// FIX for TypedArrays that don't store floating point values with enough accuracy
if(initPos === len - 1) {
return percents[initPos];
}
flag = false;
}
}
return percents[initPos] + (percents[initPos+1] - percents[initPos])*lPerc;
}
}
function getPointInSegment(pt1, pt2, pt3, pt4, percent, bezierData) {
var t1 = getDistancePerc(percent, bezierData);
var u0 = 1;
var u1 = 1 - t1;
var ptX = Math.round((u1*u1*u1* pt1[0] + (t1*u1*u1 + u1*t1*u1 + u1*u1*t1)* pt3[0] + (t1*t1*u1 + u1*t1*t1 + t1*u1*t1)*pt4[0] + t1*t1*t1* pt2[0])* 1000) / 1000;
var ptY = Math.round((u1*u1*u1* pt1[1] + (t1*u1*u1 + u1*t1*u1 + u1*u1*t1)* pt3[1] + (t1*t1*u1 + u1*t1*t1 + t1*u1*t1)*pt4[1] + t1*t1*t1* pt2[1])* 1000) / 1000;
return [ptX, ptY];
}
function getSegmentArray() {
}
var bezier_segment_points = createTypedArray('float32', 8);
function getNewSegment(pt1, pt2, pt3, pt4, startPerc, endPerc, bezierData) {
startPerc = startPerc < 0 ? 0 : startPerc > 1 ? 1 : startPerc;
var t0 = getDistancePerc(startPerc, bezierData);
endPerc = endPerc > 1 ? 1 : endPerc;
var t1 = getDistancePerc(endPerc, bezierData);
var i, len = pt1.length;
var u0 = 1 - t0;
var u1 = 1 - t1;
var u0u0u0 = u0*u0*u0;
var t0u0u0_3 = t0*u0*u0*3;
var t0t0u0_3 = t0*t0*u0*3;
var t0t0t0 = t0*t0*t0;
//
var u0u0u1 = u0*u0*u1;
var t0u0u1_3 = t0*u0*u1 + u0*t0*u1 + u0*u0*t1;
var t0t0u1_3 = t0*t0*u1 + u0*t0*t1 + t0*u0*t1;
var t0t0t1 = t0*t0*t1;
//
var u0u1u1 = u0*u1*u1;
var t0u1u1_3 = t0*u1*u1 + u0*t1*u1 + u0*u1*t1;
var t0t1u1_3 = t0*t1*u1 + u0*t1*t1 + t0*u1*t1;
var t0t1t1 = t0*t1*t1;
//
var u1u1u1 = u1*u1*u1;
var t1u1u1_3 = t1*u1*u1 + u1*t1*u1 + u1*u1*t1;
var t1t1u1_3 = t1*t1*u1 + u1*t1*t1 + t1*u1*t1;
var t1t1t1 = t1*t1*t1;
for(i=0;i<len;i+=1) {
bezier_segment_points[i * 4] = Math.round((u0u0u0 * pt1[i] + t0u0u0_3 * pt3[i] + t0t0u0_3 * pt4[i] + t0t0t0 * pt2[i]) * 1000) / 1000;
bezier_segment_points[i * 4 + 1] = Math.round((u0u0u1 * pt1[i] + t0u0u1_3 * pt3[i] + t0t0u1_3 * pt4[i] + t0t0t1 * pt2[i]) * 1000) / 1000;
bezier_segment_points[i * 4 + 2] = Math.round((u0u1u1 * pt1[i] + t0u1u1_3 * pt3[i] + t0t1u1_3 * pt4[i] + t0t1t1 * pt2[i]) * 1000) / 1000;
bezier_segment_points[i * 4 + 3] = Math.round((u1u1u1 * pt1[i] + t1u1u1_3 * pt3[i] + t1t1u1_3 * pt4[i] + t1t1t1 * pt2[i]) * 1000) / 1000;
}
return bezier_segment_points;
}
return {
getSegmentsLength : getSegmentsLength,
getNewSegment : getNewSegment,
getPointInSegment : getPointInSegment,
buildBezierData : buildBezierData,
pointOnLine2D : pointOnLine2D,
pointOnLine3D : pointOnLine3D
};
}
var bez = bezFunction();