Support addPath properly.

However this is not correctly handling the transform. To do it correctly we need to store the sub-paths in the path in their original form (not a string) and apply the transforms at render time.
This commit is contained in:
k1w1 2024-04-18 13:26:13 -07:00
parent cc6973fa1c
commit a891b0ad58
3 changed files with 114 additions and 52 deletions

View File

@ -92,30 +92,30 @@ export default (function () {
// entity mapping courtesy of tinymce // entity mapping courtesy of tinymce
namedEntities = createNamedToNumberedLookup( namedEntities = createNamedToNumberedLookup(
"50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy," + "50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy," +
"5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute," + "5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute," +
"5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34," + "5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34," +
"5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil," + "5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil," +
"68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde," + "68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde," +
"6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute," + "6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute," +
"6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml," + "6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml," +
"75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc," + "75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc," +
"7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash," + "7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash," +
"7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta," + "7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta," +
"sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu," + "sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu," +
"st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi," + "st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi," +
"t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota," + "t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota," +
"tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau," + "tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau," +
"u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip," + "u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip," +
"81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym," + "81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym," +
"8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr," + "8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr," +
"8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod," + "8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod," +
"8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup," + "8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup," +
"8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4," + "8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4," +
"nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob," + "nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob," +
"rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0," + "rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0," +
"Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm," + "Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm," +
"80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger," + "80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger," +
"811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro", "811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",
32 32
); );
@ -355,9 +355,9 @@ export default (function () {
} }
var element = this.__document.createElementNS( var element = this.__document.createElementNS(
"http://www.w3.org/2000/svg", "http://www.w3.org/2000/svg",
elementName elementName
), ),
keys = Object.keys(properties), keys = Object.keys(properties),
i, i,
key; key;
@ -775,12 +775,26 @@ export default (function () {
path = this.__currentDefaultPath; path = this.__currentDefaultPath;
} }
var pathElement = this.__createPathElement(); if (path.__pathString.length > 0) {
this.__applyStyleToElement(pathElement, action); var pathElement = this.__createPathElement();
pathElement.setAttribute("paint-order", "fill stroke markers"); this.__applyStyleToElement(pathElement, action);
pathElement.setAttribute("d", path.__pathString); pathElement.setAttribute("paint-order", "fill stroke markers");
pathElement.setAttribute("d", path.__pathString);
if (path2d) {
this.__applyTransformation(pathElement);
}
}
if (path2d) { if (path2d) {
this.__applyTransformation(pathElement); path2d.__subPaths.forEach((subPath) => {
var pathElement = this.__createPathElement();
this.__applyStyleToElement(pathElement, action);
pathElement.setAttribute("paint-order", "fill stroke markers");
pathElement.setAttribute("d", subPath.path.__pathString);
if (subPath.transform) {
this.__applyTransformation(pathElement, this.getTransform().multiply(subPath.transform));
}
});
} }
}; };
@ -1160,9 +1174,9 @@ export default (function () {
*/ */
Context.prototype.createPattern = function (image, repetition) { Context.prototype.createPattern = function (image, repetition) {
var pattern = this.__document.createElementNS( var pattern = this.__document.createElementNS(
"http://www.w3.org/2000/svg", "http://www.w3.org/2000/svg",
"pattern" "pattern"
), ),
id = randomString(this.__ids), id = randomString(this.__ids),
img; img;
pattern.setAttribute("id", id); pattern.setAttribute("id", id);
@ -1351,10 +1365,10 @@ export default (function () {
/** /**
* Not yet implemented * Not yet implemented
*/ */
Context.prototype.drawFocusRing = function () {}; Context.prototype.drawFocusRing = function () { };
Context.prototype.createImageData = function () {}; Context.prototype.createImageData = function () { };
Context.prototype.putImageData = function () {}; Context.prototype.putImageData = function () { };
Context.prototype.globalCompositeOperation = function () {}; Context.prototype.globalCompositeOperation = function () { };
return Context; return Context;
})(); })();

View File

@ -22,6 +22,7 @@ export default (function () {
} }
this.ctx = ctx; this.ctx = ctx;
this.__subPaths = []; // Array of path string/transform pairs.
this.__currentPosition = { x: undefined, y: undefined }; this.__currentPosition = { x: undefined, y: undefined };
}; };
@ -29,18 +30,19 @@ export default (function () {
return this.ctx.__matrixTransform(x, y); return this.ctx.__matrixTransform(x, y);
}; };
Path2D.prototype.addPath = function (path, transform) { Path2D.prototype.addPath = function (path, transform = undefined) {
if (transform) this.__subPaths.push({ path: path, transform: transform });
console.error("transform argument to addPath is not supported");
this.__pathString = this.__pathString + " " + path;
}; };
Path2D.prototype.appendPath = function (path) {
this.__pathString = this.__pathString + " " + path;
}
/** /**
* Closes the current path * Closes the current path
*/ */
Path2D.prototype.closePath = function () { Path2D.prototype.closePath = function () {
this.addPath("Z"); this.appendPath("Z");
}; };
/** /**
@ -50,7 +52,7 @@ export default (function () {
Path2D.prototype.moveTo = function (x, y) { Path2D.prototype.moveTo = function (x, y) {
// creates a new subpath with the given point // creates a new subpath with the given point
this.__currentPosition = { x: x, y: y }; this.__currentPosition = { x: x, y: y };
this.addPath( this.appendPath(
format("M {x} {y}", { format("M {x} {y}", {
x: this.__matrixTransform(x, y).x, x: this.__matrixTransform(x, y).x,
y: this.__matrixTransform(x, y).y, y: this.__matrixTransform(x, y).y,
@ -64,14 +66,14 @@ export default (function () {
Path2D.prototype.lineTo = function (x, y) { Path2D.prototype.lineTo = function (x, y) {
this.__currentPosition = { x: x, y: y }; this.__currentPosition = { x: x, y: y };
if (this.__pathString.indexOf("M") > -1) { if (this.__pathString.indexOf("M") > -1) {
this.addPath( this.appendPath(
format("L {x} {y}", { format("L {x} {y}", {
x: this.__matrixTransform(x, y).x, x: this.__matrixTransform(x, y).x,
y: this.__matrixTransform(x, y).y, y: this.__matrixTransform(x, y).y,
}) })
); );
} else { } else {
this.addPath( this.appendPath(
format("M {x} {y}", { format("M {x} {y}", {
x: this.__matrixTransform(x, y).x, x: this.__matrixTransform(x, y).x,
y: this.__matrixTransform(x, y).y, y: this.__matrixTransform(x, y).y,
@ -101,7 +103,7 @@ export default (function () {
*/ */
Path2D.prototype.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { Path2D.prototype.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
this.__currentPosition = { x: x, y: y }; this.__currentPosition = { x: x, y: y };
this.addPath( this.appendPath(
format("C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}", { format("C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}", {
cp1x: this.__matrixTransform(cp1x, cp1y).x, cp1x: this.__matrixTransform(cp1x, cp1y).x,
cp1y: this.__matrixTransform(cp1x, cp1y).y, cp1y: this.__matrixTransform(cp1x, cp1y).y,
@ -118,7 +120,7 @@ export default (function () {
*/ */
Path2D.prototype.quadraticCurveTo = function (cpx, cpy, x, y) { Path2D.prototype.quadraticCurveTo = function (cpx, cpy, x, y) {
this.__currentPosition = { x: x, y: y }; this.__currentPosition = { x: x, y: y };
this.addPath( this.appendPath(
format("Q {cpx} {cpy} {x} {y}", { format("Q {cpx} {cpy} {x} {y}", {
cpx: this.__matrixTransform(cpx, cpy).x, cpx: this.__matrixTransform(cpx, cpy).x,
cpy: this.__matrixTransform(cpx, cpy).y, cpy: this.__matrixTransform(cpx, cpy).y,
@ -180,7 +182,7 @@ export default (function () {
); );
this.lineTo(startX, startY); this.lineTo(startX, startY);
this.addPath( this.appendPath(
format( format(
"A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}", "A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}",
{ {
@ -370,7 +372,7 @@ export default (function () {
this.lineTo(startX, startY); this.lineTo(startX, startY);
this.ctx.__transformMatrix = currentTransform; this.ctx.__transformMatrix = currentTransform;
this.addPath( this.appendPath(
format( format(
"A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}", "A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}",
{ {

View File

@ -45,4 +45,50 @@ export default function path2D(ctx) {
ctx.fill(); ctx.fill();
ctx.restore(); ctx.restore();
ctx.restore(); ctx.restore();
// Scaling path versus scaling the context.
const path2 = makePath(
ctx,
`M -10 -10
L 10 -10
L 10 10
L -10 10
Z`
);
ctx.save();
ctx.translate(25, 100);
ctx.scale(2, 1);
ctx.strokeStyle = "red";
ctx.moveTo(-10, -10);
ctx.lineTo(10, -10);
ctx.lineTo(10, 10);
ctx.lineTo(-10, 10);
ctx.closePath();
ctx.fillStyle = "grey";
ctx.fill();
ctx.scale(1 / 2, 1); // Reset scale so that stroke is not scaled.
ctx.stroke();
ctx.restore();
ctx.save();
ctx.translate(100, 100);
ctx.scale(2, 1);
ctx.fillStyle = "grey";
ctx.fill(path2);
ctx.strokeStyle = "red";
let pNext = makePath(ctx);
// add first path, transform path, twice size, move 100,10
pNext.addPath(path2, new DOMMatrix([
2, 0,
0, 1,
0,
0,
]));
ctx.scale(1 / 2, 1); // Reset scale so that stroke is not scaled.
ctx.stroke(pNext);
ctx.restore();
} }