Punti distribuiti uniformemente lungo una curva più bezier


10

Mi guardo intorno da un po 'e non riesco a trovare una soluzione a questo problema. Diciamo che ho una curva cubica più bezier (definita da 4 punti) e voglio ottenere una serie di punti che sono distribuiti uniformemente lungo la curva. Pensa di posizionare un testo lungo una curva per un esempio.

Ora il problema è che se inserisco t(valore di interpolazione da 0 a 1) con un incremento costante i punti non vengono distribuiti uniformemente. La distanza lungo la curva è minore quando la curva fa una curva e più lunga quando la curva è dritta.

Quindi, come posiziono i punti in modo uniforme lungo una curva più bezier?


4
Stai cercando una soluzione "puramente matematica" (o particolarmente efficiente)? Altrimenti, l'approccio diretto è: converti la curva in una polilinea, camminando lungo la curva, aumentando tdi, diciamo, 100 passi e misurando le distanze tra i punti risultanti. Quindi, interpolare lungo questa polilinea come desiderato.
Marco13,

Penso che tu stia cercando la parola chiave "parametrizzazione della lunghezza dell'arco", a cui è stata data risposta, ad esempio, in questa domanda .
Wondra,

Cosa ha detto @ Marco13!
David Van Brink,

Secondo le risposte / i commenti, l'approccio che ho citato non è solo semplice, ma anche piuttosto comune. È per una lingua particolare? Forse qualcuno avrebbe pubblicato alcune righe di codice quindi ...
Marco13

Risposte:


3

È più una domanda di matematica. Quindi, una curva di Bézier ha la seguente formula , sia nella xe ycomponente.

B_x(t) = (1-t)^3 * P0_x + (1-t)^2 * t * P1_x + (1-t) * t^2 * P2_x + t^3 * P3_x
B_y(t) = (1-t)^3 * P0_y + (1-t)^2 * t * P1_y + (1-t) * t^2 * P2_x + t^3 * P3_y

La lunghezza percorsa tlungo una curva gammaè data da:

length_gamma(t) = integration( sqrt( derivative(  gamma_x(s)  ) ^2 + derivative(  gamma_y(s)  ) ^2 ) )

Non esiste una soluzione scrivibile dall'uomo all'integrale, quindi è necessario approssimarsi.

Sostituisci gamma(t)con l'espressione B(t)per ottenere la lunghezza length_Bpercorsa tlungo il segmento più bezier. Diciamo che viaggia da 0a L.

Ora scegli i nvalori tra 0e Lche corrispondono ai punti equidistanti. Ad esempio, lunghezze del modulo k*L/nper kda 0a n.

Ora è necessario invertire la funzione length_B, in modo da poter calcolare la parte tposteriore dalla lunghezza l. È un bel po 'di matematica e io sono pigro da morire, prova a farlo da solo. Se non ci riesci, puoi andare allo scambio di stack matematici . Per una risposta più completa, puoi comunque andare lì.

Una volta che hai quella length_Bfunzione inversa (o un'approssimazione ragionevole), il tuo processo è abbastanza semplice.

  • Crea lunghezze l[k]della distanza del percorso data lontano dall'origine (P0_x,P1_x).
  • Calcola il loro t[k]utilizzo corrispondente length_B_inverse.
  • Positivo usando i punti (B_x(t[k]),B_y(t[k])).

1
Grazie! Risulta che la matematica che ti serve per integrare il polinomio di Bernstein è un incubo. Sono stato in grado di utilizzare questa soluzione
Foaly


0

Solo per espandere ciò che Marco ha detto, una tecnica comune per farlo è quella di percorrere la curva con incrementi molto più piccoli rispetto ai passi a lunghezza fissa che si desidera eseguire e memorizzare il punto di output risultante (e forse la distanza?) In una tabella.

Quindi, si passa attraverso la tabella e si scaricano tutte le voci ad eccezione di quei punti che sono più vicini ai multipli interi delle distanze che si desidera percorrere.

Quindi ti rimane una tabella che puoi indicizzare direttamente in fase di esecuzione molto rapidamente. Se vuoi andare nel punto che è 5 volte la dimensione della tua distanza, guardi nella tua tabella all'indice [5].

Si noti che è possibile eseguire i due passaggi in uno e non archiviare effettivamente gli elementi aggiuntivi nella tabella per iniziare, ma è più facile da visualizzare e comprendere in due passaggi.

Una volta ho visto una tecnica per calcolare effettivamente questo al volo senza precalcolare una tabella (non ha nemmeno usato iterazione / ricerca radice!), Ma sfortunatamente non ricordo affatto i dettagli):

