Skip to content

Commit

Permalink
feat(fabric.Path) Simplify S and T command in C and Q. (fabricjs#6507)
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur authored and shanicerae committed Jan 16, 2021
1 parent f2e3dd6 commit ec2d14c
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 159 deletions.
167 changes: 13 additions & 154 deletions src/shapes/path.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
initialize: function(path, options) {
options = options || { };
this.callSuper('initialize', options);

if (!path) {
path = [];
}
Expand All @@ -59,19 +58,14 @@

this.path = fromArray
? fabric.util.makePathSimpler(path)
// one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
: path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);

: fabric.util.makePathSimpler(
fabric.util.parsePath(path)
);

if (!this.path) {
return;
}

if (!fromArray) {
this.path = fabric.util.makePathSimpler(
fabric.util.parsePath(this.path)
);
}

fabric.Polyline.prototype._setPositionDimensions.call(this, options);
},

Expand All @@ -81,15 +75,12 @@
*/
_renderPathCommands: function(ctx) {
var current, // current instruction
previous = null,
subpathStartX = 0,
subpathStartY = 0,
x = 0, // current x
y = 0, // current y
controlX = 0, // current control point x
controlY = 0, // current control point y
tempX,
tempY,
l = -this.pathOffset.x,
t = -this.pathOffset.y;

Expand Down Expand Up @@ -130,89 +121,26 @@
);
break;

case 'S': // shorthand cubic bezierCurveTo, absolute
tempX = current[3];
tempY = current[4];
if (previous[0].match(/[CcSs]/) === null) {
// If there is no previous command or if the previous command was not a C, c, S, or s,
// the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
ctx.bezierCurveTo(
controlX + l,
controlY + t,
current[1] + l,
current[2] + t,
tempX + l,
tempY + t
);
x = tempX;
y = tempY;

// set control point to 2nd one of this command
// "... the first control point is assumed to be
// the reflection of the second control point on
// the previous command relative to the current point."
controlX = current[1];
controlY = current[2];

break;

case 'Q': // quadraticCurveTo, absolute
tempX = current[3];
tempY = current[4];

ctx.quadraticCurveTo(
current[1] + l,
current[2] + t,
tempX + l,
tempY + t
current[3] + l,
current[4] + t
);
x = tempX;
y = tempY;
x = current[3];
y = current[4];
controlX = current[1];
controlY = current[2];
break;

case 'T':
tempX = current[1];
tempY = current[2];

if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control point
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
ctx.quadraticCurveTo(
controlX + l,
controlY + t,
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
break;

case 'z':
case 'Z':
x = subpathStartX;
y = subpathStartY;
ctx.closePath();
break;
}
previous = current;
}
},

Expand Down Expand Up @@ -321,15 +249,10 @@
var aX = [],
aY = [],
current, // current instruction
previous = null,
subpathStartX = 0,
subpathStartY = 0,
x = 0, // current x
y = 0, // current y
controlX = 0, // current control point x
controlY = 0, // current control point y
tempX,
tempY,
bounds;

for (var i = 0, len = this.path.length; i < len; ++i) {
Expand All @@ -353,101 +276,37 @@
break;

case 'C': // bezierCurveTo, absolute
controlX = current[3];
controlY = current[4];
bounds = fabric.util.getBoundsOfCurve(x, y,
current[1],
current[2],
controlX,
controlY,
current[3],
current[4],
current[5],
current[6]
);
x = current[5];
y = current[6];
break;

case 'S': // shorthand cubic bezierCurveTo, absolute
tempX = current[3];
tempY = current[4];
if (previous[0].match(/[CcSs]/) === null) {
// If there is no previous command or if the previous command was not a C, c, S, or s,
// the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
case 'Q': // quadraticCurveTo, absolute
bounds = fabric.util.getBoundsOfCurve(x, y,
controlX,
controlY,
current[1],
current[2],
tempX,
tempY
);
x = tempX;
y = tempY;
// set control point to 2nd one of this command
// "... the first control point is assumed to be
// the reflection of the second control point on
// the previous command relative to the current point."
controlX = current[1];
controlY = current[2];
break;

case 'Q': // quadraticCurveTo, absolute
controlX = current[1];
controlY = current[2];
bounds = fabric.util.getBoundsOfCurve(x, y,
controlX,
controlY,
controlX,
controlY,
current[1],
current[2],
current[3],
current[4]
);
x = current[3];
y = current[4];
break;

case 'T':
tempX = current[1];
tempY = current[2];

if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control point
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
bounds = fabric.util.getBoundsOfCurve(x, y,
controlX,
controlY,
controlX,
controlY,
tempX,
tempY
);
x = tempX;
y = tempY;
break;

case 'z':
case 'Z':
x = subpathStartX;
y = subpathStartY;
break;
}
previous = current;
bounds.forEach(function (point) {
aX.push(point.x);
aY.push(point.y);
Expand Down
60 changes: 56 additions & 4 deletions src/util/path.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,9 @@
// m or M command. When a z or Z command is drawn, x and y need to be resetted to
// the last x1 and y1.
x1 = 0, y1 = 0, current, i, converted,
destinationPath = [];
// previous will host the letter of the previous command, to handle S and T.
// controlX and controlY will host the previous reflected control point
destinationPath = [], previous, controlX, controlY;
for (i = 0; i < len; ++i) {
converted = false;
current = path[i].slice(0);
Expand Down Expand Up @@ -291,6 +293,8 @@
current[6] += y;
// falls through
case 'C':
controlX = current[3];
controlY = current[4];
x = current[5];
y = current[6];
break;
Expand All @@ -302,8 +306,31 @@
current[4] += y;
// falls through
case 'S':
// would be sScC but since we are swapping sSc for C, we check just that.
if (previous === 'C') {
// calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
else {
// If there is no previous command or if the previous command was not a C, c, S, or s,
// the control point is coincident with the current point
controlX = x;
controlY = y;
}
x = current[3];
y = current[4];
current[0] = 'C';
current[5] = current[3];
current[6] = current[4];
current[3] = current[1];
current[4] = current[2];
current[1] = controlX;
current[2] = controlY;
// current[3] and current[4] are NOW the second control point.
// we keep it for the next reflection.
controlX = current[3];
controlY = current[4];
break;
case 'q': // quadraticCurveTo, relative
current[0] = 'Q';
Expand All @@ -313,6 +340,8 @@
current[4] += y;
// falls through
case 'Q':
controlX = current[1];
controlY = current[2];
x = current[3];
y = current[4];
break;
Expand All @@ -322,8 +351,24 @@
current[2] += y;
// falls through
case 'T':
if (previous === 'Q') {
// calculate reflection of previous control point
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
else {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
controlX = x;
controlY = y;
}
current[0] = 'Q';
x = current[1];
y = current[2];
current[1] = controlX;
current[2] = controlY;
current[3] = x;
current[4] = y;
break;
case 'a':
current[0] = 'A';
Expand All @@ -346,6 +391,7 @@
if (!converted) {
destinationPath.push(current);
}
previous = current[0];
}
return destinationPath;
};
Expand All @@ -357,10 +403,16 @@
parsed,
re = fabric.rePathCommand,
match,
coordsStr;
coordsStr,
// one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
path;
if (!pathString || !pathString.match) {
return result;
}
path = pathString.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);

for (var i = 0, coordsParsed, len = pathString.length; i < len; i++) {
currentPath = pathString[i];
for (var i = 0, coordsParsed, len = path.length; i < len; i++) {
currentPath = path[i];

coordsStr = currentPath.slice(1).trim();
coords.length = 0;
Expand Down
Loading

0 comments on commit ec2d14c

Please sign in to comment.