I seem to have a logical misconception, regarding the
or [1 0 0 1 tx ty], where tx and ty are the distances to translate coordinates in x and y, respectively.
Example
Following a small example providing two svg
. Each containing one path
which both get rotated by 15°
. I tried to mark the point p(x1,y1) by a lime circle
.
;window.onload = () => {
//REM: Query the first element in svg => [path, path]
document.querySelectorAll('svg > *[transform]').forEach((element) => {
let tSVG = element.ownerSVGElement;
let tBBox = element.getBBox();
let tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Draw a circle at p(x1,y1) - before rotation
let tOldX = tBBox.x * tMatrix.a tMatrix.e;
let tOldY = tBBox.y * tMatrix.d tMatrix.f
let tCircle1 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle1.setAttributeNS(null, 'r', '5');
tCircle1.setAttributeNS(null, 'fill', 'orange');
tCircle1.setAttributeNS(null, 'fill-opacity', '0.5');
tCircle1.setAttributeNS(null, 'cx', tOldX);
tCircle1.setAttributeNS(null, 'cy', tOldY);
tSVG.appendChild(tCircle1);
//REM: Rotate by 15° around center of itself
let tTransform = tSVG.createSVGTransform();
tTransform.setRotate(15, tBBox.x tBBox.width / 2, tBBox.y tBBox.height / 2);
element.transform.baseVal.appendItem(tTransform);
element.transform.baseVal.initialize(element.transform.baseVal.consolidate());
//REM: Fetch new matrix
tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Pure scale
let tScaleX = Math.sqrt(tMatrix.a * tMatrix.a tMatrix.b * tMatrix.b);
let tScaleY = Math.sqrt(tMatrix.c * tMatrix.c tMatrix.d * tMatrix.d);
//REM: Draw a circle at p(x1,y1) - after rotation
let tNewX = tBBox.x * tScaleX tMatrix.e;
let tNewY = tBBox.y * tScaleY tMatrix.f;
let tCircle2 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle2.setAttributeNS(null, 'r', '5');
tCircle2.setAttributeNS(null, 'fill', 'lime');
tCircle2.setAttributeNS(null, 'fill-opacity', '0.9');
tCircle2.setAttributeNS(null, 'cx', tNewX);
tCircle2.setAttributeNS(null, 'cy', tNewY);
tSVG.appendChild(tCircle2);
//REM: Connection
let tLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
tLine.setAttributeNS(null, 'stroke', 'green');
tLine.setAttributeNS(null, 'x1', tOldX);
tLine.setAttributeNS(null, 'y1', tOldY);
tLine.setAttributeNS(null, 'x2', tNewX);
tLine.setAttributeNS(null, 'y2', tNewY);
tSVG.appendChild(tLine);
console.log(
'e: ' tMatrix.e,
'f: ' tMatrix.f
)
})
}
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '950,-250,100,400' height='800' width='300'>
<path d = 'm1029.31 112.6h97.40v57.01h-97.40z' fill = 'red' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)' />
</svg>
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '-40,-40,150,100' height='200' width='300'>
<path d = 'M0 0L100 0L100 60L0 60z' fill = 'blue' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)'></path>
</svg>
Question
To my understanding, I should be able to get the point p(x1,y1) aka top-left, by using the following calculation:
- x1: (bbox.x * scale) matrix.e //original position x scaled object moved by x
- y1: (bbox.y * scale) matrix.f //original position y scaled object moved by y
Yet, it merely works in the second/blue svg and not the first/red one. The f of the first/red svg seems way off to me:
- e: 80.92766714917656 f: -261.78134648854723 //first/red svg
- e: 23.52145237337207 f: -25.726854765665173 //second/blue svg
Where am I going wrong with this?
Remark
While I added the Javascript tag for the syntax, the focus is not on Javascript. Javascript offers other possibilities to place the lime circle
correctly.
CodePudding user response:
I didn't look into why exactly it was failing. I have a couple of ideas. But if you apply the correct matrix multiplication formulas, it works correctly.
let tNewX = tBBox.x * tMatrix.a tBBox.y * tMatrix.c tMatrix.e;
let tNewY = tBBox.x * tMatrix.b tBBox.y * tMatrix.d tMatrix.f;
;window.onload = () => {
//REM: Query the first element in svg => [path, path]
document.querySelectorAll('svg > *[transform]').forEach((element) => {
let tSVG = element.ownerSVGElement;
let tBBox = element.getBBox();
let tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Draw a circle at p(x1,y1) - before rotation
let tOldX = tBBox.x * tMatrix.a tMatrix.e;
let tOldY = tBBox.y * tMatrix.d tMatrix.f
let tCircle1 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle1.setAttributeNS(null, 'r', '5');
tCircle1.setAttributeNS(null, 'fill', 'orange');
tCircle1.setAttributeNS(null, 'fill-opacity', '0.5');
tCircle1.setAttributeNS(null, 'cx', tOldX);
tCircle1.setAttributeNS(null, 'cy', tOldY);
tSVG.appendChild(tCircle1);
//REM: Rotate by 15° around center of itself
let tTransform = tSVG.createSVGTransform();
tTransform.setRotate(15, tBBox.x tBBox.width / 2, tBBox.y tBBox.height / 2);
element.transform.baseVal.appendItem(tTransform);
element.transform.baseVal.initialize(element.transform.baseVal.consolidate());
//REM: Fetch new matrix
tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Draw a circle at p(x1,y1) - after rotation
let tNewX = tBBox.x * tMatrix.a tBBox.y * tMatrix.c tMatrix.e;
let tNewY = tBBox.x * tMatrix.b tBBox.y * tMatrix.d tMatrix.f;
let tCircle2 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle2.setAttributeNS(null, 'r', '5');
tCircle2.setAttributeNS(null, 'fill', 'lime');
tCircle2.setAttributeNS(null, 'fill-opacity', '0.9');
tCircle2.setAttributeNS(null, 'cx', tNewX);
tCircle2.setAttributeNS(null, 'cy', tNewY);
tSVG.appendChild(tCircle2);
//REM: Connection
let tLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
tLine.setAttributeNS(null, 'stroke', 'green');
tLine.setAttributeNS(null, 'x1', tOldX);
tLine.setAttributeNS(null, 'y1', tOldY);
tLine.setAttributeNS(null, 'x2', tNewX);
tLine.setAttributeNS(null, 'y2', tNewY);
tSVG.appendChild(tLine);
console.log(
'e: ' tMatrix.e,
'f: ' tMatrix.f
)
})
}
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '950,-250,100,400' height='800' width='300'>
<path d = 'm1029.31 112.6h97.40v57.01h-97.40z' fill = 'red' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)' />
</svg>
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '-40,-40,150,100' height='200' width='300'>
<path d = 'M0 0L100 0L100 60L0 60z' fill = 'blue' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)'></path>
</svg>