Mapping Bezier Curves from d3.js to Three.js

As part of incorporating three.js into a d3 viz, I wanted to be able to draw the nice smooth curves that d3 generates. It turns out that three.js has a CubicBezierCurve3 that matches nicely with the output of the d3.svg.diagonal function.

The short video below shows it in action - details are included further below.

Transitioning from 2D/svg in a D3 Viz to 3D in Three.js:
Mapping Bezier Curves
How to Map Bezier Curves from d3.js to Three.js

To map from the d3 2d world to the three.js world, we:

  • get the svg "d" attribute of the d3-generated path for the curve
  • from the "d" attribute, parse out the four points for the cubic Bezier function - here are two little (non-optimized!) helper functions to do that:
    function getPointsFromBezierD(d) {
        var firstSplit = d.split('C');
        //split off the 'M'
        var startPoint_all=firstSplit[0].replace('M','');
        var P0 = getXY(startPoint_all);
        var otherPoints = firstSplit[1].split(' ');
        var P1 = getXY(otherPoints[0]);
        var P2 = getXY(otherPoints[1]);
        var P3 = getXY(otherPoints[2]);
        return {P0:P0, P1:P1, P2:P2, P3:P3};
    }
    function getXY(s) {
        var pieces = s.split(',');
        var x = Number(pieces[0]);
        var y = Number(pieces[1]);
        return{x:x, y:y};
    }
  • use these four points to generate a THREE.CubicBezierCurve in the $z=0$ plane that is added to the scene:
    var material = new THREE.LineBasicMaterial( { color: whatever});
    var geometry = new THREE.Geometry();
    var thePoints = getPointsFromBezierD(d);
    var curve = new THREE.CubicBezierCurve3(
                       new THREE.Vector3(thePoints.P0.x, height-thePoints.P0.y,0),
                       new THREE.Vector3(thePoints.P1.x, height-thePoints.P1.y,0),
                       new THREE.Vector3(thePoints.P2.x, height-thePoints.P2.y,0),
                       new THREE.Vector3(thePoints.P3.x, height-thePoints.P3.y,0));
    geometry.vertices = curve.getPoints(20);
    var curveObject = new THREE.Line(geometry, material);
    scene.add(curveObject);
    
    Note that I am flipping the y values here - in general, the scaling could be more involved.
With anti-aliasing enabled, the result is much better looking than I expected for such an easy way to implement this.

Of course, you don't have to put the 3D curve in the $z=0$ plane - that's just what I'm doing here initially.

No comments:

Post a Comment

Popular Posts