From c16e3d1d0b3620c2929791613a1918455b4171d7 Mon Sep 17 00:00:00 2001 From: Dyakin Date: Sun, 19 Aug 2018 23:19:13 +0300 Subject: [PATCH] get point at path --- src/shapes/Path.js | 57 +++++++++++++++++++++++++++++++++++ test/unit/shapes/Path-test.js | 34 +++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/src/shapes/Path.js b/src/shapes/Path.js index 186cb15c..593d4fc8 100644 --- a/src/shapes/Path.js +++ b/src/shapes/Path.js @@ -34,8 +34,16 @@ this.className = 'Path'; this.dataArray = Konva.Path.parsePathData(this.getData()); + this.pathLength = 0; + for (var i = 0; i < this.dataArray.length; ++i) { + this.pathLength += this.dataArray[i].pathLength; + } this.on('dataChange.konva', function() { that.dataArray = Konva.Path.parsePathData(this.getData()); + this.pathLength = 0; + for (var i = 0; i < this.dataArray.length; ++i) { + this.pathLength += this.dataArray[i].pathLength; + } }); this.sceneFunc(this._sceneFunc); @@ -116,6 +124,55 @@ width: Math.round(maxX - minX), height: Math.round(maxY - minY) }; + }, + getLength: function () { + return this.pathLength; + }, + getPointAtLength: function (length) { + var point, i = 0, ii = this.dataArray.length; + + if (!ii) { + return null; + } + + while (length > this.dataArray[i].pathLength && i < ii) { + length -= this.dataArray[i].pathLength; + ++i; + } + + if (i === ii) { + point = this.dataArray[i - 1].points.slice(-2); + return { + x: point[0], + y: point[1] + }; + } + + if (length < 0.01) { + point = this.dataArray[i].points.slice(0, 2); + return { + x: point[0], + y: point[1] + }; + } + + var cp = this.dataArray[i]; + var p = cp.points; + switch (cp.command) { + case 'L': + return Konva.Path.getPointOnLine(length, cp.start.x, cp.start.y, p[0], p[1]); + case 'C': + return Konva.Path.getPointOnCubicBezier(length / cp.pathLength, cp.start.x, cp.start.y, p[0], p[1], p[2], p[3], p[4], p[5]); + case 'Q': + return Konva.Path.getPointOnQuadraticBezier(length / cp.pathLength, cp.start.x, cp.start.y, p[0], p[1], p[2], p[3]); + case 'A': + var cx = p[0], cy = p[1], rx = p[2], ry = p[3], theta = p[4], dTheta = p[5], psi = p[6]; + theta += dTheta * length / cp.pathLength; + return Konva.Path.getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi); + } + + return null; + } }; Konva.Util.extend(Konva.Path, Konva.Shape); diff --git a/test/unit/shapes/Path-test.js b/test/unit/shapes/Path-test.js index 60f6862c..a27a68e6 100644 --- a/test/unit/shapes/Path-test.js +++ b/test/unit/shapes/Path-test.js @@ -987,6 +987,40 @@ suite('Path', function() { ); }); + // ====================================================== + test('get path length', function() { + var path = new Konva.Path({ data: 'M 10,10 L 20,10 L 20,20' }); + assert.equal(path.getLength(), 20); + }); + + // ====================================================== + + test('get point at path', function() { + var stage = addStage(); + var layer = new Konva.Layer(); + + var path = new Konva.Path({ + stroke: 'red', + strokeWidth: 3, + data: 'M 300,10 L 250,100 A 100 40 30 1 0 150 150 C 160,100, 290,100, 300,150' + }); + layer.add(path); + + for (var i = 0; i < path.getLength(); i += 20) { + var p = path.getPointAtLength(i); + var circle = new Konva.Circle({ + x: p.x, + y: p.y, + radius: 2, + fill: 'black', + stroke: 'black' + }); + layer.add(circle); + } + + stage.add(layer); + }); + // ====================================================== test('Borneo Map (has scientific notation: -10e-4)', function() { var stage = addStage();