123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- import {
- bmPow,
- bmFloor,
- bmSqrt,
- getDefaultCurveSegments,
- } from './common';
- import {
- createSizedArray,
- createTypedArray,
- } from './helpers/arrays';
- import segmentsLengthPool from './pooling/segments_length_pool';
- import bezierLengthPool from './pooling/bezier_length_pool';
- function bezFunction() {
- 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 = getDefaultCurveSegments();
- var k;
- var i;
- var len;
- var ptCoord;
- var perc;
- var addedLength = 0;
- var ptDistance;
- var point = [];
- var lastPoint = [];
- var lengthData = bezierLengthPool.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 = bmPow(1 - perc, 3) * pt1[i] + 3 * bmPow(1 - perc, 2) * perc * pt3[i] + 3 * (1 - perc) * bmPow(perc, 2) * pt4[i] + bmPow(perc, 3) * pt2[i];
- point[i] = ptCoord;
- if (lastPoint[i] !== null) {
- ptDistance += bmPow(point[i] - lastPoint[i], 2);
- }
- lastPoint[i] = point[i];
- }
- if (ptDistance) {
- ptDistance = bmSqrt(ptDistance);
- addedLength += ptDistance;
- }
- lengthData.percents[k] = perc;
- lengthData.lengths[k] = addedLength;
- }
- lengthData.addedLength = addedLength;
- return lengthData;
- };
- }());
- function getSegmentsLength(shapeData) {
- var segmentsLength = segmentsLengthPool.newElement();
- var closed = shapeData.c;
- var pathV = shapeData.v;
- var pathO = shapeData.o;
- var pathI = shapeData.i;
- var i;
- var 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 = getDefaultCurveSegments();
- var k;
- var i;
- var len;
- var ptCoord;
- var perc;
- var addedLength = 0;
- var ptDistance;
- var point;
- var 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 = bmPow(1 - perc, 3) * pt1[i] + 3 * bmPow(1 - perc, 2) * perc * (pt1[i] + pt3[i]) + 3 * (1 - perc) * bmPow(perc, 2) * (pt2[i] + pt4[i]) + bmPow(perc, 3) * pt2[i];
- point[i] = ptCoord;
- if (lastPoint !== null) {
- ptDistance += bmPow(point[i] - lastPoint[i], 2);
- }
- }
- ptDistance = bmSqrt(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 = bmFloor((len - 1) * perc);
- var lengthPos = perc * bezierData.addedLength;
- var lPerc = 0;
- if (initPos === len - 1 || initPos === 0 || lengthPos === lengths[initPos]) {
- return percents[initPos];
- }
- 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 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];
- }
- var bezierSegmentPoints = createTypedArray('float32', 8);
- function getNewSegment(pt1, pt2, pt3, pt4, startPerc, endPerc, bezierData) {
- if (startPerc < 0) {
- startPerc = 0;
- } else if (startPerc > 1) {
- startPerc = 1;
- }
- var t0 = getDistancePerc(startPerc, bezierData);
- endPerc = endPerc > 1 ? 1 : endPerc;
- var t1 = getDistancePerc(endPerc, bezierData);
- var i;
- var len = pt1.length;
- var u0 = 1 - t0;
- var u1 = 1 - t1;
- var u0u0u0 = u0 * u0 * u0;
- var t0u0u0_3 = t0 * u0 * u0 * 3; // eslint-disable-line camelcase
- var t0t0u0_3 = t0 * t0 * u0 * 3; // eslint-disable-line camelcase
- var t0t0t0 = t0 * t0 * t0;
- //
- var u0u0u1 = u0 * u0 * u1;
- var t0u0u1_3 = t0 * u0 * u1 + u0 * t0 * u1 + u0 * u0 * t1; // eslint-disable-line camelcase
- var t0t0u1_3 = t0 * t0 * u1 + u0 * t0 * t1 + t0 * u0 * t1; // eslint-disable-line camelcase
- var t0t0t1 = t0 * t0 * t1;
- //
- var u0u1u1 = u0 * u1 * u1;
- var t0u1u1_3 = t0 * u1 * u1 + u0 * t1 * u1 + u0 * u1 * t1; // eslint-disable-line camelcase
- var t0t1u1_3 = t0 * t1 * u1 + u0 * t1 * t1 + t0 * u1 * t1; // eslint-disable-line camelcase
- var t0t1t1 = t0 * t1 * t1;
- //
- var u1u1u1 = u1 * u1 * u1;
- var t1u1u1_3 = t1 * u1 * u1 + u1 * t1 * u1 + u1 * u1 * t1; // eslint-disable-line camelcase
- var t1t1u1_3 = t1 * t1 * u1 + u1 * t1 * t1 + t1 * u1 * t1; // eslint-disable-line camelcase
- var t1t1t1 = t1 * t1 * t1;
- for (i = 0; i < len; i += 1) {
- bezierSegmentPoints[i * 4] = math.round((u0u0u0 * pt1[i] + t0u0u0_3 * pt3[i] + t0t0u0_3 * pt4[i] + t0t0t0 * pt2[i]) * 1000) / 1000; // eslint-disable-line camelcase
- bezierSegmentPoints[i * 4 + 1] = math.round((u0u0u1 * pt1[i] + t0u0u1_3 * pt3[i] + t0t0u1_3 * pt4[i] + t0t0t1 * pt2[i]) * 1000) / 1000; // eslint-disable-line camelcase
- bezierSegmentPoints[i * 4 + 2] = math.round((u0u1u1 * pt1[i] + t0u1u1_3 * pt3[i] + t0t1u1_3 * pt4[i] + t0t1t1 * pt2[i]) * 1000) / 1000; // eslint-disable-line camelcase
- bezierSegmentPoints[i * 4 + 3] = math.round((u1u1u1 * pt1[i] + t1u1u1_3 * pt3[i] + t1t1u1_3 * pt4[i] + t1t1t1 * pt2[i]) * 1000) / 1000; // eslint-disable-line camelcase
- }
- return bezierSegmentPoints;
- }
- return {
- getSegmentsLength: getSegmentsLength,
- getNewSegment: getNewSegment,
- getPointInSegment: getPointInSegment,
- buildBezierData: buildBezierData,
- pointOnLine2D: pointOnLine2D,
- pointOnLine3D: pointOnLine3D,
- };
- }
- const bez = bezFunction();
- export default bez;
|