Cos'è un semplice algoritmo per calcolare punti distribuiti uniformemente su un'ellisse?


8

Sto cercando un semplice algoritmo per tracciare punti distribuiti uniformemente su un'ellisse, dati gli assi maggiore e minore. Questo è davvero facile da fare con un cerchio come questo:

var numberOfPoints = 8;
var angleIncrement = 360 / numberOfPoints;
var circleRadius = 100;
for (var i = 0; i < numberOfPoints; i++) {
    var p = new Point();
    p.x = (circleRadius * Math.cos((angleIncrement * i) * (Math.PI / 180)));
    p.y = (circleRadius * Math.sin((angleIncrement * i) * (Math.PI / 180)));
}

(che crea punti che si trovano alla stessa distanza dai punti vicini) ma non riesco a trovare o capire un modo per farlo su un'ellisse. (Soluzioni in AS3 preferite, ma non richieste.)


3
Per essere chiari, vuoi che i punti siano equidistanti tra la circonferenza?
tenpn

Risposte:


6

Parametrizzare nuovamente per lunghezza dell'arco e campionare uniformemente. Se hai bisogno di aiuto per fare la matematica, te lo chiederei qui:
https://math.stackexchange.com/

è stato anche chiesto qui: /mathpro/28070/finding-n-points-that-are-equidistant-around-the-circumference-of-an-ellipse


Caspita, questo è più complicato di quanto pensassi! Quindi sì, sostanzialmente sto provando a fare questo quitordie.com/goodEllipse.gif (tratto da questa discussione bigresource.com/Tracker/Track-flash-DO1WzX6KNq ). Se ho la comprensione correttamente questo, vuol dire che sto non cercando di fare i punti equidistanti dalla lunghezza d'arco.
Giustino C. Round

Penso che tu voglia tracciare i punti equidistanti dalla lunghezza dell'arco. Non esiste semplicemente una bella formula per la circonferenza di un'ellisse. Nella pagina mathoverflow è presente un collegamento a questa pagina en.wikipedia.org/wiki/Circumference che fornisce approssimazioni per la circonferenza che dovresti essere in grado di utilizzare.
Jonathan Fischoff,

La vera lezione che penso è che se vuoi che la matematica sia semplice in generale, usa i polinomi o le funzioni razionali. Le ellissi sembrano semplici, ma hanno proprietà complesse che le rendono difficili da calcolare. I polinomi sono molto ben curati e le curve approssimative (come le ellissi) molto bene.
Jonathan Fischoff,

6

Approssimazione ragionevole

Come già affermato in altre risposte, non esiste un modo esatto per farlo. Tuttavia, è possibile approssimare in modo efficiente una soluzione.

La mia formula gestirà solo il quadrante in alto a destra . Varie modifiche ai segni dovranno essere applicate per gestire altri quadranti.

Sia d la distanza dell'arco desiderata tra punti consecutivi. Supponiamo che l'ultimo punto tracciato sia in (x, y) .

  |
b +-------._  (x,y)
  |         `@-._
  |              `-.
  |                 `.
  |                   \
 -+--------------------+--->
 O|                    a

Quindi il punto successivo dovrebbe essere tracciato alle seguenti coordinate:

x' = x + d / sqrt(1 + b²x² / (a²(a²-x²)))
y' = b sqrt(1 - x'²/a²)

Prova

Lascia che il punto successivo sia su (x + Δx, y + Δy) . Entrambi i punti soddisfano l'equazione dell'ellisse:

x²/a² + y²/b² = 1
(xx)²/a² + (yy)²/b² = 1

Sbarazzarsi di y nelle equazioni dà:

Δy = b (sqrt(1 - (xx)²/a²) - sqrt(1 - x²/a²))

Supponiamo che Δx sia abbastanza piccolo, quindi sostituiamo f (x + Δx) -f (x) con f '(x) Δx usando l' approssimazione lineare per f' :

Δy = -bxΔx / (a² sqrt(1 - x²/a²))

Se d è abbastanza piccolo, allora Δx e Δy sono abbastanza piccoli e la lunghezza dell'arco è vicina alla distanza euclidea tra i punti. È pertanto valida la seguente approssimazione:

Δx² + Δy² ~ d²

Sostituiamo Δy in quanto sopra e risolviamo per Δx :

Δx ~ d / sqrt(1 + b²x² / (a²(a²-x²)))

E se d non fosse abbastanza piccolo?

Se d è troppo grande per le approssimazioni sopra siano validi, è sufficiente sostituire d con d / N , ad esempio N = 3 , e solo tracciare un punto su N .

Nota finale

Questo metodo presenta problemi a extrema ( x = 0 o y = 0 ), che possono essere trattati usando approssimazioni aggiuntive ( cioè saltando l'ultimo punto del quadrante, che sia effettivamente tracciato o meno).

Gestire l'intera ellisse sarà probabilmente più robusto rifacendo il tutto usando le coordinate polari. Tuttavia, è un po 'di lavoro, e questa è una vecchia domanda, quindi lo farò solo se c'è qualche interesse dal poster originale :-)


1
Ho auto-votato per l'arte ASCII.
Jimmy,

0

In un certo senso dipende esattamente cosa intendi per "uniformemente". Ho scritto un post sull'uso delle ellissi nel mio gioco qui: http://world-create.blogspot.com/2009/01/ellipse-maths.html

Dal post:

class Ellipse
{
  Vector3 m_centre;
  Vector3 m_up;
  Vector3 m_along;
  float m_h;
  float m_l;
};

Vector3 Ellipse::PointAt(float t)
{
  float c = cos(t);
  float s = sin(t);

  return m_h * c * m_up + m_l * s * m_along + m_centre;      
}

Puoi ottenere punti distribuiti uniformemente attorno all'ellisse in base all'angolo facendo:

PointAt(0.0f);
PointAt(kHalfPi);
PointAt(kPi);
PointAt(kHalfPi * 3.0f);
PointAt(kTwoPi);

Ma a seconda delle specifiche dell'ellisse questi valori potrebbero essere raggruppati (se c'è un'estremità "appuntita" dell'ellisse).


0

La risposta, con il codice Java completo, si trova su StackOverflow qui

Risposta di:

modificato l'11 dicembre 13 alle 4:14 John Paul

rispose l'11 dicembre 13 alle 3:48 Dave

package com.math;

public class CalculatePoints {

public static void main(String[] args) {
    // TODO Auto-generated method stub

    /*
     * 
    dp(t) = sqrt( (r1*sin(t))^2 + (r2*cos(t))^2)
    circ = sum(dp(t), t=0..2*Pi step 0.0001)

    n = 20

    nextPoint = 0
    run = 0.0
    for t=0..2*Pi step 0.0001
        if n*run/circ >= nextPoint then
            set point (r1*cos(t), r2*sin(t))
            nextPoint = nextPoint + 1
        next
        run = run + dp(t)
    next
 */


    double r1 = 20.0;
    double r2 = 10.0;

    double theta = 0.0;
    double twoPi = Math.PI*2.0;
    double deltaTheta = 0.0001;
    double numIntegrals = Math.round(twoPi/deltaTheta);
    double circ=0.0;
    double dpt=0.0;

    /* integrate over the elipse to get the circumference */
    for( int i=0; i < numIntegrals; i++ ) {
        theta += i*deltaTheta;
        dpt = computeDpt( r1, r2, theta);
        circ += dpt;
    }
    System.out.println( "circumference = " + circ );

    int n=20;
    int nextPoint = 0;
    double run = 0.0;
    theta = 0.0;

    for( int i=0; i < numIntegrals; i++ ) {
        theta += deltaTheta;
        double subIntegral = n*run/circ;
        if( (int) subIntegral >= nextPoint ) {
            double x = r1 * Math.cos(theta);
            double y = r2 * Math.sin(theta);
            System.out.println( "x=" + Math.round(x) + ", y=" + Math.round(y));
            nextPoint++;
        }
        run += computeDpt(r1, r2, theta);
    }
}

static double computeDpt( double r1, double r2, double theta ) {
    double dp=0.0;

    double dpt_sin = Math.pow(r1*Math.sin(theta), 2.0);
    double dpt_cos = Math.pow( r2*Math.cos(theta), 2.0);
    dp = Math.sqrt(dpt_sin + dpt_cos);

    return dp;
}

}
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.