New Path3D functions proposals

Hi all, here is some new functionality for Path3D I’ve been working on, please tell what you think about it or what else could be added, I will then make a PR.

  • New constructor/update parameter: alignTangentsWithPath (false would make it look like it does now by default):

  • New function: getPointAt (also for Curve3) this will get an interpolated point somewhere along the curve, from 0.0 to 1.0 (note: in Path2 it’s called getPointAtLengthPosition).

  • New functions: getTangentAt, getNormalAt, getBinormalAt, getDistanceAt. They take two parameters: a position from 0.0 to 1.0 like getPointAt, and an optional boolean (not for getDistanceAt) of wether to interpolate them as well. If chosen not to interpolate, the actual tangent/normal/binormal from the precomputed array is returned, not a clone of it.

  • New function: getPreviousPointIndexAt. This returns the array index of the previous point of an interpolated point along the curve.

  • New function: getSubPositionAt. This returns the position of an interpolated point relative to the two points it lies between, from 0.0 (point A) to 1.0 (point B).

  • New function: getPoints. This is the same as getCurve. Added to allow for more compatibility between Curve3 and Path3D (e.g. a function not having to check which one is provided).

  • New function: length. This just returns the last value of the distances array. This will also allow for more compatibility between Curve3 and Path3D.

Edit #1:

  • New function: slice. (also for Curve3) This returns new sub path/curve from a starting point to an end point (0.0 to 1.0). Just like slicing an array, both the start/end points are optional, and negative values wrap around from the end of the curve. If the starting position is greater than the end position, they get swapped instead of returning an inverted slice. (with arrays, an empty array would be returned)

Edit #2:

  • New function: getClosestPositionTo. This returns the position of the closest virtual point on the path to an arbitrary Vector3, from 0.0 to 1.0.


alignTangentsWithPath: true
interpolate: false

alignTangentsWithPath: true
interpolate: true



All good for me!

Pinging @jerome and @JohnK to get second thoughts

1 Like

I think it’s really a good idea and this would really useful as it has been requested before in some old forum posts.

1 Like

Good for me as well.

1 Like

Loving it @Gijs!

I’m a great user of Path3D and Curve3D for my game, maybe some day I’ll code a RailPath3D class :train2:

Anyway, some expert challenges I found so far:

  • Finding the closest spherical (radial) distance on Path3D to an arbitrary point (Vector3) e.g. my vehicle or train station is currently closest to 40% between Path point 3 and 60% from Path point 4.
  • Intersection of a plane and where on the path the collissions are (0 or more points/point interpolations). E.g. my vehicle needs to stop on the path before an imaginary plane (e.g. Start/Finish line).
  • Ability to set the last tangent/normal/binormal (e.g. by some arbitrary end point that is not to be included in the path itself). I now ‘fix’ this by plotting an extra end point in the direction I want the last Path point to be, and this extra end point differs very very little from the actual end point. But this technique is a bit tricky because it still increases the path length by a very very little distance (that can make a huge difference when trying to make solid circle paths for example).
  • Reversing the path, but remaining starting position and direction the same. (This I try to do for railway turnouts where some directions have to be taken in reverse heading. Joined by Curve3D.continue.)

I can elaborate a bit more in detail on this if you want.

The best ideas come from Holland :wink:


1 Like

The PR is live! :smiley:

It includes all functions listed above, except no additions to Curve3, so they remain nicely separated, with all data functionality being in Path3D.


It sure was a challenge, and also a great function, to get the closest position (0.0 to 1.0) to an arbitrary Vector3, which I’ve added as well. So then the closest radial distance would be:

  1. getClosestPositionTo the Vector3
  2. getPointAt the position
  3. Vector3.Distance between the Vector3 and the point on the path


I guess you could then also use getClosestPositionTo for the plane position.

As for the last point with different a different TNBs, can’t you just duplicate the last point and set custom TNBs afterward?

As for the reversing the path, could you elaborate on that? How are you doing it in GeekTrains (nice work by the way, it’s fun to play with, including the railway turnouts :slightly_smiling_face:)?


3D Math may become a challenge quite fast, indeed. Finding a solution for a 3D problem may therefore be a nice relief of hard brain work. :slight_smile:

I’ll have to test your new functions to be able to give real feedback on their workings. So now I just looked at the code.

Steps 1. and 2. will give me the point on the Path I am looking for (in “Path 1D space” coordinate) when input is a 3D world space position (e.g. from a Mesh). Step 3. is a test to check whether the function getClosestPositionTo really found the closest position. Excellent!

The math behind distance between a point and a line (and the points projected position on the line) is described in 5.1.1. of Eric Lengyel’s Mathematics for 3D Game Programming and Computer Graphics (3rd Edition). The story of the plane vs line is a bit different though, because there are infinite number of Vector3’s in a plane and the plane may be parallel with the Path3D point line yielding infinite solutions. Chapter 5.2.1. describes the mathematical solution of intersection of a line and a plane. Perhaps we have to combine some math from there with the getClosestPositionTo at the Path3D point nodes at i and i+1…

The ‘adjusted TNB’ at the last point I find a very good idea to get rid of the extra (fake) end-point for getting the desired TNB at end-point minus 1. That’s something for me to work out.

Yes I can elaborate on the ‘continue a reversed path’. The Curve3D.continue() continues at the end of a Path3D A from the start point of the Path3D B. So the (forward) axis of B at point 0 are aligned with the final axis of Path3D end point n. But now I have another case. I don’t want to continue A(n) with B(0), but ‘back off’ B(0) with A(n) such that A(n) continues B(0), not with A(0) as starting point of reference but B(0) as starting point & axis of reference. Effectively I want to “continue in reversed direction” hence my terminology.

See above example image. How to transform Path3D E (expressed as local starting position (0,0,0) and axis Forward) such that it’s end point and axis aligns with the already continued (transformed) target starting position of Path3D D (world transformed D(0) is same as node T and is same as world transformed C(n) for that matter)).

As for your question how I now do the math for it in GeekTrains. Well, I try to transform the points of E myself such that the end position becomes (0,0,0) and then reverse the points order. But that doesn’t work for all cases of incoming side tracks to a turnout. Therefore you now see in some levels strange angle (very unaligned) incoming train tracks…

If more clarification is needed or you want example we might schedule a phone call or hangouts meeting. :slight_smile:

Thanks for your heads-up for my game project. If you like it you can give your love as Facebook like or even create a (free) account so you’ll get updates whenever I completed a new level or fixed some code like in this case track laying. The documentation page also contains some info for developers to get inspiration for their own code projects.


1 Like

Ok, having the ability to continue (in either direction) a Path3D like a Curve3, except optionally following the current path transform makes a lot of sense, though it would leave only the static methods of Curve3 to be unique…

The Path3D segments could be turned into rays to make use of the many intersection tests of Ray.

How about just a


To then do something like

var rays = path3d.getRays();
var distances = path3d.getDistances();

for(var i=0; i<rays.length; i++){
    var ray = rays[i];
    var d = ray.intersectsPlane(myPlane);
    if(d !== null && d <= ray.length){
    	var point = ray.origin.add(ray.direction.scale(d));
    	var position = (distances[i] + ray.direction.scale(d).length()) / path3d.length();

Intersection playground:

1 Like

And here’s a back and continue functions demo:

Second parameter is whether to align the two paths