Se me lo ricordo o lo trovo, posterò le informazioni!


1
questo potrebbe anche interessarti: math.stackexchange.com/questions/15896/…
Alan Wolfe

0

Passaggio 1: generare N + 1 punti interpolando la curva con incrementi di 1 / N. N dovrebbe essere abbastanza grande per buoni risultati visivi ma abbastanza piccolo per essere facilmente calcolato. Un valore di 50 dovrebbe essere OK per la maggior parte delle situazioni, ma dovrebbe essere ottimizzato per il tuo caso specifico. Chiamerò questo i "punti interpolati".

In alternativa è possibile generare un breve elenco di segmenti e interrompere in modo ricorsivo ogni segmento più lungo della lunghezza massima desiderata del segmento (inizialmente è necessario generare almeno quattro segmenti per tenere conto delle curve S in cui l'inizio è molto vicino alla fine).

Passaggio 2: "Cammina la linea" utilizzando i punti interpolati e la spaziatura desiderata tra ciascun punto.

Lascio qui il mio codice Unity:

public static Vector2[] InterpolateBezier(Vector2 start, Vector2 startControlPoint,
    Vector2 end, Vector2 endControlPoint, int segments)
{
    Vector2[] interpolated = new Vector2[segments + 1];
    interpolated[0] = start;
    interpolated[segments] = end;

    float step = 1f / segments;
    for (int i = 1; i < segments; i++)
    {
        interpolated[i] = GetBezierPosition(start, startControlPoint, end,
            endControlPoint, i * step);
    }

    return interpolated;
}

public static Vector2 GetBezierPosition(Vector2 start, Vector2 startControlPoint,
    Vector2 end, Vector2 endControlPoint, float t)
{
    float omt = 1f - t;
    float omt2 = omt * omt;
    float t2 = t * t;

    return
        start * (omt2 * omt) +
        startControlPoint * (3f * omt2 * t) +
        endControlPoint * (3f * omt * t2) +
        end * (t2 * t);
}

public static List<Vector2> WalkLine(Vector2[] points, float spacing, float offset = 0)
{
    List<Vector2> result = new List<Vector2>();

    spacing = spacing > 0.00001f ? spacing : 0.00001f;

    float distanceNeeded = offset;
    while (distanceNeeded < 0)
    {
        distanceNeeded += spacing;
    }

    Vector2 current = points[0];
    Vector2 next = points[1];
    int i = 1;
    int last = points.Length - 1;
    while (true)
    {
        Vector2 diff = next - current;
        float dist = diff.magnitude;

        if (dist >= distanceNeeded)
        {
            current += diff * (distanceNeeded / dist);
            result.Add(current);
            distanceNeeded = spacing;
        }
        else if (i != last)
        {
            distanceNeeded -= dist;
            current = next;
            next = points[++i];
        }
        else
        {
            break;
        }
    }

    return result;
}

-1

Ecco alcuni algoritmi che danno risultati abbastanza ok:

  Point Index(float pos) const
  {
    int count = p.NumPoints();
    Vector val(0.0,0.0,0.0);
    for(int i=0;i<count;i++)
      { 
        val += bin(pos,i,count-1)*Vector(p.Points(i));
      }
    return Point(val);
  }
  float bin(float pos, int i, int n) const
  { 
    return float(ni(n,i)) * pow(double(pos), double(i))*pow(double(1.0-pos), double(n-i));
  }
  int ni(int n, int i) const
  {
    if (i==0) { return 1; }
    if (n==i) { return 1; }
    return ni(n-1,i-1)+ni(n-1,i);
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.