Skip to content

Commit

Permalink
Fix svg path.getPointAtLength(). fix #1766
Browse files Browse the repository at this point in the history
  • Loading branch information
lavrton committed Jun 12, 2024
1 parent 0a99665 commit 8be222e
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 56 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

- Fix svg `path.getPointAtLength()` calculations in some cases
- Fix `shape.getClientRect()` when any of parents is cached

### 9.3.11 (2024-05-23)
Expand Down
75 changes: 26 additions & 49 deletions src/shapes/Path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,61 +320,38 @@ export class Path extends Shape<PathConfig> {
}

static getPointOnLine(dist, P1x, P1y, P2x, P2y, fromX?, fromY?) {
if (fromX === undefined) {
fromX = P1x;
}
if (fromY === undefined) {
fromY = P1y;
}
fromX = fromX ?? P1x;
fromY = fromY ?? P1y;

var m = (P2y - P1y) / (P2x - P1x + 0.00000001);
var run = Math.sqrt((dist * dist) / (1 + m * m));
if (P2x < P1x) {
run *= -1;
const len = this.getLineLength(P1x, P1y, P2x, P2y);
if (len < 1e-10) {
return { x: P1x, y: P1y };
}
var rise = m * run;
var pt;

if (P2x === P1x) {
// vertical line
pt = {
x: fromX,
y: fromY + rise,
};
} else if ((fromY - P1y) / (fromX - P1x + 0.00000001) === m) {
pt = {
x: fromX + run,
y: fromY + rise,
};
} else {
var ix, iy;

var len = this.getLineLength(P1x, P1y, P2x, P2y);
// if (len < 0.00000001) {
// return {
// x: P1x,
// y: P1y,
// };
// }
var u = (fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y);
u = u / (len * len);
ix = P1x + u * (P2x - P1x);
iy = P1y + u * (P2y - P1y);

var pRise = this.getLineLength(fromX, fromY, ix, iy);
var pRun = Math.sqrt(dist * dist - pRise * pRise);
run = Math.sqrt((pRun * pRun) / (1 + m * m));
if (P2x < P1x) {
run *= -1;
}
rise = m * run;
pt = {
x: ix + run,
y: iy + rise,
};
// Vertical line
return { x: fromX, y: fromY + (P2y > P1y ? dist : -dist) };
}

const m = (P2y - P1y) / (P2x - P1x);
const run = Math.sqrt((dist * dist) / (1 + m * m)) * (P2x < P1x ? -1 : 1);
const rise = m * run;

if (Math.abs(fromY - P1y - m * (fromX - P1x)) < 1e-10) {
return { x: fromX + run, y: fromY + rise };
}

return pt;
const u =
((fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y)) / (len * len);
const ix = P1x + u * (P2x - P1x);
const iy = P1y + u * (P2y - P1y);
const pRise = this.getLineLength(fromX, fromY, ix, iy);
const pRun = Math.sqrt(dist * dist - pRise * pRise);
const adjustedRun =
Math.sqrt((pRun * pRun) / (1 + m * m)) * (P2x < P1x ? -1 : 1);
const adjustedRise = m * adjustedRun;

return { x: ix + adjustedRun, y: iy + adjustedRise };
}

static getPointOnCubicBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
Expand Down
58 changes: 51 additions & 7 deletions test/unit/Path-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1134,11 +1134,11 @@ describe('Path', function () {

assert.deepEqual(points, [
{ x: 300, y: 10 },
{ x: 290.2871413779118, y: 27.48314552325543 },
{ x: 280.57428275582356, y: 44.96629104651086 },
{ x: 270.86142413373534, y: 62.4494365697663 },
{ x: 261.1485655116471, y: 79.93258209302172 },
{ x: 251.43570688955887, y: 97.41572761627717 },
{ x: 290.28714137642737, y: 27.483145522430753 },
{ x: 280.57428275285474, y: 44.96629104486151 },
{ x: 270.86142412928206, y: 62.44943656729226 },
{ x: 261.1485655057094, y: 79.93258208972301 },
{ x: 251.4357068821368, y: 97.41572761215377 },
{ x: 230.89220826660141, y: 87.23996356219386 },
{ x: 207.0639321224534, y: 74.08466390481559 },
{ x: 182.87529785963875, y: 63.52674972743341 },
Expand Down Expand Up @@ -1168,6 +1168,47 @@ describe('Path', function () {
stage.add(layer);
});

it('get point at path with float attrs', function () {
var stage = addStage();
var layer = new Konva.Layer();

const data =
'M419.0000314094981 342.88624187900973 L419.00003140949804 577.0038889378335 L465.014001082264 577.0038889378336 Z';
var path = new Konva.Path({
stroke: 'red',
strokeWidth: 3,
data,
});
layer.add(path);
if (isBrowser) {
const SVGPath = document.createElementNS(
'http://www.w3.org/2000/svg',
'path'
) as SVGPathElement;
SVGPath.setAttribute('d', data);
for (var i = 0; i < path.getLength(); i += 1) {
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);
const position = SVGPath.getPointAtLength(i);
assert(
Math.abs(p.x - position.x) <= 1,
'error for x should be smaller than 10% for i = ' + i
);
assert(
Math.abs(p.y - position.y) <= 1,
'error for y should be smaller than 10% for i = ' + i
);
}
}
});

it('get point at path - bezier', function () {
var stage = addStage();
var layer = new Konva.Layer();
Expand Down Expand Up @@ -1618,8 +1659,11 @@ describe('Path', function () {
layer.add(path);
stage.add(layer);

const trace = layer.getContext().getTrace()
const trace = layer.getContext().getTrace();

assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();moveTo(200,100);lineTo(300,100);lineTo(300,150);closePath();fillStyle=#ccc;fill(evenodd);restore();');
assert.equal(
trace,
'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();moveTo(200,100);lineTo(300,100);lineTo(300,150);closePath();fillStyle=#ccc;fill(evenodd);restore();'
);
});
});

0 comments on commit 8be222e

Please sign in to comment.