Svg Coordinates with Transform Matrix

How can I use an SVG element’s transformation matrix to calculate destination coordinates?

You can use simple trigonometric transformation:

const rotatePoint = (point, center, rotateAngle) => {
const dx = point.x - center.x;
const dy = point.y - center.y;
const distance = Math.hypot(dx, dy);
const currentAngle = Math.atan(dy / dx);
const nextAngle = currentAngle - rotateAngle;
const nextDX = distance * Math.cos(nextAngle);
const nextDY = distance * Math.sin(nextAngle);
return {x: center.x + nextDX, y: center.y + nextDY};
};

The snippet displays rotation of a blue point around the red one (30 / 90 / 123 degrees counter-clockwise)

const rotatePoint = (point, center, angle) => {
const dx = point.x - center.x;
const dy = point.y - center.y;
const distance = Math.hypot(dx, dy);
const current = Math.atan(dy / dx);
const next = current - angle;
const nextDX = distance * Math.cos(next);
const nextDY = distance * Math.sin(next);
return {x: center.x + nextDX, y: center.y + nextDY};
};

const center = {x: 150, y: 150};
const start = {x: 200, y: 30};
const svg = d3.select('svg');

svg.append('circle')
.attr('cx', center.x)
.attr('cy', center.y)
.attr('r', 5)
.style('fill', 'red');
svg.append('circle')
.attr('cx', start.x)
.attr('cy', start.y)
.attr('r', 5)
.style('fill', 'blue');

// Rotate 30 deg
const p30 = rotatePoint(start, center, Math.PI * 30 / 180);
svg.append('circle')
.attr('cx', p30.x)
.attr('cy', p30.y)
.attr('r', 5)
.style('fill', 'green');
// Rotate 90 deg
const p90 = rotatePoint(start, center, Math.PI * 90 / 180);
svg.append('circle')
.attr('cx', p90.x)
.attr('cy', p90.y)
.attr('r', 5)
.style('fill', 'orange');
// Rotate 123 deg
const p123 = rotatePoint(start, center, Math.PI * 123 / 180);
svg.append('circle')
.attr('cx', p123.x)
.attr('cy', p123.y)
.attr('r', 5)
.style('fill', 'yellow');
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="250" height="200"></svg>

How to compute new coordinates after rotation?

I looked up some math.

It's a matrix-vector-multiplication. For SVG that means:

matrix(a b c d e f) corresponds to

x(new) = a*x + c*y + e

y(new) = b*x + d*y + f

How to calculate SVG transform matrix from rotate/translate/scale values?

Translate(tx, ty) can be written as the matrix:

1  0  tx
0 1 ty
0 0 1

Scale(sx, sy) can be written as the matrix:

sx  0  0
0 sy 0
0 0 1

Rotate(a) can be written as a matrix:

cos(a)  -sin(a)  0
sin(a) cos(a) 0
0 0 1

Rotate(a, cx, cy) is the combination of a translation by (-cx, cy), a rotation of a degrees and a translation back to (cx, cy), which gives:

cos(a)  -sin(a)  -cx × cos(a) + cy × sin(a) + cx
sin(a) cos(a) -cx × sin(a) - cy × cos(a) + cy
0 0 1

If you just multiply this with the translation matrix you get:

cos(a)  -sin(a)  -cx × cos(a) + cy × sin(a) + cx + tx
sin(a) cos(a) -cx × sin(a) - cy × cos(a) + cy + ty
0 0 1

Which corresponds to the SVG transform matrix:

(cos(a), sin(a), -sin(a), cos(a), -cx × cos(a) + cy × sin(a) + cx + tx, -cx × sin(a) - cy × cos(a) + cy + ty).

In your case that is: matrix(0.866, -0.5 0.5 0.866 8.84 58.35).

If you include the scale (sx, sy) transform, the matrix is:

(sx × cos(a), sy × sin(a), -sx × sin(a), sy × cos(a), (-cx × cos(a) + cy × sin(a) + cx) × sx + tx, (-cx × sin(a) - cy × cos(a) + cy) × sy + ty)

Note that this assumes you are doing the transformations in the order you wrote them.

How to convert svg element coordinates to screen coordinates?

I was playing around with this snippet below when I wanted to do the same (learn which screen coordinates correspond to the SVG coordinates). I think in short this is what you need:

  1. Learn current transformation matrix of the SVG element (which coordinates you are interested in), roughly: matrix = element.getCTM();

  2. Then get screen position by doing, roughly: position = point.matrixTransform(matrix), where "point" is a SVGPoint.

See the snippet below. I was playing with this by changing browser window size and was altering svg coordinates to match those of the div element

// main SVG:var rootSVG = document.getElementById("rootSVG");// SVG element (group with rectangle inside):var rect = document.getElementById("rect");// SVGPoint that we create to use transformation methods:var point = rootSVG.createSVGPoint();// declare vars we will use below:var matrix, position;// this method is called by rootSVG after load:function init() {  // first we learn current transform matrix (CTM) of the element' whose screen (not SVG) coordinates we want to learn:  matrix = rect.getCTM();  // then we "load" SVG coordinates in question into SVGPoint here:  point.x = 100;  // replace this with the x co-ordinate of the path segment  point.y = 300;  // replace this with the y co-ordinate of the path segment  // now position var will contain screen coordinates:  position = point.matrixTransform(matrix);  console.log(position)  // to validate that the coordinates are correct - take these x,y screen coordinates and apply to CSS #htmlRect to change left, top pixel position. You will see that the HTML div element will get placed into the top left corner of the current svg element position.}
html, body { margin: 0; padding: 0; border: 0; overflow:hidden; background-color: #fff; }svg {      position: fixed;    top:0%;    left:0%;    width:100%;    height:100%;    background:#fff;   }#htmlRect {  width: 10px;  height: 10px;  background: green;  position: fixed;  left: 44px;  top: 132px;}
<body>  <svg id="rootSVG" width="100%" height="100%" viewbox="0 0 480 800" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init()">
<g id="rect"> <rect id="rectangle" x="100" y="300" width="400" height="150"/> </g>
</svg> <div id="htmlRect"></div></body>

How to get the actual x/y position of an element in SVG with transformations and matrices

As well as i understood your problem, you need to know the x and y co-ordinates of an element, after it is transformed.

Mathematically, all transformations can be represented as 3x3 transformation matrices of the following form:

a   b   e
c d f
0 0 1

Since only six values are used in the above 3x3 matrix, a transformation matrix is also expressed as a vector: [a b c d e f].
a and d responsible for scaling in x and y respectively, whereas e and f gives you the translated axis in the x and y respectively.
So In your code which is

     <text transform="matrix(1,0,0,-1,236.532,417.253)" id="text6560">
<tspan x="0 4.448" y="0" id="tspan6562">10</tspan>
</text>

Element text is translated 236.532 in the x-axis 417.253 in the y-axis.
So tspan x point becomes 236.. + 4.4.. and y point 417.. + 0.



Related Topics



Leave a reply



Submit