« C4D C.O.F.F.E.E.で四角ポリゴンの法線を求める方法を考える。 | トップページ | ただの暇つぶし… »

C4D C.O.F.F.E.E.前回の四角ポリゴンの法線を求める関数を結果から比較してみる。

四角ポリゴンを分割した三角形の法線と面積比から求める関数。
頂点付近の4つの法線ベクトルから求める。
対角線から求める。

この3つの関数から実際にポリゴンの法線を求めて見ます。

4本の三角形の法線と面積比から求める。

//四角ポリゴンを三角形に分割して垂直に交わるベクトルと面積比から法線を求める。

//三角形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;
}


//四角ポリゴンの法線を求める
GetPolygonNormal(p0 , p1 , p2 , p3)
{
  var v;
  var s1 = GetArea(p0 , p1 , p2);
  var s2 = GetArea(p2 , p3 , p0);
  var s3 = GetArea(p0 , p1 , p3);
  var s4 = GetArea(p1 , p2 , p3);
  var ssum = s1 + s2 + s3 + s4;

  v  = vnorm(vcross(p2 - p1 , p0 - p1)) * (s1 / ssum);
  v += vnorm(vcross(p0 - p3 , p2 - p3)) * (s2 / ssum);
  v += vnorm(vcross(p1 - p0 , p3 - p0)) * (s3 / ssum);
  v += vnorm(vcross(p3 - p2 , p1 - p2)) * (s4 / ssum);

  return vnorm(v);
}

頂点付近の法線から求める。

//四角ポリゴンの頂点付近の垂直ベクトルから法線を求める
GetPolygonNormal(p0 , p1 , p2 , p3)
{
  var v;
 
  v  = vnorml(vcross(p1 - p0 , p3 - p0));//三角ポリゴンの場合、下と同じになります
  v += vnorml(vcross(p2 - p1 , p0 - p1));//三角ポリゴンの場合、上と同じになります
  v += vnorml(vcross(p3 - p2 , p1 - p2));//三角ポリゴンの場合(0.0 , 0.0 , 0.0)になります
  v += vnorml(vcross(p0 - p3 , p2 - p3));//三角ポリゴンの場合(0.0 , 0.0 , 0.0)になります

  return normal(v);
}

対角ベクトルから求める。

//四角ポリゴンの対角ベクトルから法線を求める
GetPolygonNormal(p0 , p1 , p2 , p3)
{
  return vnorm(vcross(p0 - p2 , p1 - p3));
}

予備で、2本の三角形の法線と面積比。

//2本の三角形の法線と面積比

//三角形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;
}


//四角ポリゴンの法線を求める
GetPolygonNormal(p0 , p1 , p2 , p3)
{
  var v;
  var s1 = GetArea(p0 , p1 , p2);
  var s2 = GetArea(p2 , p3 , p0);
  var ssum = s1 + s2;

  v  = vnorm(vcross(p2 - p1 , p0 - p1)) * (s1 / ssum);
  v += vnorm(vcross(p0 - p3 , p2 - p3)) * (s2 / ssum);

  return vnorm(v);
}

テストするポリゴンです。
意味のある数値ではないので、この数値に限定する必要はありませんが…

x y z
p0 0.0 0.0 -100.0
p1 -100.0 60.0 0.0
p2 0.0 20.0 100.0
p3 100.0 40.0 0.0

多くのポリゴンで試す必要がありますが、今回は1枚だけ。

今回のテストスクリプトです。

/*
法線の取得テスト
*/



//三角形p0,p1,p2の面積
GetArea(p0 , p1 , p2)
{
  var s;
  var half;
  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;
}


//四角ポリゴンの法線を求める(2枚の三角形に分割し、それぞれの法線ベクトルと面積比から)
GetPolygonNormal_D(p0 , p1 , p2 , p3)
{
  var v;
  var s1 = GetArea(p0 , p1 , p2);
  var s2 = GetArea(p2 , p3 , p0);
  var ssum = s1 + s2;

  v  = vnorm(vcross(p2 - p1 , p0 - p1)) * (s1 / ssum);
  v += vnorm(vcross(p0 - p3 , p2 - p3)) * (s2 / ssum);

  return vnorm(v);
}



