Skip to content

Instantly share code, notes, and snippets.

@juandopazo
Created August 3, 2012 03:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save juandopazo/3244053 to your computer and use it in GitHub Desktop.
Save juandopazo/3244053 to your computer and use it in GitHub Desktop.
Geometric linear algebra for 3d CSS3

@mapagella came to me with a question. A friend of his was trying to do ray tracing with CSS3. He wanted to rotate in 3D a <div> element shaped like a line so that it started and ended in specific points in space.

Ray tracing gone wrong

It turns out that CSS3 defines a rotate3d() transform function that allows you to rotate an HTML element a certain angle around a certain direction in 3D space. The function looks like rotate3d(x, y, z, angle) where x, y and z define the direction around which the element will be rotated. The question then is what direction and which angle to use.

Let's start by defining what our line will be. We'll use a div element with a line class: <div class="line"></div>. And we'll define line as:

.line {
  background: green;
  height: 1px;
}

Afterwards we'll set the width of the line to the length of a vector. That means that the line as a vector will initially have the coordinates (length,0,0).

But before jumping to the math we must set one extra CSS rule. CSS transformations act on a "transform origin" because HTML elements are all boxes. Since we want this box to look and act like a line, we have to first tell CSS to set the origin at the left of the box:

.line {
  background: green;
  height: 1px;
  transform-origin: left center;
}

Now that our div behaves like a vector we can start figuring out how to rotate it. Let's say we want to position it so that the line starts at v1 = (x1,y1,z1) and ends at v2 = (x2,y2,z2).

The vector that sets the desired direction of the line will be dv = v2-v1 = (dx,dy,dz). We want to rotate the original line so that it matches this vector. That means that we want to rotate in the plane defined by the line and dv. This also means that the axis around which we'll be rotating the line is perpendicular to the plane. We can obtain the axis by calculating the cross product between the line and dv:

normal = line x dv
       = (length,0,0) x (dx,dy,dz)
       = (0,-length*dz,length*dy)

We already know that length is the norm of dv: length = sqrt(dx^2+dy^2+dz^2). So the only thing left is the angle between the line and dv. We can calculate it using a property of the dot product:

A . B = |A| |B| cos u    =>    u = acos( A . B / (|A| |B|) )

In this case |line| = |dv| = length. And so we calculate the dot product and the angle like this:

line . dv = (length,0,0) . (dx,dy,dz)
          = length*dx

=>  u = acos( length*dx / length^2 )
      = acos( dx / length )

We now know everything we need to know and we can write a little JavaScript function that will move the line to the corresponding points:

function ray(line, v1, v2) {
  var dx = v2[0] - v1[0],
      dy = v2[1] - v1[1],
      dz = v2[2] - v1[2],

  length = Math.sqrt( dx*dx + dy*dy + dz*dz ),
  angle  = Math.acos( dx / length );

  line.style.transform = 'translate3d(' + v1[0] + 'px,' + v1[1] + 'px,' + v1[2] + 'px) ' +
                         'rotate3d(0,' + (-length*dz) + ',' + (length*dy) + ',' + angle + 'rad)';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment