楕円上の均等に分散された点を計算するための単純なアルゴリズムは何ですか?


8

長軸と短軸を考慮して、楕円上に均等に分散した点をプロットする簡単なアルゴリズムを探しています。これは、次のような円を使用すると非常に簡単です。

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)));
}

(これにより、隣接するポイントから同じ距離のポイントが作成されます)が、楕円でこれを行う方法を見つけられないかわかりません。(AS3のソリューションが推奨されますが、必須ではありません。)


3
明確にするために、円周に対して点を等間隔にしたいですか?
tenpn

回答:


6

弧の長さでパラメータを再パラメータ化し、均一にサンプリングします。計算の手助けが必要な場合は、https//math.stackexchange.com/で質問します。

ここでも質問されました:https : //mathoverflow.net/questions/28070/finding-n-points-that-are-equidistant-around-the-circumference-of-an-ellipse


これは思ったより複雑です。つまり、基本的には、このquitordie.com/goodEllipse.gif(このスレッドbigresource.com/Tracker/Track-flash-DO1WzX6KNqから取得)を実行しようとしています。これを正しく理解していれば、アークの長さでポイントを等距離にしようとしているわけではありません
ジャスティンC.ラウンド

アークの長さで等間隔にポイントをプロットしたいと思います。楕円の円周を表す公式はありません。mathoverflowページには、このページen.wikipedia.org/wiki/Circumferenceへのリンクがあり、使用できる周長の概算を示しています。
ジョナサンフィショフ2010

本当の教訓は、数学を一般的に単純にしたい場合は、多項式または有理関数を使用することです。楕円は単純に見えますが、複雑なプロパティがあるため、計算が難しいです。多項式は非常によく変形し、近似曲線(楕円など)は非常に適切です。
ジョナサンフィショフ2010

6

合理的な近似

他の回答ですでに述べたように、これを行う正確な方法はありません。ただし、解を効率的に近似することは可能です。

私の数式は右上の象限のみを処理します。他の象限を処理するには、さまざまな符号の変更を適用する必要があります。

してみましょうdは連続する点間のご希望のアーク距離とすること。最後にプロットされた点が(x、y)にあるとします。

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

次に、次の点を次の座標にプロットします。

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

証明

次の点を(x +Δx、y +Δy)とします。両方の点が楕円方程式を満たします。

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

方程式からyを取り除くと、次のようになります。

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

我々は仮定Δxが、我々は交換するので、十分に小さいですfは(X +Δxだけ)-f(x)を持つF '(x)は、Δxが使用して線形近似のためにF'を

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

dが十分に小さい場合、ΔxΔyは十分に小さく、弧の長さは点間のユークリッド距離に近くなります。したがって、次の近似が有効です。

Δx² + Δy² ~ d²

上記のΔyを置き換え、Δxを解きます。

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

dが十分に小さくない場合はどうなりますか?

上記の近似を有効にするにはdが大きすぎる場合は、単にdd / Nに置き換えます。たとえば、N = 3とし、Nから1つの点のみをプロットします。

最後のメモ

この方法には極値(x = 0またはy = 0)で問題があり、追加の近似を使用して処理できます(つまり、実際にプロットされているかどうかに関係なく、象限の最後のポイントをスキップします)。

極座標を使用して全体をやり直すことにより、楕円全体の処理がおそらくより堅牢になります。しかし、それはいくつかの作業であり、これは古い質問なので、元のポスターから何らかの関心がある場合にのみ、それを行います:-)


1
アスキーアートに自動賛成します。
ジミー

0

私は、あなたが「均等に」とはどういう意味かによって異なります。私のゲームでの楕円の使用に関する投稿をここに書きました:http : //world-create.blogspot.com/2009/01/ellipse-maths.html

投稿から:

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

次のようにすることで、角度によって楕円の周りに等間隔のポイントを取得できます。

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

ただし、楕円の詳細によっては、これらの値がまとまってしまう可能性があります(楕円の「尖った」端がある場合)。


0

答えは、完全なJavaコードで、StackOverflowにあります。

回答者:

作成13 12月。1313 12:14ジョンポール

作成11 12月。132013-12-11 03: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;
}

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.