Incorporation of Three.js 3D into a d3.js Viz - First Steps in Transitioning from 2D to 3D View

These are baby steps in the incorporation of webgl/three.js into an existing d3 visualization.

The visualization in question is one for viewing family trees. I also want to explore the addition of 3D viewing with this visualization. Part of this involves the transition from viewing the viz in 2d the svg world, to viewing things in the 3D with three.js.

The scenario is that the user is looking at things 2D, and then we provide a smooth transition to 3D.

I have lots of kinks to work out, but it looks like I've got the (very) basic transition to work, and thought others might be interested.

A very preliminary example showing an application of the technique is shown in the short youtube video below.

Baby steps in transitioning from 2D/svg in a D3 Viz to 3D in Three.js
(this is using the "spherical" approach discussed below,
which is why the tree is curved when rotated in 3D)
Two Approaches

There are two basic (and simple) approaches. One, referred to here as the "spherical" method, is slightly more involved and perhaps unnecessary. That is the one I did first because I wanted to see if it would even work. I then tried the simpler method and confirmed that it seemed to work as well, although when rotated in 3D the tree is (not unexpectedly) in a single plane.

Common Features of Both Approaches

We assume that the original (2D) visualization is in the $z=0$ plane, with enclosing width and height equal to that of the svg element.

We set up a THREE.PerspectiveCamera with the proper location and (vertical) field of view so that plane $z=0$ with width and height equal to the original width and height of the 2D div/svg element fills the viewing region (see figure below).

Determining field of view for camera based on its location
so that plane with d3 viz fills view

If $w$ and $h$ are the width and height of the svg element, then the camera is placed at $\langle w/2, h/2,Z\rangle$ for an arbitrary positive $Z$, with the camera "looking at" the point $\langle w/2, h/2,0\rangle$.

In order to get the field of view to match the plane of width $w$ and height $h$ in the $z=0$ plane, we need to set the field of view. I hadn't realized that this is the vertical field of view in three.js, but it is. The field of view $fov$ (in degrees) needed here for the three.js perspective camera is can be calculated by $$fov = (2 * Math.atan(h/(2*Z)))/(Math.PI/180)$$ and the three.js camera is then instantiated as $$camera = new \ THREE.PerspectiveCamera(fov, w/h, .1, 5000)$$ where the $near=0.1$ and $far=5000$ parameters may need to be tuned based on the size of the initial visualization.

"Spherical" Approach

In this case, I want all of the points being viewed to be the same 3D distance from the camera position, but when (initially) viewing the tree in 3D space, it looks the same as viewing the tree in 2D/svg. For one thing, this of course means that they are all located on a sphere centered at the camera position. I had (apparently incorrectly) assumed that were it otherwise, then the points would not appear at the same location initially.

Anyway.

Since the point $(w,h/2,0)$ is on this sphere, the radius of the sphere is the distance from this point to the camera location $(w/2,h/2,Z)$. This means that the radius is $R = \sqrt{(w^2/4) + Z^2)}$, and the equation for the sphere is $$(x - w/2)^2 + (y - h/2)^2 + (z-Z)^2 = R^2$$

The point $(x',y',0)$ will be mapped to a point on this sphere - this point is on the line from the camera position through the point $(x',y',0)$ in the "viewing rectangle". The parametric form for this line is $$\begin{align} F(t) &= \vec{v}_0 + t * (\vec{v}_1 - \vec{v}_0) \nonumber \\ &= \langle w/2, h/2, Z\rangle + t * ( \langle x', y',0\rangle - \langle w/2, h/2, Z\rangle ) \nonumber \end{align} $$

Plugging this into the equation for the sphere and solving for $t$ yields the nice looking $$t' = { R \over \sqrt{ (x' - w/2)^2 + (y' - h/2)^2 + Z^2 }}$$ (there are actually two roots, but we take the positive one since the point we seek is on the "negative Z" side of the original point). The final mapped point $\langle x,y,z\rangle$ is then given by plugging $t'$ into the equation for the line $(F(t)$.

Simpler "Flat" Approach

In this case, we simply map the points $\langle x', y'\rangle$ to the 3D point $\langle x', y',0\rangle$, and we're done.

As for the first method, this also looked the same as the 2D view when initially viewed. When rotated in 3D, the tree is (of course) flatly in a plane.

Why not just use an orthographic camera?

I am hoping to use the "split depth" technique to enhance 3D navigation/manipulation (I wrote about this recently here), and this seems far more effective with a perspective camera than an orthographic camera.

Still To Do

As indicated above, this note is reporting a very small step: there are many things to do in the near-term:

  • Add more animations as part of the transition itself
  • Use some sort of luminescence/glowing for the nodes, and look into why they have some rough edges
  • Use 3D Bezier curves/tubes between nodes (NOTE: Getting the 3D Bezier curves is described at http://www.nowherenearithaca.com/2015/08/mapping-bezier-curves-from-d3js-to.html)
  • Explore applications of the "split depth" effect to aid in navigation and focus when working with things in 3D

Popular Posts