TubeGeometry - path finding between points and directions

The past few evenings I’ve been trying to solve quite a complexing mathematical / pathfinding conundrum… I am trying to find a path and create a TubeGeometry between 2 points with 2 independent directions, lets say

{ positionA: Vector3(x,y,z)), directionA: (Vector3(x,y,z)) }
{ positionB: Vector3(x,y,z)), directionB: (Vector3(x,y,z)) }

the path should ideally never exceed a minimal / maximal bend angle ( 1.5 * the diameter of the resulting TubeGeometry radius ) and therein follow the least resistant / shortest path from pointA to pointB outside of this turn angle threshold…

so far I have been trying to apply some simple equations to the curve to go from pointA to pointB, to then maybe apply some further mathematical concepts later on, yet this has lead to outcomes with little to no useful results eg… adding the lerped directional vector between directionA and directionB to the lerped positional vector between positionA and positionB with a Math.sin coeff to generate a half sine between points, which generate a curve that does not reflect what i imagine to be a smooth curve with a minimal / maximal turning angle ( derived from the resulting TubeGeometry’s radius ) between points.

in the latest approach, i have been experimenting with calculating the cross product of the directional vector between positionA and positionB ( let’s call this “globalDirection”) and the lerped directional vector between directionA and directionB ( let’s call this “tangentialDirection” ), then using an Object3D() to traverse along the length between positions, using…

object3D.rotateOnAxis(globalDirection.cross(tangentialDirection), directionA.angleTo(directionB) / distance) 
object3D.translateOnAxis( new Vector3( 0, 0, 1 ), ( distance / 2 ) / ( (Math.PI * 2) - aTo ) )

however this yeilds some varied and unexpected results in practice…

image

I’ve prepared a minimal demo that reflects the state of the function from the images above…

the function in question where the curve is currently being calculated is generateSmoothCurve() on line 271 which pushes the iterative positions to the CatmullRomCurve3.points = []

I’m wondering, if anyone has any ideas on how this could be achieved, or if you’ve seen something similar implemented before, it’d be great to hear your thoughts on an approach to this that can be performative in three!

To Note:

the randomly distributed spherical positions / directions are just for testing random / extreme edge cases where the function should hold true.

I think I’ve seen some sort of tutorial for this type of thing using blender geometry nodes where procedural tubes can have physics applied and dragged from outlet to inlet like a soft body almost…

If i understand it correctly, you need to build a curve between two points where you have a derivative (direction) set at each point, and you need the curvature not to exceed a certain value anywhere along the curve.

You could use Bezier curves, for them there is a formula for curvature (and you can find its maximum).

You could start from quadratic curve, calculate max, if it exceeds your requirements, try cubic curve etc.

Also, higher degree curves can be represented as a sequence of lower degree curves, so at the end you might have a bunch of quadratic curves.

As a wild guess, I would build a quadratic curve, if it’s not good enough, add an extra point where the curvature is max, build two quadratic curves (start point, max) (max, end point) etc.

2 Likes

Although I have nothing to suggest at the moment, I’d like more details on the quoted paragraph, as I’m not sure I understand it (especially (1) the bending angle, (2) its relation to the diameter and (3) how to compare angles with distances). Could you illustrate this with some drawing? For example, one bad path, one good path and another good path with the same points/direction, but with different min/max bending angle.

1 Like

@Lawrence3DPK
I would give CubicBezierCurve3 a try;

1 Like

Hey, thanks for the quick responses, there’s a lot to go by that points the original question in the right direction!

I took a little break from the direction I was going in before and in recollection I think this may be the way to go, however I need to read more in-depth into both curve types, from what I’m gathering I may need to combine both @tfoller’s idea of having two curves that reach a max at both ends and then resolve the path therein organically, but also the link from @prisoner849 suggests the control points of CubicBezierCurve3 look like they would serve the correct path each end with 3 simple control points being the normalized direction * intended max bend radius iiuc ( however, plotting these points conditionally if the overall turn is above 90 degrees is still quite a blur for me ), here’s an image to illustrate the thought…

@PavelBoytchev, valid questions, I’ll try to explain and illustrate my terminology and line of thinking more thoroughly as I’ve also had these questions churning on the backburner…

  1. the bending angle:

    simplifying the “max bend angle” concept we can use a TaurusGeometry, by “max bend angle” I essentially mean what’s referred to in three’s TaurusGeometry as the property radius - Radius of the torus, from the center of the torus to the center of the tube. Default is 1 and in maya also radius, here’s an image to illustrate…

… …image

  1. its relation to the diameter:

    the relation to the diameter in this case would be TaurusGeometry’s property tube— Radius of the tube. Default is 0.4 and in maya Section Radius, here’s an image to illustrate…

whereby the “max bend angle” (raduis) never exceeds 1.5 * the “path” or "section radius"
… …image

the reason I’ve avoided using TaurusGeometry so far at both ends, calculating it’s position, direction and arc and then bridging the gap with a straight tube is that there would be no path to potentially edit the control points of…

  1. how to compare angles with distances:

    this is probably one of the more crucial points here, of course from start to end point there’s shortest path distance but this does not include any equation to determine a curved paths length, there could however be conditions put in place to divide the straight line distance into segments of functionality, my current line of thinking is somewhat inclusive of a simple approach to this calculation… If there’s a start point, start direction and direction from start to end then there’s the possibility of using a nested Object3D() inside a parent that sits 1.5 * “max bend angle” along the direction from start to end point with it’s spherical phi & theta (0,0) sitting at the start point, this way the curve can be interpreted spherically from both start and end towards each other until a “straight” or “organic” path can be achieved… here’s a further image to illustrate this thought…

I may wait to hear the feedback of the masters before progressing but the previous attempts were a complex fail, I think both @tfoller & @prisoner849’s approach sounds too simple too be true but will likely work a charm, however, I’m also inclined to experiment with Object3D#.setFromSpherical(r, t, p) as it makes a little more logical sense in how (“absolute”?) point plotting would work…

PS: i guess as an overall rounded vision of the idea it’s quite a bit how this stuff behaves irl ( without the physics… just yet :hot_face:)…

Update:

just came across a great resource for plotting cubic curves, which sparked another idea… the ideal curve would likely be an Open-Uniform B-Spline, the points of which could be plotted from an initial, simpler set of editable “Cubic Bézier arc” control points…

I did somewhat similar calculations for Bezier curves, trying to minimize the distance between the polygon representation of the curve and an ideal curve. You can change the precision, set initially to 0.33, to a smaller (0.05) or bigger (3) number and press “Update maxDist” button to see how it works:

https://alikim.com/qcode/?three%20:%20adaptive%20qbc

maybe you will find this useful as well.

2 Likes