« StudioMOMO「C.O.F.F.E.E.でバッチレンダリング」… | トップページ | C4DのC.O.F.F.E.E.のPointLineDistance()関数を使って、ポリゴンの面積を求めてみる »

C4DのC.O.F.F.E.E.で「ヘロンの公式」を使って、ポリゴンの面積を求めてみる

おいおい、ポリゴンの面積なんて計算する必要があるのかい?
「最近のあんたは、的外れなことをしてないかい?」そう言われてしまいそうだが…

ま、このブログは最初から、的外れだから問題ないだろう…

では、面積を求めます。

「ヘロンの公式」
三角形の三辺abcの長さから面積を求める公式です。

Heron_01

ただし s は三辺の長さの総和の1/2

Heron_02

これを使ってみよう。
数学が苦手でも、3辺の長ささえわかれば、特別な計算をしなくても面積が求めることができます。

3点を渡すと面積を返す関数にしてみよう。

面積は英語で? … area

面積はエリアなのか…Sが付く単語かと思ったら…

//三角形p0,p1,p2の面積
GetArea(p0 , p1 , p2)
{
  var s;  //面積
  var half;  //三辺の総和の1/2
  var a , b , c;  //各三辺の長さ

  a = vlen(p1 - p0);
  b = vlen(p2 - p1);
  c = vlen(p0 - p2);
  half = (a + b + c) / 2.0;
  s = sqrt(half * (half - a) * (half - b) * (half - c));

  return s;
}

ベクトルの長さを求める vlen()
平方根を求める sqrt()

これは三角ポリゴンのみの対応だね…
四角ポリゴンは?
三角形に分割して2回実行する?
それとも四角ポリゴン用の関数を作る?

関数に渡す前に三角ポリゴンなのか、四角ポリゴンなのか判断して…
三角ポリゴンなら(abc)…
四角ポリゴンなら2個の三角形に分割して、(p0 , p1 , p2)と(p2 , p3 , p0)2回実行

Heron_03_2

最初から、四角ポリゴンを対象にして、三角ポリゴンでも4点を渡し…
関数内部で三角形に分割して、2個の三角形の面積を求めて足した物を返せば良いのだ。
三角ポリゴンもデータ的に4点で構成されているわけだ。

Heron_04_2

この三角ポリゴンのデータを三角形に分割すると、最初の3点の三角形は面積はあるけど…
後の三角形は面積が無いわけだから、足しても最初の三角形の面積にしかならないわけだ…
そうすると、三角ポリゴンかどうか判断しなくても、全部四角ポリゴンとして渡して機械的に処理しても、問題なく面積が返ってくるわけだ…

//ポリゴンの面積
GetArea(p0 , p1 , p2 , p3)
{
  var s;
  var half_A;
  var half_B;
  var a , b , c , d , e;

  a = vlen(p1 - p0);
  b = vlen(p2 - p1);
  c = vlen(p3 - p2);
  d = vlen(p0 - p3);
  e = vlen(p2 - p0);

  half_A = (a + b + e) / 2.0;
  half_B = (c + d + e) / 2.0;
  s  = sqrt(half_A * (half_A - a) * (half_A - b) * (half_A - e));
  s += sqrt(half_B * (half_B - c) * (half_B - d) * (half_B - e));

  return s;
}

こんな怠惰な発想で良いのだろうか?
良い訳がない。と思いながら作業を進める。

実際にポリゴンオブジェクトの面積を計算してみる。

ポリゴンオブジェクトの、全てのポリゴンの面積の総和をコンソールに表示します。

/*
ポリゴンオブジェクトの面積を求める
2009.6.11
*/


//ポリゴンの面積
GetArea(p0 , p1 , p2 , p3)
{
  var s;
  var half_A;
  var half_B;
  var a , b , c , d , e;

  a = vlen(p1 - p0);
  b = vlen(p2 - p1);
  c = vlen(p3 - p2);
  d = vlen(p0 - p3);
  e = vlen(p2 - p0);
  half_A = (a + b + e) / 2.0;
  half_B = (c + d + e) / 2.0;
  s  = sqrt(half_A * (half_A - a) * (half_A - b) * (half_A - e));
  s += sqrt(half_B * (half_B - c) * (half_B - d) * (half_B - e));

  return s;
}


