11

I'm combining 3D content with Three.js with HTML and SVG content. The Three.js CSSLoader does a pretty good job synchronizing placement of HTML and SVG content in the 3D world.

But the SVG/HTML coordinate systems are 'left-handed', whereas Three.js coordinate system is 'right-handed'. This basically means that their y-axes are reversed. In SVG/HTML, y/top goes up as you go down the screen, and Three.js uses the more standard mathematical convention of y going down as you go down the screen.

I have to continually convert from one to the other, which is pretty error prone. I know I am not the first to run into this (for example, look here). Has someone come up with a general solution? Here's what I tried:

  1. Do everything in an Object3D with .scale.y = -1. As you may suspect, this turns out to be a disaster. It turns everything inside-out, and don't even try to put your camera in there.

  2. Do everything in an Object3D with .rotate.x = Math.PI. This is more promising, but the z axis is no longer consistent with the HTML concept of z-index. Still, this is what I'm using now.

  3. In HTML, don't use top, use bottom. In SVG, do everything inside a <g transform="scale(1, -1)"> inside a <g transform="translate(0, imageHeight)">. However, I feel this would be more confusing for developers, and the imageHeight has to be kept up to date at all times, which is yet another burden.

Has anyone come up with something better? Perhaps a library to help with this?

Community
  • 1
  • 1
mhelvens
  • 4,225
  • 4
  • 31
  • 55
  • I think the solution is to create a world coordinate system for your SVG/HTML using THREE.js convention. So the center of the screen (called viewport) is 0,0,0. So objects in SVG/HTML get transformed by viewport.center(0,0,0) + viewport.halfWidth. – beiller Dec 16 '14 at 15:36
  • @beiller: I don't see how this solves the flipped y-axis problem, which my question is about. :-/ Am I missing something? – mhelvens Dec 16 '14 at 16:30
  • Maybe you are, or maybe its not workable from your situation. Eg: you have a CSS sprite at world coordinates (2,0,0). To draw the sprite, you would use the following formula: pos.x + viewportCenter.x + viewportHalfWidth.x - object.halfWidth.x. Which means assuming (0,0,0) viewport and 10x10 sprite and 800x600 dimensions, you draw x at 397px from the left of the window. – beiller Dec 16 '14 at 20:30
  • Similarly for Y axis, (2,0,0) is left: 397px; top: 295px; for the 10x10 sprite. – beiller Dec 16 '14 at 20:38
  • @beiller: Choosing `y = 0` is cheating. ;-) As the y axis of HTML space decreases, the y axis of 3D space increases. Anyway, sure: I can convert coordinates from one to the other in every place I need to specify coordinates, but that's exactly what I'm trying to avoid. I'd like to transform one of the coordinate systems to correspond 1-to-1 with the other, in a single location, so that the many modules of the app can assume there is only one system. This is not a math problem, but a JavaScript problem. – mhelvens Dec 16 '14 at 21:17
  • The method I lay out for converting from one space to another is the same method used in the vertex shader of opengl, by transforming each vertex by the "model view matrix" (old style opengl). You should tackle this conversion in a draw function and should only have to write it once. And sorry yes you are right. For y coordinate, negate the value for pos.y. – beiller Dec 17 '14 at 16:12

1 Answers1

1

I would suggest you to use the SVG Global Transform Attribute, if you post an example of your code, i could edit the answer and post the example here, maybe a JSfiddle.

Basically you will need to add the transformation to your SVG, in your case to change the direction of y-axis, you can do a "scale(1,-1)".

See the W3 documentation with examples in the following link: http://www.w3.org/TR/SVG/coords.html#SVGGlobalTransformAttribute

The first common use of this attribute:

Most ProjectedCRS have the north direction represented by positive values of the second axis and conversely SVG has a y-down coordinate system. That's why, in order to follow the usual way to represent a map with the north at its top, it is recommended for that kind of ProjectedCRS to use the ‘svg:transform’ global attribute with a 'scale(1, -1)' value as in the third example below.

They have some examples there too, I hope it solves your problem. :)

Jacobi
  • 1,508
  • 15
  • 29
  • 1
    Any text will be upside down. – Robert Longson Dec 17 '14 at 12:11
  • This is thing nr.3 I tried (see original question). It has a number of problems. And @RobertLongson is right: things would be upside down --- not only text, but every element I put inside. Still, I'm afraid this may be the lesser of evils. Perhaps if there is a way to auto-correct for this, i.e., locally scale back every child element? – mhelvens Dec 17 '14 at 19:11
  • Hi, yes in fact every thing will be upside down, I thought in your number 3 you didn't tried the Global one. I looked over the internet too, but didn't found any other way to do what you want, unfortunately I think that there isn't an easy solution for now, sorry, and good look. – Jacobi Dec 19 '14 at 11:50