0

Currently trying to instantiate some objects along a bezier curve. It's working, except for the fact that the meshes are not evenly distributed along the curve. So I need to do some arc-parameterization.

My information has come from this document WarpingTextToSplines so the code below is my interpretation of the pseudo-code.

First off, finding the (approx) length of the curve, and dividing it into sections:

private void buildCurveTables()
{
    int curveSegments = 100;
    lengthTable = new float[curveSegments + 1];

    Vector3 previousPoint = GetBezierPoint(0, curvePoints[0], curvePoints[1], curvePoints[2]);

    float sum = 0;
    lengthTable[0] = 0;

    for (int i = 1; i < lengthTable.Length; i++)
    {
        Vector3 currentPoint = GetBezierPoint(i / (float) lengthTable.Length, curvePoints[0], curvePoints[1], curvePoints[2]);
        sum += Vector3.Distance(previousPoint, currentPoint);
        lengthTable[i] = sum;
        previousPoint = currentPoint;
    }

    totalCurveLength = sum;
}

This stores 100 values of the lengths along the curve. These are used later to determine the correct position on the curve to put the mesh. The value is found in the table using this:

private float findPositionOnCurve(float u)
{
    float t; //Find t for the given u
    float targetArcLength = u * lengthTable[lengthTable.Length-1];

    //Debug.Log("u is: " + u);

    int index = Array.BinarySearch(lengthTable, targetArcLength);

    if (u >=1)
    {
        return 1;
    }

    if (index < 0)
    {
        index = ~index - 1;
        //No exact match found
        float lengthBefore = lengthTable[index];
        return (index + (targetArcLength - lengthBefore) / (lengthTable[index + 1] - lengthBefore)) / lengthTable.Length;
    }
    else
    {
        //Exact match found
        t = index / (float)lengthTable.Length - 1;
        //Debug.Log("Exact match, returning " + t);
        return t;
    }
}

I've tried a number of different quadratic bezier curve algorithms, but these are the ones that seem to be producing the correct results.

 private Vector3 GetBezierPoint(float t, Vector3 start, Vector3 control, Vector3 end)
{
    //float x = (((1 - t) * (1 - t)) * start.x) + (2 * t * (1 - t) * control.x) + ((t * t) * end.x);
    //float y = (((1 - t) * (1 - t)) * start.y) + (2 * t * (1 - t) * control.y) + ((t * t) * end.y);
    //float z = (((1 - t) * (1 - t)) * start.z) + (2 * t * (1 - t) * control.z) + ((t * t) * end.z);
    //return new Vector3(x, y, z);

    //http://answers.unity3d.com/questions/990171/curve-between-lerps.html
    float rt = 1 - t;
    return rt * rt * start + 2 * rt * t * control + t * t * end;
}

UPDATE

I'll also put up the code I've written to Instantiating the objects on the curve, in case I'm doing something dumb there..

//Only try to curve the objects once the control point has been put down.
 if (curvedMode && createdCurveControlHandle)
 {
 Vector3 startOfCurve = getCurveStartPoint().transform.position;

 curvePoints = new Vector3[3] { startOfCurve, currentCurveControlHandle.transform.position, currentMousePosition };

 buildCurveTables();

//Lerp value is generated through a loop that generates 'n' number of 
objects based on the distance between the start and current mouse position
 float mappedLerp = findPositionOnCurve(lerpValue); 

 Vector3 pointTest = GetBezierPoint(mappedLerp, curvePoints[0], curvePoints[1], curvePoints[2]);

 nextPlacementLocationTarget = pointTest;

 nextPosition = Vector3.Lerp(startOfCurve, nextPlacementLocationTarget,mappedLerp);}

Here are a few of the lerp lookups:

Lerp: 0.32 mappedLerp: 0.1659664
Lerp: 0.36 mappedLerp: 0.1896916
Lerp: 0.3999999 mappedLerp: 0.2143274
Lerp: 0.4399999 mappedLerp: 0.2399609
Lerp: 0.4799999 mappedLerp: 0.2667291
Lerp: 0.5199999 mappedLerp: 0.2948231
Lerp: 0.5599999 mappedLerp: 0.3244205

ADDITIONAL UPDATE

After some tweaking and help from SO user 'MBo' I've now got a fairly even distribution using a fixed length of ten objects. However they are still distributing more towards the start of the curve.