//四角ポリゴンの法線を求める
//(分割方向を変え4枚の三角形に分割し、それぞれの法線ベクトルと面積比から)

GetPolygonNormal_A(p0 , p1 , p2 , p3)
{
  var v;
  var s1 = GetArea(p0 , p1 , p2);
  var s2 = GetArea(p2 , p3 , p0);
  var s3 = GetArea(p0 , p1 , p3);
  var s4 = GetArea(p1 , p2 , p3);
  var ssum = s1 + s2 + s3 + s4;

  v  = vnorm(vcross(p2 - p1 , p0 - p1)) * (s1 / ssum);
  v += vnorm(vcross(p0 - p3 , p2 - p3)) * (s2 / ssum);
  v += vnorm(vcross(p1 - p0 , p3 - p0)) * (s3 / ssum);
  v += vnorm(vcross(p3 - p2 , p1 - p2)) * (s4 / ssum);

  return vnorm(v);
}


//四角ポリゴンの法線を求める(頂点付近のベクトル4本から)
GetPolygonNormal_B(p0 , p1 , p2 , p3)
{
  var v;
 
  v  = vnorm(vcross(p1 - p0 , p3 - p0));
  v += vnorm(vcross(p2 - p1 , p0 - p1));
  v += vnorm(vcross(p3 - p2 , p1 - p2));
  v += vnorm(vcross(p0 - p3 , p2 - p3));

  return vnorm(v);
}


//四角ポリゴンの法線を求める(対角ベクトルから)
GetPolygonNormal_C(p0 , p1 , p2 , p3)
{
  return vnorm(vcross(p0 - p2 , p1 - p3));
}




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

  var pts = op->GetPoints();
  var pgs = op->GetPolygons();

  var p0 = pts[pgs[0]];
  var p1 = pts[pgs[1]];
  var p2 = pts[pgs[2]];
  var p3 = pts[pgs[3]];

  println("-----");
  println("4面積 : " , GetPolygonNormal_A(p0 , p1 , p2 , p3));
  println("4法線 : " , GetPolygonNormal_B(p0 , p1 , p2 , p3));
  println("対角 : " , GetPolygonNormal_C(p0 , p1 , p2 , p3));
  println("2面積 : " , GetPolygonNormal_D(p0 , p1 , p2 , p3));
  println("-----");
}

結果です。

x y z
4枚の三角形の法線と面積比 0.099015 0.990148 -0.099015
4頂点付近の法線 0.094213 0.991423 -0.092413
対角ベクトル 0.099015 0.990148 -0.099015
2枚の三角形の法線と面積比 0.099015 0.990148 -0.099015

「4つの頂点付近の法線」からは、頂点どうしの距離が考慮されていないので、ポイントが1つだけ極端に離れていても4つのベクトルのウェイトが同じです。
正確なものは得られないと予想はしていました。

「4枚の三角形の法線と面積比」からは、1枚のポリゴンを分割方向を変えた三角形を4枚使っています。
面積が倍になるので、これも正確なものにはならないと予想していました。

予備の「2枚の三角形の法線と面積比」からは、分割方向が1方向だけなので、これも正確なものにはならないだろうと予想していましたが、「4枚・・・」の場合と同じ結果になったのは予想外でした。結果が同じなら、わざわざ「4枚・・・」から求めるより、「2枚・・・」からの方がシンプルなので使うなら、こちらを使う事になるでしょう。
結果が同じになったということは、考え方は間違ってはいないということだろうか…?

「対角ベクトル」と、「2枚の三角形・・・」と「4枚の三角形・・・」の結果が同じになるとは、全く予想していませんでした。
通常「対角ベクトル」を使いますが、分割した三角形の法線と面積を使っても間違いではないと言う事です。
ま、一番シンプルな「対角ベクトル」を使うことをお勧めします。当たり前か…

さて最後に、テストしたポリゴンのポイントを移動して変形してください。
変形後、先ほどのテストスクリプトを実行しても「4頂点付近・・・」以外が返すベクトルは一致しています。
この事だけ確認してください。

|

« C4D C.O.F.F.E.E.で四角ポリゴンの法線を求める方法を考える。 | トップページ | ただの暇つぶし… »