main(doc , op)
{
  if(!instanceof(op , PolygonObject))return;

  var pgc = op->GetPolygonCount();
  var pgs = op->GetPolygons();
  var pts = op->GetPoints();
  var i , idx;
  var s;

  for(i = 0 , s = 0 ; i < pgc ; i++){
    idx = i * 4;
    s += GetArea(pts[pgs[idx]] ,
                   pts[pgs[idx + 1]] ,
                     pts[pgs[idx + 2]] ,
                       pts[pgs[idx + 3]]);
  }

  println(s);
}

さて、ポリゴンの面積を計算してみよう。
プリミティブオブジェクトのポリゴンを使ってみます。
幅100/高さ100、編集可能にし、ポリゴンオブジェクトにします。

結果です。

10000.000000

面積は間違っていませんね。

では、複数枚のポリゴンを計算してみましょう。
立方体オブジェクトで試してみます。
サイズは、200×200×200で、同じように編集可能にしポリゴンオブジェクトにします。

結果です。

240000.000000

間違ってないですね。
200×200×6枚
体積じゃないですよ。

この立方体ポリゴンを「三角ポリゴン化」を実行してから実行してください。

240000.000000

間違いなく、同じ数値が返ってきます。

では、この面積を求めるスクリプトですが、複数選択オブジェクトに対応していません。
ポリゴンも選択ポリゴンにも対応していません。
C4Dコマンドライクにして見ましょう。

ポリゴンモード以外は、全ポリゴンが対象。

ポリゴンモードの時
ポリゴンが全く選択選択されていなければ、全ポリゴンが対象。
ポリゴンが選択されていれば、選択されているポリゴンのみが対象。

これで良いね。

参考にするスクリプトは、ごく最近に取り上げた記事の中にあります。
http://villager-and-c4d.cocolog-nifty.com/blog/2009/05/r11sdkobject-b5.html

参考にするスクリプトには、余計な部分が含まれています。
ヌルオブジェクトの追加とアンドゥ。

/*
ポリゴンオブジェクトの面積を計算させる為のベース。
R11SDK object(n)タイプ
2009.6.11
*/



main(doc , op)
{

  if(!object(0))return;//選択されたオブジェクトが無ければ終了

 
var mode;     //ポリゴンモードか?
  var pts , pgs;//ポイント配列とポリゴン配列
 
var pgc;      //ポリゴン数
 
var sel;      //ポリゴン選択
 
var obj_c;    //選択オブジェクトカウンター
  var i;

  mode =
IsCommandChecked(12187);//ポリゴンモードか?

  for(obj_c = 0 ; object(obj_c) ; obj_c++){
   
if(!instanceof(object(obj_c) , PolygonObject))continue;//選択オブジェクトがポリゴン以外は除外

    pts = object(obj_c)->GetPoints();
    pgc = object(obj_c)->GetPolygonCount();
    pgs =
object(obj_c)->GetPolygons();
    sel =
object(obj_c)->GetPolygonSelection();

   
if(!mode || (mode && !(sel->GetCount())))sel->SelectAll(0 , pgc);
   
//ポリゴンモード以外、もしくはポリゴンモードでポリゴンが選択されていなければ、全ポリゴンを選択にする。

   
//選択されたポリゴンの面積を計算します。
   
for(i = 0 ; i < pgc ; i++){
      if(!(sel->IsSelected(i)))continue;//選択状態にないポリゴンは対象外





    }
  }

}

ごめんなさい。
参考にするまでもなかったような…

面積を計算するポリゴンは、選択されているポリゴンを対象にします。
その為、ポリゴンモード以外の場合、全ポリゴンを選択状態にします。
ポリゴンモードの場合、選択ポリゴンがあれば、それをそのまま対称にします。
が、ポリゴンが全く選択されていなければ、全てを選択状態にします。

