diff --git a/src/util/path.js b/src/util/path.js index 319e153cca1..7220702e91d 100644 --- a/src/util/path.js +++ b/src/util/path.js @@ -229,7 +229,13 @@ return segsNorm; }; - + /** + * This function take a parsed SVG path and make it simpler for fabricJS logic. + * simplification consist of: only UPPERCASE absolute commands ( relative converted to absolute ) + * S converted in C, T converted in Q, A converted in C. + * @param {Array} path the array of commands of a parsed svg path for fabric.Path + * @return {Array} the simplified array of commands of a parsed svg path for fabric.Path + */ function makePathSimpler(path) { // x and y represent the last point of the path. the previous command point. // we add them to each relative command to make it an absolute comment. @@ -455,6 +461,8 @@ }; } + // this will run over a path segment ( a cubic or quadratic segment) and approximate it + // with 100 segemnts. This will good enough to calculate the length of the curve function pathIterator(iterator, x1, y1) { var tempP = { x: x1, y: y1 }, p, tmpLen = 0, perc; for (perc = 0.01; perc <= 1; perc += 0.01) { @@ -465,7 +473,43 @@ return tmpLen; } - //measures the length of a pre-simplified path + /** + * Given a pathInfo, and a distance in pixels, find the percentage from 0 to 1 + * that correspond to that pixels run over the path. + * The percentage will be then used to find the correct point on the canvas for the path. + * @param {Array} segInfo fabricJS collection of information on a parsed path + * @param {Number} distance from starting point, in pixels. + * @return {Number} length of segment + */ + function findPercentageForDistance(segInfo, distance) { + var perc = 0, tmpLen = 0, iterator = segInfo.iterator, tempP = { x: segInfo.x, y: segInfo.y }, + p, nextLen, nextStep = 0.01; + // nextStep > 0.0001 covers 0.00015625 that 1/64th of 1/100 + // the path + while (tmpLen < distance && perc <= 1 && nextStep > 0.0001) { + p = iterator(perc); + nextLen = calcLineLength(tempP.x, tempP.y, p.x, p.y); + // compare tmpLen each cycle with distance, decide next perc to test. + if ((nextLen + tmpLen) > distance) { + // we discard this step and we make smaller steps. + nextStep /= 2; + perc -= nextStep; + } + else { + tempP = p; + perc += nextStep; + tmpLen += nextLen; + } + } + return p; + } + + /** + * Run over a parsed and simplifed path and extrac some informations. + * informations are length of each command and starting point + * @param {Array} path fabricJS parsed path commands + * @return {Array} path commands informations + */ function getPathSegmentsInfo(path) { var totalLength = 0, len = path.length, current, //x2 and y2 are the coords of segment start @@ -500,6 +544,7 @@ current[5], current[6] ); + tempInfo.iterator = iterator; tempInfo.length = pathIterator(iterator, x1, y1); x1 = current[5]; y1 = current[6]; @@ -513,6 +558,7 @@ current[3], current[4] ); + tempInfo.iterator = iterator; tempInfo.length = pathIterator(iterator, x1, y1); x1 = current[3]; y1 = current[4]; @@ -546,6 +592,7 @@ } var segInfo = infos[i], segPercent = distance / segInfo.length, command = segInfo.command, segment = path[i]; + switch (command) { case 'M': return { x: segInfo.x, y: segInfo.y }; @@ -555,35 +602,15 @@ new fabric.Point(segInfo.destX, segInfo.destY), segPercent ); - break; case 'L': return new fabric.Point(segInfo.x, segInfo.y).lerp( new fabric.Point(segment[1], segment[2]), segPercent ); - break; case 'C': - return getPointOnCubicBezierIterator( - segInfo.x, - segInfo.y, - segment[1], - segment[2], - segment[3], - segment[4], - segment[5], - segment[6] - )(segPercent); - break; + return findPercentageForDistance(segInfo, distance); case 'Q': - return getPointOnQuadraticBezierIterator( - segInfo.x, - segInfo.y, - segment[1], - segment[2], - segment[3], - segment[4] - )(segPercent); - break; + return findPercentageForDistance(segInfo, distance); } } @@ -685,6 +712,7 @@ /** * Draws arc + * @deprecated * @param {CanvasRenderingContext2D} ctx * @param {Number} fx * @param {Number} fy diff --git a/test/visual/golden/textpath3.png b/test/visual/golden/textpath3.png new file mode 100644 index 00000000000..ff84eab2e0a Binary files /dev/null and b/test/visual/golden/textpath3.png differ diff --git a/test/visual/text_path.js b/test/visual/text_path.js index e75ddfc7cd7..5b5ec2f2a8d 100644 --- a/test/visual/text_path.js +++ b/test/visual/text_path.js @@ -58,5 +58,22 @@ height: 200, }); + function textpath3(canvas, callback) { + var path = new fabric.Path('M -194 -109 C 538 -300 154 50 98 29'); + var text = new fabric.Text('Testing constant distance on bezier curve.', { path: path, top: 30, left: 30 }); + canvas.add(text); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'Textpath on a particular bezier', + code: textpath3, + golden: 'textpath3.png', + percentage: 0.09, + width: 610, + height: 270, + }); + tests.forEach(visualTestLoop(QUnit)); })();