Home > Enterprise >  Get pivot cx / cy from rotation SVGMatrix
Get pivot cx / cy from rotation SVGMatrix

Time:07-28

Given is an SVG element which has a rotation transformation among others.

Transformations can be iterated in JS, finding the rotation transformation by its type SVGTransform.SVG_TRANSFORM_ROTATE. The angle can be extracted using SVGTransform.angle. Everything else is encoded in SVGTransform.matrix.

How to reconstruct the rotation center offset from the transform object?

console.clear();
let r = document.querySelector('svg rect');

let transforms = r.transform.baseVal;

for(let i=0; i<transforms.length; i  )
{
  let t=transforms[i];
  if(t.type==SVGTransform.SVG_TRANSFORM_TRANSLATE)
  {
    console.log('Found translate ' t.matrix.e ', ' t.matrix.f);
  }
  else if(t.type==SVGTransform.SVG_TRANSFORM_ROTATE)
  {
    console.log('Found rotation');
    console.log(' - angle is ' t.angle); //30
    console.log(' - cx is ' '???'); //50 wanted
    console.log(' - cy is ' '???'); //25 wanted
  }
}
<svg width="300" height="200">
  <rect style="fill: blue;" width="100" height="50" transform="translate(30 30) rotate(30 50 25)" />
</svg>

CodePudding user response:

After logging t it seems there is no straightforward way to get back cx and cy.

But we may calculate it from some equations. The center of rotation is the only point that maps to itself under a rotation (if the angle is not zero or equivalent).

If the center is located at x,y , it needs to satisfy the following equation involving matrix multiplication:

enter image description here

One can solve the two equations a*x c*y e=x and b*x d*y f=y. After some further calculations it's possible to find x and y:

console.clear();
let r = document.querySelector('svg rect');

let transforms = r.transform.baseVal;

for(let i=0; i<transforms.length; i  )
{
  let t=transforms[i];
  if(t.type==SVGTransform.SVG_TRANSFORM_TRANSLATE)
  {
    console.log('Found translate ' t.matrix.e ', ' t.matrix.f);
  }
  else if(t.type==SVGTransform.SVG_TRANSFORM_ROTATE)
  {
    console.log('Found rotation');
    let{a,b,c,d,e,f}= t.matrix ;
    let denominator =  a - a*d   b*c d-1
    let x = ((d-1)*e-c*f)/denominator
    let y = ((a-1)*f-b*e)/denominator
    console.log(t, 'angle' in t )
    console.log(' - angle is ' t.angle); //30
    console.log(' - cx is '  x); // 50.00000000000001
    console.log(' - cy is '  y); // 25.000000000000004 
  }
}
<svg width="300" height="200">
  <rect style="fill: blue;" width="100" height="50" transform="translate(30 30) rotate(30 50 25)" />
</svg>

As you can see, while the results are really close to the original, it can't exactly recover the input. But then you probably have no more than 4 decimal places in the input, you may want to use something like cx.toFixed(4).

Also, if the angle is 0 or other multiples of 360 degrees, the center cannot be recovered, as such rotation is just identity matrix, any center gives the same results. Depending on your use case, it might be better to save the data about cx, cy etc. somewhere and write to the rectangle based on that, rather than getting the transforms from the rectangle and trying to recover them.

  • Related