2012-02-17 29 views
7

私は、3次元空間の頂点の配列によって定義される凸多面体(面)の配列によって定義される閉じた凸多面体を持っています。均一な密度を仮定して、多面体の重心を見つけようとしています。現時点で私はこの擬似コードのアルゴリズムで計算します。凸多面体の重心

public Vector3 getCentroid() { 
    Vector3 centroid = (0, 0, 0); 
    for (face in faces) { 
     Vector3 point = face.centroid; 
     point.multiply(face.area()); 
     centroid.add(point); 
    } 
    centroid.divide(faces.size()); 
    return centroid; 
} 

これは基本的に、顔の重心の加重平均をとります。私は正しいアルゴリズムをオンラインで見つけることができなかったので、これが正しいことを100%確信しているわけではありません。誰かが私のアルゴリズムを確認したり、正しいものを私に紹介したりすることができたら、私はそれを感謝します。

ありがとうございました。


[EDIT]だからここ

は、私は、重心を見つけるために使用しています実際のJavaコードです。多面体を多面体内の任意の点に収束するピラミッドに分割する。ピラミッド重心の加重平均は、次の式に基づいています。

C 全て = SUM 全てピラミッド(C ピラミッド *ボリュームピラミッド)/体積ここ全て

は(重くコードコメント)である:

// Compute the average of the facial centroids. 
    // This gives an arbitrary point inside the polyhedron. 
    Vector3 avgPoint = new Vector3(0, 0, 0); 
    for (int i = 0; i < faces.size(); i++) { 
     avgPoint.add(faces.get(i).centroid); 
    } 
    avgPoint.divide(faces.size()); 

    // Initialise the centroid and the volume. 
    centroid = new Vector3(0, 0, 0); 
    volume = 0; 

    // Loop through each face. 
    for (int i = 0; i < faces.size(); i++) { 
     Face face = faces.get(i); 

     // Find a vector from avgPoint to the centroid of the face. 
     Vector3 avgToCentroid = face.centroid.clone(); 
     avgToCentroid.sub(avgPoint); 

     // Gives the unsigned minimum distance between the face and a parallel plane on avgPoint. 
     float distance = avgToCentroid.scalarProjection(face.getNormal()); 

     // Finds the volume of the pyramid using V = 1/3 * B * h 
     // where: B = area of the pyramid base. 
     //   h = pyramid height. 
     float pyramidVolume = face.getArea() * distance/3; 

     // Centroid of a pyramid is 1/4 of the height up from the base. 
     // Using 3/4 here because vector is travelling 'down' the pyramid. 
     avgToCentroid.multiply(0.75f); 
     avgToCentroid.add(avgPoint); 
     // avgToCentroid is now the centroid of the pyramid. 

     // Weight it by the volume of the pyramid. 
     avgToCentroid.multiply(pyramidVolume); 

     volume += pyramidVolume; 
    } 

    // Average the weighted sum of pyramid centroids. 
    centroid.divide(volume); 

お気軽にお問い合わせくださいあなたが見ているエラーを指摘してください。

+0

matlab codeを掲載しました。 – dmuir

+1

[この回答](http://stackoverflow.com/a/4824248/71059)の "[編集]" "の後のビットも同様の質問になります。 – AakashM

+0

あなたのコードでは、重心を初期化しましたが、ループ内でそれを使用したことはありません。あなたの数式によると、最後にすべてのボリュームの合計で除算します。すべてのavgToCentroid(centroid.add(avgToCentroid))の重心を集計すべきではありませんか?ボリュームと同じピラミッドボリュームの合計ですか? –

答えて

7

一般に、多面体の構造によって異なります。 4つの可能なケースがあります:

  • 頂点だけが体重を持っています。つまり、多面体は点のシステムです。 - その質量

    r_c = sum(r_i * m_i)/sum(m_i) 
    
    ここ

    r_iはi番目の頂点、m_iを表すベクトルである:次に、あなただけのすべてのポイントの加重和の平均値を計算することができます。 nは頂点の数である

    r_c = sum(r_i)/n 
    

    :等しい質量の場合は、単純な式で私たちを残します。両方の合計がベクトル化されていることに注意してください。

  • エッジのみに重みがあり、多面体は基本的に死体です。この場合は、各辺を辺の真ん中に位置する頂点で置き換え、辺全体の重み付けをすることで、前のものに減らすことができます。

  • 顔のみに体重があります。この場合も最初のものに減らすことができます。各面は2次元の凸形図形であり、重心が見つかる。それぞれの顔に重心を代入すると、この場合が最初のものになります。

  • 固体多面体(あなたの場合、から「均一密度と仮定すると」)。この問題には、より複雑なアプローチが必要です。最初のステップは、多面体を四面体に分割することです。これを行う方法はshort descriptionです。四面体の重心は、すべての中央値が交差する点に位置します。 (四面体の中央値は、その頂点と反対の面の重心を結ぶ線です。)次のステップは、パーティション内の各四面体を重心で置き換えることです。そして、最後のステップは、得られた重み付き点集合の重心を見つけることです。これはまさしく最初の場合です。固体の場合については

+0

qの中に「均一密度を仮定する」テキストが与えられていることが、最後のケースであることは間違いありません。 – AakashM

+0

@AakashM、あなたが正しいです、ちょうど答えをより完全にしたいです。あなたの発言を反映するためにそれを少し更新しました。 – Andrei

+0

はい、私が持っている場合は、固い多面体です。私はおそらくそれを明らかにしていたはずです。しかし、はい、私が取る方法は、あなたの4番目の点に似ていますが、まったく同じではありません。私は多面体をいくつかのピラミッドに分割しようとしています。私の質問に対するAakashMのコメントで説明されています。私は実際にこのアプローチを考えていましたが、代わりに顔の重心と顔の領域を使用し、計算をあまり行う必要がないと考えました。しかし、とにかく、私がそれをした後、問題はあなたの最初のケースに変わります。ご協力いただきありがとうございます。 – null0pointer

2

、(pitfallsを持っている)多面体をtetrahedralizeしようとするよりもはるかにsimpler methodがあります。ここで

は(まともするVector3実装を想定して)擬似っぽいのjava-っぽいコードです:

// running sum for total volume 
double vol = 0; 
// running sum for centroid 
Vector3 centroid = (0, 0, 0); 
for each triangle (a,b,c) 
{ 
    // Compute area-magnitude normal 
    Vector3 n = (b-a).cross(c-a); 
    vol += a.dot(n)/6.; 
    // Compute contribution to centroid integral for each dimension 
    for(int d = 0;d<3;d++) 
    centroid[d] += n[d] * ((a[d]+b[d])^2 + (b[d]+c[d])^2 + (c[d]+a[d])^2); 
} 
// final scale by inverse volume 
centroid *= 1./(24.*2.*vol); 

注意、あなたが三角形よりも高い程度の面を持っている場合は、自明ファンを三角測量することができ、これはまだ動作します。

多面体が凸でない場合でも便利です。

また、私はそれを保証することはできませんが、http://www.cs.berkeley.edu/~jfc/mirtich/massProps.htmlを見て価値があるかもしれない