El Developer
El Developer

Reputation: 3346

Custom THREE.Curve.create displays incorrectly

As suggested in this answer, I've created a linearly interpolated curve like this:

THREE.Linear3 = THREE.Curve.create(

  function ( points, label /* array of Vector3 */) {

    this.points = (points == undefined) ? [] : points;
    this.label = label;

  },

  function ( t ) {    
    var v = new THREE.Vector3();
    var c = [];
    var points = this.points, point, intPoint, weight;
    point = ( points.length - 1 ) * t;

    intPoint = Math.floor( point );
    weight = point - intPoint;

    c[ 1 ] = intPoint;
    c[ 2 ] = intPoint  > points.length - 2 ? points.length - 1 : intPoint + 1;

    var pt1 = points[ c[1] ],
      pt2 = points[ c[2] ];

    v.copy( pt1 ).lerp( pt2, weight );

    return v;

  }

);

However, when I'm trying to display a trajectory at different lengths (in an animated kinda-way) I get the following behavior i.e. instead of the curve going through the points, it kinda cuts through the space, note that in the example below each trajectory is supposed to go through the coordinates of each of the spheres (animated gif below):

Imgur

I am not sure I understand the getPoint function or what is it supposed to return. Any Help is greatly appreciated.

JSFiddle

This is a minimal example but you can see how the right corner has a jerky motion as the tube expands.

http://jsfiddle.net/ElDeveloper/3uyf3sq3/1/

Upvotes: 2

Views: 603

Answers (1)

Volune
Volune

Reputation: 4339

Cleaning some code

That helped me investigate the issue.

  • You are leaking geometries, you need to dispose the geometry after removing the mesh from the scene

    scene.remove(c_mesh)
    c_tube && c_tube.dispose();
    
  • Use WebGLRenderer. The CanvasRenderer leaks removed objects, and you're creating new objects on each frame. (If you're stuck with CanvasRenderer for some reason, sorry for you)

  • (For the fiddle) slow the motion, requestAnimationFrame isn't required for a test, setTimeout(animate, 500); allows the user to see what's happening.

  • What's the point of a 0-segment tube ?

    if (index >= points.length - 1){
        index = 1; //start with 2 points
    }
    

Works as expected

  • The TubeGeometry does a tube of N (second argument in constructor, 16 in fiddle) segments. (I'll come back to that later, but I don't think you always want 16 segments)

  • The default behaviour of Curve.getPoinAt (the method used by TubeGeometry and probably lots of other geometries) is to return equidistant points. You can expect: distance(getPointAt(0),getPointAt(0.1)) == distance(getPointAt(0.1),getPointAt(0.2)) to be true.

  • Because of these points, no matter how many points you put in your path, the TubeGeometry will build a 16-segments tube with all segment of the same length, going from the first to the last point of your path. There is little chance that one of the 15 intermediate points will be exactly at the position of an edge. That should explain what you see.

Trying to fix the stuff

  • First get rid of that equidistant way-to-be of the TubeGeometry+Path. Overloading getUtoTmapping should be enough (I found that reading the source):

    THREE.Linear3.prototype.getUtoTmapping = function(u) {
        return u;
    };
    
  • I changed your getPoint. It probably does the same thing, but I was more comfortable with my code the investigate

    function ( t ) {    
        var points = this.points;
        var index = ( points.length - 1 ) * t;
        var floorIndex = Math.floor(index);
        if(floorIndex == points.length-1)
            return points[floorIndex];
        var floorPoint = points[floorIndex];
        var ceilPoint = points[floorIndex+1];
        return floorPoint.clone().lerp(ceilPoint, index - floorIndex);
    }
    
  • Give the correct number of segments to the TubeGeometry constructor:

    var pathPoints = points.slice(0, index);
    c_path = new THREE.Linear3(pathPoints, 'Test');
    c_tube = new THREE.TubeGeometry(c_path, pathPoints.length-1, 10, 16, false, true);
    

    At this point, you should have roughly what you were expecting

  • You should see the tube always going through the edges. You should also see that the TubeGeometry isn't meant to have angles. You could improve this angle issue either by looking at how TubeGeometry and Curve handles tangents, or (if you don't care about slowness) by increasing the number of segments to a very large number:

    c_tube = new THREE.TubeGeometry(c_path, 200/*pathPoints.length-1*/, 10, 16, false, true);
    

That's all for the answer. You can find the last version of my experiments here: http://jsfiddle.net/dqn73m98/5/. You can also ask the three.js developers if such a feature exists, or request an implementation in a github issue (if someone as time to do it), here

Upvotes: 2

Related Questions