それでは先ほどの、ポリゴンの面積を計算する関数と、ポリゴンの4点を渡す部分を組み込みます。

/*
ポリゴンオブジェクトの面積を計算する。
R11SDK object(n)タイプ
2009.6.11
*/


//ポリゴンの面積
GetArea(p0 , p1 , p2 , p3)
{
  var s;
  var half_A;
  var half_B;
  var a , b , c , d , e;

  a = vlen(p1 - p0);
  b = vlen(p2 - p1);
  c = vlen(p3 - p2);
  d = vlen(p0 - p3);
  e = vlen(p2 - p0);
  half_A = (a + b + e) / 2.0;
  half_B = (c + d + e) / 2.0;
  s  = sqrt(half_A * (half_A - a) * (half_A - b) * (half_A - e));
  s += sqrt(half_B * (half_B - c) * (half_B - d) * (half_B - e));

  return s;
}



main(doc , op)
{

  if(!object(0))return;//選択されたオブジェクトが無ければ終了

 
var mode;     //ポリゴンモードか?
  var pts , pgs;//ポイント配列とポリゴン配列
 
var pgc;      //ポリゴン数
 
var sel;      //ポリゴン選択
 
var obj_c;    //選択オブジェクトカウンター
  var i;        //カウンター
  var idx;      //ポイント配列用インデックス
  var s;        //面積

  mode =
IsCommandChecked(12187);//ポリゴンモードか?

  for(obj_c = 0 ; object(obj_c) ; obj_c++){
   
if(!instanceof(object(obj_c) , PolygonObject))continue;//選択オブジェクトがポリゴン以外は除外

    pts = object(obj_c)->GetPoints();
    pgc = object(obj_c)->GetPolygonCount();
    pgs =
object(obj_c)->GetPolygons();
    sel =
object(obj_c)->GetPolygonSelection();

   
if(!mode || (mode && !(sel->GetCount())))sel->SelectAll(0 , pgc);
   
//ポリゴンモード以外、もしくはポリゴンモードでポリゴンが選択されていなければ、全ポリゴンを選択にする。

   
//選択されたポリゴンの面積を計算します。
   
for(i = 0 , s = 0 ; i < pgc ; i++){
      if(!(sel->IsSelected(i)))continue;//選択状態にないポリゴンは対象外
       idx = i * 4;
       s += GetArea(pts[pgs[idx]] ,
                       pts[pgs[idx + 1]] ,
                         pts[pgs[idx + 2]] ,
                           pts[pgs[idx + 3]]);
    }
    println(object(obj_c)->GetName() , " : " , s);
  }
}

これで、ポリゴンオブジェクトの面積を計算できます。
たぶん使うとしたら、CAD絡みの方だろうか?
でも面積を計算するなら、普通CADで計算する事でしょう。

最後にメニューの状態です。

複数選択オブジェクトの中に、ポリゴンオブジェクトが在るか?

var c;
ENABLE = FALSE;
for(c = 0 ; object(c) ; c++){
  if(instanceof(object(c) , PolygonObject))ENABLE = TRUE;
}

これで、完成です。

ま、言うまでもないけれど、誤差はあるのだ。
三角ポリゴン1枚で構成されたオブジェクトなら問題ないだろう…
でも、大体が曲面を構成する中の一個のポリゴンです。
四角ポリゴンを単純に2個の三角ポリゴンに分割して面積を求めるのは、安易な考えですけどね。その所で誤差がでます。
誤差を少なくするのであれば、曲面を意識した細分割を実行して計算するとよいでしょう。
ただし、C4Dの細分割(細分化)は、平面ポリゴンとしての分割とHyperNURBSしかないですけどね。

|

« StudioMOMO「C.O.F.F.E.E.でバッチレンダリング」… | トップページ | C4DのC.O.F.F.E.E.のPointLineDistance()関数を使って、ポリゴンの面積を求めてみる »