1

There has been a lot of questions regarding the coördinate system of svg elements but no one has got me solving my problem.

Look at this fiddle: https://jsfiddle.net/archemedia/4f54jnm8/

In the startup function, I added connect("A", "B") which connects element A and B with the line. When I try to connect("A", "D") the line doesn't position correctly, due to the transform attribute of element D.

Could someone provide me with a clear solution? I don't want to use a svg library, I just want plain javascript code which solves the problem, preferably by adding it to the fiddle.

Many thanks

Dany Dhondt
  • 881
  • 9
  • 27

2 Answers2

2

Your getMid function needs to account for the transform matrix of the rectangles.

function getMid (rect, svg) {
  let point = svg.createSVGPoint();
  point.x = rect.x.baseVal.value + rect.width.baseVal.value / 2;
  point.y = rect.y.baseVal.value + rect.height.baseVal.value / 2;
  return point.matrixTransform(svg.getScreenCTM().inverse().multiply(rect.getScreenCTM()));
}

Note the new svg parameter. This is your SVGSVGElement. In the fiddle, you can grab it with document.getElementById('Laag_1').

Normally, I'd cite the relevant pages on MDN, but their SVG documentation is lacking. I had to piece this together from a couple of SO questions. The key was searching "svg get transform matrix" and following the rabbit hole from there.

AuxTaco
  • 4,883
  • 1
  • 12
  • 27
  • Indeed, when changing the percentual size of the svg, the line is at a different place. Any idea how to solve that? – Dany Dhondt Dec 27 '17 at 12:00
  • By brute-forcing every combination of `svg`, `rect`, `getCTM()`, `getScreenCTM()`, `inverse()`, and `multiply()`, yes! My brain's not... at its best, right now. – AuxTaco Dec 27 '17 at 12:08
  • I would recommend you avoid using `getScreenCTM`, as implementations are buggy and not consistent between browsers. Using `getCTM` should in this case be appropriate. – jcaron Dec 27 '17 at 12:17
  • @jcaron Testing in Firefox, `svg.getCTM()` is null, and `return point.matrixTransform(svg.getScreenCTM().inverse().multiply(rect.getCTM()));` yields incorrect results. `return point.matrixTransform(rect.getCTM());`, my original answer, yields incorrect results depending on the CSS size of the SVG. – AuxTaco Dec 27 '17 at 12:26
  • Replying from my phone so can’t really test, but you should need both matrices (the element’s and the SVG’s), and they should both come from the same function. Surprised getCTM returns null on the SVG element. A workaround may be to add a inside the SVG and use that? – jcaron Dec 27 '17 at 12:29
0

This is by far the most correct and useful answer and it might server the community. I've searched a lot and got multiple workarounds with regex and custom functions, all of which didn't work. My problem is finally solved. I updated the fiddle with the answer from AuxTaco and the getCTM() tweak suggested by jcaron. Thanks everyone!

function getMid(rect, svg){
        var point = svg.createSVGPoint();
        point.x = rect.x.baseVal.value + rect.width.baseVal.value / 2;
        point.y = rect.y.baseVal.value + rect.height.baseVal.value / 2;
        return point.matrixTransform(svg.getCTM().inverse().multiply(rect.getCTM()));
    }

https://jsfiddle.net/archemedia/4f54jnm8/2/

Dany Dhondt
  • 881
  • 9
  • 27