Closed
Bug 1064151
Opened 10 years ago
Closed 10 years ago
Nested SVG: getTransformToElement returns incorrect transformation matrix
Categories
(Core :: SVG, defect)
Tracking
()
VERIFIED
INVALID
People
(Reporter: simon.eu, Unassigned)
Details
Attachments
(8 files, 4 obsolete files)
User Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0 Build ID: 20140212131424 Steps to reproduce: Firefox' implementation of SVGLocatable.getTransformToElement() is incorrect when transforming to something else than the root SVG element.
Reporter | ||
Comment 1•10 years ago
|
||
Rendering in Firefox with incorrect circle location
Reporter | ||
Comment 2•10 years ago
|
||
Test case
Reporter | ||
Comment 3•10 years ago
|
||
Please refer to the test case for more details.
Reporter | ||
Comment 4•10 years ago
|
||
Short JavaScript test to test for the bug
Reporter | ||
Comment 5•10 years ago
|
||
Source code comment added, test fixed
Attachment #8485608 -
Attachment is obsolete: true
Comment 6•10 years ago
|
||
I'm afraid you've been led astray by Chrome. The "sub" element is at 100,100 in the co-ordinate system established by its parent. That needs to be added in if you want to transform to a child. It looks like you want a transform from a child of "sub".
Status: UNCONFIRMED → RESOLVED
Closed: 10 years ago
Resolution: --- → INVALID
Updated•10 years ago
|
Status: RESOLVED → VERIFIED
Reporter | ||
Comment 7•10 years ago
|
||
I think I disagree. Maybe I do not understand the specs correctly. But from my understanding, the second SVG element has its own coordinate system A, and the group element defines another coordinate system B. Now if I have coordinates in B and want to convert them to the coordinate system of A, I apply a transformation matrix from B to A. I agree that sub is at 100,100 relative to the root SVG. But note that I did not add the circles to the root element, but to the sub and its group child. The way it is at the moment in Firefox is that there is a sub SVG in a SVG. The sub SVG contains circles, which change their position *in sub's coordinate system* depending on where the sub SVG is located in the root SVG. In other words, the sub SVG looks different depending on where you put it. This is not related to Chrome. I did the test first and then wondered why the transformation did not work as expected. It was rather by accident that I took a look at other browsers.
Reporter | ||
Comment 8•10 years ago
|
||
Bug demo
Reporter | ||
Comment 9•10 years ago
|
||
I have added another test case. The JavaScript code adds four times *exactly the same* SVG element to the root SVG, but in Firefox they all look different.
![]() |
||
Comment 10•10 years ago
|
||
One issue here is that the SVG spec does not adequately define what an element's "user space" is, or in the case of getTransformToElement() specifically, what "user coordinate system on the current element" means. Take the <svg> element for example - there are four coordinate spaces of relevance for the following <svg>: <svg x="10" y="10" width="10" height="10" transform="translate(10,10)" viewBox="-10 -10 10 10"> 0. we start with the coordinate system that the <svg>'s parent establishes for its children 1. the {10,10} translation due to the 'transform' attribute is then applied to establishes a second coordinate system 2. next the {10,10} translation due to the 'x' and 'y' attributes is applied to establish a third coordinate system 3. final the {10,10} translation due to the 'viewBox' attribute is applied to create a fourth coordinate system (for the <svg>'s children) (I numbered from 0 because 1-3 are coordinate spaces created by properties of the element, whereas 0 is preexisting, and unaffected by anything the element does. In other words I'm trying to distinguish the fact that there are four coordinate spaces of interested for an element, but only three sources of coordinate space changes brought about by that element.) In computer graphics as a field, when you talk about the "user space"/"user coordinate system" for an object you are talking about the coordinate system that is "used" when the basic properties of the object that give the object its shape are used to place it into the scene. So for a <circle> it's the coordinate space that the "cx", "cy" and "r" attributes are relative to. Or for an <svg> it's the coordinate space that the "x", "y", "width" and "height" attributes are relative to. For that reason I consider an element's "user space" to be the coordinate space established _by_ #1 above. The spec. seems to back that up in the text describing getTransformToElement(), where it says "user coordinate system on the current element (after application of the ‘transform’ property)". So it's saying the method returns the transform from one element's user space to another element's user space, and it clarifies in the parenthetical that user space is indeed "after application of the ‘transform’ property" - so the space established by #1 above. So, in the case of your example: <svg id="sub" x="100" y="100"> <g id="subgroup" transform="translate(50 50) scale(2 2)"> the transform from "subgroup" to "sub" includes "sub"s #2 and #3 transforms and "subgroup"s #1 transforms ("sub"s #3 establishing "subgroup"s #0). More specifically, the transforms due to "sub"s 'x' and 'y' attributes ({100,100}) and 'viewBox' attribute (none), and "subgroup"s 'transform' attribute (translate(50 50) scale(2 2)). So the 'x' and 'y' attributes on the <svg> should be included in the result. Now, all this said, I understand that getting a transform to/from an <svg> element's user space is not very helpful. Since the <svg>'s "user space" is partway through the coordinate system changes that the <svg> element's attributes can make, this behavior is neither useful for positioning something as a child of the <svg>, nor as a child of the <svg>'s parent. So the question is why does the spec. say what it says? I would speculate that the reason is that the authors had in mind shapes like <circle> and the grouping element <g> and, without clarity on #0-3 above, it didn't occur to them that having getTransformToElement() get transforms from user space to user space gives poor results when one of the to/from elements is an <svg>. With only basic shapes and <g> in mind what they wanted was more likely the transform from "the coordinate system established by an element for its children" from one element to another. Note that in the case of basic shapes and <g> that happens to be exactly the same as "user space" to "user space" because #2 and #3 transforms don't exist for those types of elements. Perhaps the spec. text can be changed to use the phrase "the coordinate system established by an element for its children" rather than talking about user space, and then we can change to have the same behavior as Chrome. I'd need to think through the consequences of that a bit more though.
Reporter | ||
Comment 11•10 years ago
|
||
I agree with you that the specification is not very clear in this point. Do you have experience in changing it to your wording? Regarding the example, I'm not sure if I understand you correctly. Let me take a different approach: a) "sub"s user space origin is at 100,100 relative to the root SVG. b) "subgroup"s user space origin is at 150,150, again relative to the root SVG. Ignoring the scaling for now, the transformation matrix from b) to a) should then be a translation matrix doing exactly translate(50 50). From the recommendation: current transformation matrix (CTM) […] The current transformation matrix (CTM) defines the mapping from the user coordinate system into the viewport coordinate system. SVGMatrix getScreenCTM() Returns the transformation matrix from current user units (i.e., after application of the ‘transform’ attribute, if any) to the parent user agent's notice of a "pixel". For a circle, user units should again be the cx and cy coordinates. In the updated bug demo (next comment) I do exactly this transformation from screen coordinates to user units. The second demo (CTM) does the reverse, and I receive two different screen coordinates for two points with exactly the same screen coordinates.
Reporter | ||
Comment 12•10 years ago
|
||
Attachment #8485653 -
Attachment is obsolete: true
Reporter | ||
Comment 13•10 years ago
|
||
Result in Firefox: Point 1 is at (78,234) Point 2 is at (28,184)
Comment 14•10 years ago
|
||
(In reply to Simon A. Eugster from comment #11) > I agree with you that the specification is not very clear in this point. Do > you have experience in changing it to your wording? > > Regarding the example, I'm not sure if I understand you correctly. Let me > take a different approach: > a) "sub"s user space origin is at 100,100 relative to the root SVG. > b) "subgroup"s user space origin is at 150,150, again relative to the root > SVG. > > Ignoring the scaling for now, the transformation matrix from b) to a) should > then be a translation matrix doing exactly translate(50 50). > No, 150, 150 as sub's user space is the root user space. sub is at 100, 100 within that co-ordinate system though.
Reporter | ||
Comment 15•10 years ago
|
||
Sorry, I did not write what I meant. It should read: a) The origin of the user space created by "sub" b) […] by "subgroup"
Comment 16•10 years ago
|
||
Updated•10 years ago
|
Attachment #8487357 -
Attachment mime type: text/plain → image/svg+xml
Comment 17•10 years ago
|
||
Attachment #8487357 -
Attachment is obsolete: true
Updated•10 years ago
|
Attachment #8487528 -
Attachment mime type: text/plain → image/svg+xml
Comment 18•10 years ago
|
||
So in my testcase (which runs on Chrome and IE should you wish to try). a) Do you think that getScreenCTM and getTransformToElement should return the same values for r and inner? If not why not since they have the same attributes. r2 is then transformed by the x of inner. Is that clearer?
Reporter | ||
Comment 19•10 years ago
|
||
I believe I have found an answer in the SVG specs. 7.4 Coordinate system transformations A new user space (i.e., a new current coordinate system) can be established by specifying transformations in the form of a ‘transform’ attribute on a container element or graphics element or a ‘viewBox’ attribute on an ‘svg’, ‘symbol’, ‘marker’, ‘pattern’ and the ‘view’ element. The ‘transform’ and ‘viewBox’ attributes transform user space coordinates and lengths on sibling attributes on the given element […] and all of its descendants. user space == user coordinate system, 'transform' creates a new user coordinate system on container/graphics elements 7.6 The ‘transform’ attribute The ‘transform’ attribute is applied to an element before processing any other coordinate or length values supplied for that element. In the element <rect x="10" y="10" width="20" height="20" transform="scale(2)"/> the x, y, width and height values are processed after the current coordinate system has been scaled uniformly by a factor of 2 by the ‘transform’ attribute. Attributes x, y, width and height (and any other attributes or properties) are treated as values in the new user coordinate system, not the previous user coordinate system. 'x', 'y', 'width', and 'height' are applied after application of 'transform' 7.9 Establishing a new viewport At any point in an SVG drawing, you can establish a new viewport into which all contained graphics is drawn by including an ‘svg’ element inside SVG content. By establishing a new viewport, you also implicitly establish a new viewport coordinate system, a new user coordinate system, and, potentially, a new clipping path[…] The bounds of the new viewport are defined by the ‘x’, ‘y’, ‘width’ and ‘height’ attributes on the element establishing the new viewport, such as an ‘svg’ element. Both the new viewport coordinate system and the new user coordinate system have their origins at (‘x’, ‘y’), where ‘x’ and ‘y’ represent the value of the corresponding attributes on the element establishing the viewport. An 'svg' element nested in another 'svg' element creates a new user coordinate system which has its origins at ('x', 'y'). This clearly specifies that for your example <svg> <svg id="inner" x="1" y="0"> </svg> <rect id="r" x="1" y="0"/> </svg> "inner" has its origin at (1,0) and not at (0,0). 4.5.23 Interface SVGLocatable SVGMatrix getTransformToElement(in SVGElement element) Returns the transformation matrix from the user coordinate system on the current element (after application of the ‘transform’ attribute, if any) to the user coordinate system on parameter element (after application of its ‘transform’ attribute, if any). In the above example, the rect element does not define a new user coordinate system because there is no transform on it, so the parent’s is used. The SVG element defines the user coordinate system with its origin at (1,0). Conclusion is that Firefox’ implementation is not correct.
Comment 20•10 years ago
|
||
There's no transform on inner either.
Reporter | ||
Comment 21•10 years ago
|
||
Yes. First the transform is applied, which is nothing in this example, then the origin is moved to the coordinates specified by the 'x' and 'y' attributes, according to section 7.9.
Comment 22•10 years ago
|
||
Attributes x, y, width and height (and any other attributes or properties) are treated as values in the new user coordinate system, not the previous user coordinate system. Which we interpret as x, y are not establishing a new coordinate system they are values in the transform-established coordinate system. 7.9 applies to children so they are all in that co-ordinate system which is that of the children, not of the element itself. You'd be much better off taking all of this discussion to w3c (http://lists.w3.org/Archives/Public/www-svg/)
Comment 23•10 years ago
|
||
(In reply to Simon A. Eugster from comment #21) > Yes. First the transform is applied, which is nothing in this example, then > the origin is moved to the coordinates specified by the 'x' and 'y' > attributes, according to section 7.9. The origin for children, not for the element itself.
Reporter | ||
Comment 24•10 years ago
|
||
Updated with matrix transform from circle to root SVG
Attachment #8487034 -
Attachment is obsolete: true
Reporter | ||
Comment 25•10 years ago
|
||
Reporter | ||
Comment 26•10 years ago
|
||
For those who want to follow, the thread on the SVG mailing list is here: http://lists.w3.org/Archives/Public/www-svg/2014Sep/0023.html In the meantime, could you please show me how I can get the third circle centered in Firefox using getTransformToElement() in Attachment #8489324 [details] (https://bugzilla.mozilla.org/attachment.cgi?id=8489324)? I cannot see how to do it. Thanks for your help!
Comment 27•10 years ago
|
||
Your problem is that you're trying to do things back to front. // Draw a circle in the svg "root" element with the same center as "target" var circRoot = circle(13); root.appendChild(circRoot); var matrixRoot = target.getTransformToElement(circRoot); var circRootTransform = pTarget.matrixTransform(matrixRoot); circRoot.setAttribute("cx", circRootTransform.x); circRoot.setAttribute("cy", circRootTransform.y); // Draw a circle in the svg "inner" element with the same center as "target" // in all browsers var circInner = circle(6); inner.appendChild(circInner); var matrixInner = target.getTransformToElement(circInner); var circInnerTransform = pTarget.matrixTransform(matrixInner); circInner.setAttribute("cx", circInnerTransform.x); circInner.setAttribute("cy", circInnerTransform.y);
Comment 28•10 years ago
|
||
Oh and remove the first argument out from the circle function and everything that uses that argument.
Reporter | ||
Comment 29•10 years ago
|
||
Thanks a lot, this works indeed!
Reporter | ||
Comment 30•9 years ago
|
||
Short update on this feature: The Chrome team decided to remove SVG.getTransformToElement() due to the unclear specification, and it is gone in version 48. https://www.chromestatus.com/feature/5736166087196672 https://lists.w3.org/Archives/Public/www-svg/2015Aug/att-0009/SVGWG-F2F-minutes-20150824.html#item02 I would suggest doing the same in Firefox due to the same reasons.
Reporter | ||
Comment 31•7 years ago
|
||
Just a short remark – getTransformToElement has now been removed from SVG2: https://www.w3.org/TR/SVG2/changes.html#types
You need to log in
before you can comment on or make changes to this bug.
Description
•