more textpath rendering fixes

This commit is contained in:
Anton Lavrenov
2020-11-17 11:19:23 -05:00
parent ad08fb2103
commit 691ef799c7
5 changed files with 120 additions and 49 deletions

View File

@@ -8,7 +8,7 @@
* Konva JavaScript Framework v7.1.7 * Konva JavaScript Framework v7.1.7
* http://konvajs.org/ * http://konvajs.org/
* Licensed under the MIT * Licensed under the MIT
* Date: Mon Nov 16 2020 * Date: Tue Nov 17 2020
* *
* Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS) * Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS)
* Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva) * Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva)
@@ -11755,7 +11755,7 @@
x: Math.round(minX), x: Math.round(minX),
y: Math.round(minY), y: Math.round(minY),
width: Math.round(maxX - minX), width: Math.round(maxX - minX),
height: Math.round(maxY - minY) height: Math.round(maxY - minY),
}; };
}; };
/** /**
@@ -11791,14 +11791,14 @@
point = this.dataArray[i - 1].points.slice(-2); point = this.dataArray[i - 1].points.slice(-2);
return { return {
x: point[0], x: point[0],
y: point[1] y: point[1],
}; };
} }
if (length < 0.01) { if (length < 0.01) {
point = this.dataArray[i].points.slice(0, 2); point = this.dataArray[i].points.slice(0, 2);
return { return {
x: point[0], x: point[0],
y: point[1] y: point[1],
}; };
} }
var cp = this.dataArray[i]; var cp = this.dataArray[i];
@@ -11838,21 +11838,24 @@
// vertical line // vertical line
pt = { pt = {
x: fromX, x: fromX,
y: fromY + rise y: fromY + rise,
}; };
} }
else if ((fromY - P1y) / (fromX - P1x + 0.00000001) === m) { else if ((fromY - P1y) / (fromX - P1x + 0.00000001) === m) {
pt = { pt = {
x: fromX + run, x: fromX + run,
y: fromY + rise y: fromY + rise,
}; };
} }
else { else {
var ix, iy; var ix, iy;
var len = this.getLineLength(P1x, P1y, P2x, P2y); var len = this.getLineLength(P1x, P1y, P2x, P2y);
if (len < 0.00000001) { // if (len < 0.00000001) {
return undefined; // return {
} // x: P1x,
// y: P1y,
// };
// }
var u = (fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y); var u = (fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y);
u = u / (len * len); u = u / (len * len);
ix = P1x + u * (P2x - P1x); ix = P1x + u * (P2x - P1x);
@@ -11866,7 +11869,7 @@
rise = m * run; rise = m * run;
pt = { pt = {
x: ix + run, x: ix + run,
y: iy + rise y: iy + rise,
}; };
} }
return pt; return pt;
@@ -11888,7 +11891,7 @@
var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct); var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
return { return {
x: x, x: x,
y: y y: y,
}; };
}; };
Path.getPointOnQuadraticBezier = function (pct, P1x, P1y, P2x, P2y, P3x, P3y) { Path.getPointOnQuadraticBezier = function (pct, P1x, P1y, P2x, P2y, P3x, P3y) {
@@ -11905,18 +11908,18 @@
var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct); var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
return { return {
x: x, x: x,
y: y y: y,
}; };
}; };
Path.getPointOnEllipticalArc = function (cx, cy, rx, ry, theta, psi) { Path.getPointOnEllipticalArc = function (cx, cy, rx, ry, theta, psi) {
var cosPsi = Math.cos(psi), sinPsi = Math.sin(psi); var cosPsi = Math.cos(psi), sinPsi = Math.sin(psi);
var pt = { var pt = {
x: rx * Math.cos(theta), x: rx * Math.cos(theta),
y: ry * Math.sin(theta) y: ry * Math.sin(theta),
}; };
return { return {
x: cx + (pt.x * cosPsi - pt.y * sinPsi), x: cx + (pt.x * cosPsi - pt.y * sinPsi),
y: cy + (pt.x * sinPsi + pt.y * cosPsi) y: cy + (pt.x * sinPsi + pt.y * cosPsi),
}; };
}; };
/* /*
@@ -11974,7 +11977,7 @@
's', 's',
'S', 'S',
'a', 'a',
'A' 'A',
]; ];
// convert white spaces to commas // convert white spaces to commas
cs = cs.replace(new RegExp(' ', 'g'), ','); cs = cs.replace(new RegExp(' ', 'g'), ',');
@@ -12199,9 +12202,9 @@
points: points, points: points,
start: { start: {
x: startX, x: startX,
y: startY y: startY,
}, },
pathLength: this.calcLength(startX, startY, cmd || c, points) pathLength: this.calcLength(startX, startY, cmd || c, points),
}); });
} }
if (c === 'z' || c === 'Z') { if (c === 'z' || c === 'Z') {
@@ -12209,7 +12212,7 @@
command: 'z', command: 'z',
points: [], points: [],
start: undefined, start: undefined,
pathLength: 0 pathLength: 0,
}); });
} }
} }
@@ -14391,7 +14394,7 @@
var attempts = 0; var attempts = 0;
p1 = undefined; p1 = undefined;
while (Math.abs(glyphWidth - currLen) / glyphWidth > 0.01 && while (Math.abs(glyphWidth - currLen) / glyphWidth > 0.01 &&
attempts < 50) { attempts < 5) {
attempts++; attempts++;
var cumulativePathLength = currLen; var cumulativePathLength = currLen;
while (pathCmd === undefined) { while (pathCmd === undefined) {
@@ -14453,7 +14456,12 @@
currentT += (glyphWidth - currLen) / pathCmd.pathLength; currentT += (glyphWidth - currLen) / pathCmd.pathLength;
} }
else { else {
currentT -= (currLen - glyphWidth) / pathCmd.pathLength; currentT = currentT - (currLen - glyphWidth) / pathCmd.pathLength;
// that one is a weird check
// but I have to add it to fix some drawings (they are in the testing)
if (currentT < 0) {
currentT += 0.02;
}
} }
if (currentT > 1.0) { if (currentT > 1.0) {
currentT = 1.0; currentT = 1.0;

4
konva.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -39,7 +39,7 @@ export class Path extends Shape<PathConfig> {
for (var i = 0; i < this.dataArray.length; ++i) { for (var i = 0; i < this.dataArray.length; ++i) {
this.pathLength += this.dataArray[i].pathLength; this.pathLength += this.dataArray[i].pathLength;
} }
this.on('dataChange.konva', function() { this.on('dataChange.konva', function () {
this.dataArray = Path.parsePathData(this.data()); this.dataArray = Path.parsePathData(this.data());
this.pathLength = 0; this.pathLength = 0;
for (var i = 0; i < this.dataArray.length; ++i) { for (var i = 0; i < this.dataArray.length; ++i) {
@@ -108,7 +108,7 @@ export class Path extends Shape<PathConfig> {
} }
getSelfRect() { getSelfRect() {
var points = []; var points = [];
this.dataArray.forEach(function(data) { this.dataArray.forEach(function (data) {
if (data.command === 'A') { if (data.command === 'A') {
// Approximates by breaking curve into line segments // Approximates by breaking curve into line segments
var start = data.points[4]; var start = data.points[4];
@@ -192,7 +192,7 @@ export class Path extends Shape<PathConfig> {
x: Math.round(minX), x: Math.round(minX),
y: Math.round(minY), y: Math.round(minY),
width: Math.round(maxX - minX), width: Math.round(maxX - minX),
height: Math.round(maxY - minY) height: Math.round(maxY - minY),
}; };
} }
/** /**
@@ -233,7 +233,7 @@ export class Path extends Shape<PathConfig> {
point = this.dataArray[i - 1].points.slice(-2); point = this.dataArray[i - 1].points.slice(-2);
return { return {
x: point[0], x: point[0],
y: point[1] y: point[1],
}; };
} }
@@ -241,7 +241,7 @@ export class Path extends Shape<PathConfig> {
point = this.dataArray[i].points.slice(0, 2); point = this.dataArray[i].points.slice(0, 2);
return { return {
x: point[0], x: point[0],
y: point[1] y: point[1],
}; };
} }
@@ -312,20 +312,23 @@ export class Path extends Shape<PathConfig> {
// vertical line // vertical line
pt = { pt = {
x: fromX, x: fromX,
y: fromY + rise y: fromY + rise,
}; };
} else if ((fromY - P1y) / (fromX - P1x + 0.00000001) === m) { } else if ((fromY - P1y) / (fromX - P1x + 0.00000001) === m) {
pt = { pt = {
x: fromX + run, x: fromX + run,
y: fromY + rise y: fromY + rise,
}; };
} else { } else {
var ix, iy; var ix, iy;
var len = this.getLineLength(P1x, P1y, P2x, P2y); var len = this.getLineLength(P1x, P1y, P2x, P2y);
if (len < 0.00000001) { // if (len < 0.00000001) {
return undefined; // return {
} // x: P1x,
// y: P1y,
// };
// }
var u = (fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y); var u = (fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y);
u = u / (len * len); u = u / (len * len);
ix = P1x + u * (P2x - P1x); ix = P1x + u * (P2x - P1x);
@@ -340,7 +343,7 @@ export class Path extends Shape<PathConfig> {
rise = m * run; rise = m * run;
pt = { pt = {
x: ix + run, x: ix + run,
y: iy + rise y: iy + rise,
}; };
} }
@@ -365,7 +368,7 @@ export class Path extends Shape<PathConfig> {
return { return {
x: x, x: x,
y: y y: y,
}; };
} }
static getPointOnQuadraticBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y) { static getPointOnQuadraticBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
@@ -383,7 +386,7 @@ export class Path extends Shape<PathConfig> {
return { return {
x: x, x: x,
y: y y: y,
}; };
} }
static getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi) { static getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi) {
@@ -391,11 +394,11 @@ export class Path extends Shape<PathConfig> {
sinPsi = Math.sin(psi); sinPsi = Math.sin(psi);
var pt = { var pt = {
x: rx * Math.cos(theta), x: rx * Math.cos(theta),
y: ry * Math.sin(theta) y: ry * Math.sin(theta),
}; };
return { return {
x: cx + (pt.x * cosPsi - pt.y * sinPsi), x: cx + (pt.x * cosPsi - pt.y * sinPsi),
y: cy + (pt.x * sinPsi + pt.y * cosPsi) y: cy + (pt.x * sinPsi + pt.y * cosPsi),
}; };
} }
/* /*
@@ -456,7 +459,7 @@ export class Path extends Shape<PathConfig> {
's', 's',
'S', 'S',
'a', 'a',
'A' 'A',
]; ];
// convert white spaces to commas // convert white spaces to commas
cs = cs.replace(new RegExp(' ', 'g'), ','); cs = cs.replace(new RegExp(' ', 'g'), ',');
@@ -715,9 +718,9 @@ export class Path extends Shape<PathConfig> {
points: points, points: points,
start: { start: {
x: startX, x: startX,
y: startY y: startY,
}, },
pathLength: this.calcLength(startX, startY, cmd || c, points) pathLength: this.calcLength(startX, startY, cmd || c, points),
}); });
} }
@@ -726,7 +729,7 @@ export class Path extends Shape<PathConfig> {
command: 'z', command: 'z',
points: [], points: [],
start: undefined, start: undefined,
pathLength: 0 pathLength: 0,
}); });
} }
} }
@@ -906,13 +909,13 @@ export class Path extends Shape<PathConfig> {
var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp; var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp; var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
var vMag = function(v) { var vMag = function (v) {
return Math.sqrt(v[0] * v[0] + v[1] * v[1]); return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
}; };
var vRatio = function(u, v) { var vRatio = function (u, v) {
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v)); return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
}; };
var vAngle = function(u, v) { var vAngle = function (u, v) {
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v)); return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
}; };
var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]); var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);

View File

@@ -297,7 +297,7 @@ export class TextPath extends Shape<TextPathConfig> {
p1 = undefined; p1 = undefined;
while ( while (
Math.abs(glyphWidth - currLen) / glyphWidth > 0.01 && Math.abs(glyphWidth - currLen) / glyphWidth > 0.01 &&
attempts < 50 attempts < 5
) { ) {
attempts++; attempts++;
var cumulativePathLength = currLen; var cumulativePathLength = currLen;
@@ -386,7 +386,12 @@ export class TextPath extends Shape<TextPathConfig> {
} else if (glyphWidth > currLen) { } else if (glyphWidth > currLen) {
currentT += (glyphWidth - currLen) / pathCmd.pathLength; currentT += (glyphWidth - currLen) / pathCmd.pathLength;
} else { } else {
currentT -= (currLen - glyphWidth) / pathCmd.pathLength; currentT = currentT - (currLen - glyphWidth) / pathCmd.pathLength;
// that one is a weird check
// but I have to add it to fix some drawings (they are in the testing)
if (currentT < 0) {
currentT += 0.02;
}
} }
if (currentT > 1.0) { if (currentT > 1.0) {

View File

@@ -712,29 +712,84 @@ suite('TextPath', function () {
test('check bad calculations', function () { test('check bad calculations', function () {
var stage = addStage(); var stage = addStage();
stage.draggable(true);
var layer = new Konva.Layer(); var layer = new Konva.Layer();
stage.add(layer); stage.add(layer);
var textpath = new Konva.TextPath({ var textpath = new Konva.TextPath({
x: 100,
y: 150,
fill: '#333', fill: '#333',
fontSize: 16, fontSize: 16,
scaleX: 0.8,
scaleY: 0.8,
text: text:
'__________________________________________________________________________________________________________________________________________________________________________________________________________________', '__________________________________________________________________________________________________________________________________________________________________________________________________________________',
data: data:
'M 109.98618090452261 138.6656132223618 C 135.94577638190955 48.80547503140701 149.91187876884422 79.79800957914573 151.40954773869348 117.23973382537689 S 123.00811620603017 419.616741991206 122.84170854271358 460.0538041771357 S 134.33883542713568 469.8304329459799 149.98115577889448 464.33898005653265 S 245.4620163316583 411.5856081972362 257.1105527638191 412.91686950376885 S 239.31850251256282 474.434854428392 249.96859296482413 475.76611573492465 S 338.21036306532665 425.67526648869347 348.5276381909548 424.3440051821608 S 337.3640408291457 461.1772344535176 338.5288944723618 464.33898005653265 S 346.8778454773869 466.79295744346734 358.52638190954775 451.4834524183417', 'M 109.98618090452261 138.6656132223618 C 135.94577638190955 48.80547503140701 149.91187876884422 79.79800957914573 151.40954773869348 117.23973382537689 S 123.00811620603017 419.616741991206 122.84170854271358 460.0538041771357 S 134.33883542713568 469.8304329459799 149.98115577889448 464.33898005653265 S 245.4620163316583 411.5856081972362 257.1105527638191 412.91686950376885 S 239.31850251256282 474.434854428392 249.96859296482413 475.76611573492465 S 338.21036306532665 425.67526648869347 348.5276381909548 424.3440051821608 S 337.3640408291457 461.1772344535176 338.5288944723618 464.33898005653265 S 346.8778454773869 466.79295744346734 358.52638190954775 451.4834524183417',
}); });
layer.add(textpath); layer.add(textpath);
var path = new Konva.Path({
stroke: 'red',
scaleX: 0.8,
scaleY: 0.8,
data:
'M 109.98618090452261 138.6656132223618 C 135.94577638190955 48.80547503140701 149.91187876884422 79.79800957914573 151.40954773869348 117.23973382537689 S 123.00811620603017 419.616741991206 122.84170854271358 460.0538041771357 S 134.33883542713568 469.8304329459799 149.98115577889448 464.33898005653265 S 245.4620163316583 411.5856081972362 257.1105527638191 412.91686950376885 S 239.31850251256282 474.434854428392 249.96859296482413 475.76611573492465 S 338.21036306532665 425.67526648869347 348.5276381909548 424.3440051821608 S 337.3640408291457 461.1772344535176 338.5288944723618 464.33898005653265 S 346.8778454773869 466.79295744346734 358.52638190954775 451.4834524183417',
});
layer.add(path);
layer.draw(); layer.draw();
var rect = textpath.getClientRect(); var rect = textpath.getClientRect();
assert.equal(rect.height, 412.4370496991363, 'check height'); assert.equal(rect.height, 332.3365704376247, 'check height');
textpath.text(''); textpath.text('');
rect = textpath.getClientRect(); rect = textpath.getClientRect();
assert.equal(rect.height, 0, 'check height'); assert.equal(rect.height, 0, 'check height');
}); });
test('check bad calculations 2', function () {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var textpath = new Konva.TextPath({
x: 0,
y: 0,
fill: '#333',
fontSize: 10,
// strokeWidth: 100,
stroke: 'black',
scaleX: 0.4,
scaleY: 0.4,
text:
'....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................',
data:
'M 117.12814070351759 108.66938206658291 C 79.18719346733668 277.73956799623113 75.85761180904522 379.96743797110554 82.84673366834171 395.7761659861809 S 148.83130025125627 280.47708118718595 177.12060301507537 244.36661824748745 S 326.1725898241206 61.02036887562815 325.67336683417085 85.815110709799 S 174.998726758794 435.7304316896985 172.8354271356784 457.1970202575377 S 273.65633103015074 310.01551271984926 307.1042713567839 270.07767352386935 S 466.09929459798997 92.08432302135678 459.9422110552764 114.3829499057789 S 266.23512060301505 435.5226006595478 254.2537688442211 461.4821961369347 S 328.1430565326633 368.1639210113065 357.09798994974875 337.2120956344221 S 486.31961118090453 207.61623570979899 502.79396984924625 195.8012916143216 S 511.48859170854274 200.85065719221106 498.50879396984925 235.79626648869348 S 379.73086055276383 489.4401119660804 391.37939698492465 495.76360317211055 S 573.2022663316583 313.03941849874377 598.4962311557789 290.0751609610553 S 608.3285672110553 288.6610529208543 608.4949748743719 298.64551271984925 S 604.9168530150754 352.64801334799 599.9246231155779 375.778678548995 S 540.6820665829146 508.5077162374372 565.643216080402 497.19199513190955 S 690.3761155778894 408.77881799623117 814.1834170854271 278.6480252826633',
});
var path = new Konva.Path({
x: 0,
y: 0,
stroke: 'red',
scaleX: 0.4,
scaleY: 0.4,
data:
'M 117.12814070351759 108.66938206658291 C 79.18719346733668 277.73956799623113 75.85761180904522 379.96743797110554 82.84673366834171 395.7761659861809 S 148.83130025125627 280.47708118718595 177.12060301507537 244.36661824748745 S 326.1725898241206 61.02036887562815 325.67336683417085 85.815110709799 S 174.998726758794 435.7304316896985 172.8354271356784 457.1970202575377 S 273.65633103015074 310.01551271984926 307.1042713567839 270.07767352386935 S 466.09929459798997 92.08432302135678 459.9422110552764 114.3829499057789 S 266.23512060301505 435.5226006595478 254.2537688442211 461.4821961369347 S 328.1430565326633 368.1639210113065 357.09798994974875 337.2120956344221 S 486.31961118090453 207.61623570979899 502.79396984924625 195.8012916143216 S 511.48859170854274 200.85065719221106 498.50879396984925 235.79626648869348 S 379.73086055276383 489.4401119660804 391.37939698492465 495.76360317211055 S 573.2022663316583 313.03941849874377 598.4962311557789 290.0751609610553 S 608.3285672110553 288.6610529208543 608.4949748743719 298.64551271984925 S 604.9168530150754 352.64801334799 599.9246231155779 375.778678548995 S 540.6820665829146 508.5077162374372 565.643216080402 497.19199513190955 S 690.3761155778894 408.77881799623117 814.1834170854271 278.6480252826633',
});
layer.add(path);
// emulate different size function:
// I found the app with custom font
// we calculations were not correct
// so I just coppied text size from that app
textpath._getTextSize = () => {
return { height: 10, width: 5.9399871826171875 };
};
layer.add(textpath);
layer.draw();
var rect = textpath.getClientRect();
assert.equal(rect.width, 298.50744485064837);
assert.equal(rect.height, 170.74755779649587);
});
}); });