I've updated my code to use the table creation and binary search mentioned in the second answer on this SO question How-to-achieve-uniform-speed-of-movement-on-a-bezier-curve

I've been through and checked it so many times, I'm starting to go cross eyed. I'm not sure if my maths is off, so I'm misunderstanding something.

The majority of the meshes on the curve are all grouped at the start, then after a while they start to even out.

If anyone has any ideas or suggestions, I'd greatly appreciate it.

Thanks

Tony
  • 3,587
  • 8
  • 44
  • 77

1 Answers1

2

Your Bezier math and table building looks OK. But searching is bizarre.

At first - use kind of binary search algorithm that is intended for inexact search - it returns index in array/list, where we can insert item. Delphi implementation example:

alow := 0;
ahigh := A.Count - 1;
while ahigh - alow > 1 do begin
  j := (ahigh + alow) div 2;
  if Value <= A[j] then
    ahigh := j
  else
    alow := j;
end;

If you place objects along the curve in order (for example, chars of text), then linear search might be more effective - you always move forward, so next searching starts from the current index:

i = 0  // or current index
while (A[i] < Value) && (i < A.Length)
   i++

Note that distribution of t parameter along Bezier arc length might be quite uneven. Example shows two curves, both with t-step parameter 1/8. t=0.5 corresponds to 0.28 of full length in the second case.

enter image description here

And now even arc-length points in range 0.05..0.95 (relativelength = 0.05 + i / 10.0). I've used 2D cubic Bezier, but there is no principal difference.

enter image description here

MBo
  • 77,366
  • 5
  • 53
  • 86
  • Thanks for the info. I've just tried modifying the search using the code from this SO post; https://gamedev.stackexchange.com/questions/5373/moving-ships-between-two-planets-along-a-bezier-missing-some-equations-for-acce?noredirect=1&lq=1 However I'm still seeing a lot of clustering on the curve. – Tony Aug 04 '17 at 20:06
  • Have you checked `lengthTable` values for smoothness? – MBo Aug 04 '17 at 20:32
  • How does your Bezier look? Is control point heavily shifted to one end? Note that distribution of t-parameter along length differs drastically for these two curves: (0,0),(100,50),(200,0) and (0,0),(-5,50),(200,0) – MBo Aug 05 '17 at 03:27
  • I've tried with curves that look like both of your examples. Both have more points clustered towards the start of the curve. They even out at a distance away, but it's always the start of the curve where they are most clustered. – Tony Aug 05 '17 at 05:53
  • I don't see glaring mistakes "by eyes" (except for update sections that exploit some mapping?). Here `i / (float) lengthTable.Length`you should use `curveSegments` divider, but error is small. Is result weird for `(0,0),(100,0),(200,0)` too? – MBo Aug 05 '17 at 06:09
  • Well, I've just altered the code to `i / (float) curvePoints.Length` and now the distribution at the start of the is better at the start, and more clustered as the length increases. Drawing a curve in a straight line as you suggested is looking OK, but there is still far to many points at the start of the curve. – Tony Aug 05 '17 at 06:16
  • Could you create `lengthTable` for straight line, `curvePoints = 10`, and show it? If table is good, then error is hiding in your searching routine – MBo Aug 05 '17 at 06:25
  • At the moment, due to the way I've got things set up, I can only create three point curves. However, this screenshot shows a short curve, in a straight line, with 10 placed objects along it. https://puu.sh/x25Qe/cd27bd4794.png – Tony Aug 05 '17 at 06:42
  • Yes, it's flaw. I've added result of my quick implementation, looks normal. Have you looked at the table itself (before any searching)? – MBo Aug 05 '17 at 07:03
  • Hmmm, well now I'm not sure where to look. I've just tried using a fixed value of five objects, and they aren't evenly spacing. The spacing is a lot better, but it's still nowhere near evenly distributed. Thanks for all your help though, really do appreciate it! – Tony Aug 05 '17 at 07:12
  • `I'm not sure where to look`: Just print `lengthTable` contents – MBo Aug 05 '17 at 07:25
  • I've just created a new curve, and printed out the lengthTable, it's available here; https://pastebin.com/bAF7DjYW – Tony Aug 05 '17 at 07:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/151129/discussion-between-tony-and-mbo). – Tony Aug 05 '17 at 07:32