1.はじめに
モデルデータは、頂点で囲まれた面で構成されたものになります。この面には法線というものがあり、表の方向を示すベクトルになります。
スライサーでは、この法線情報から当該面が区切る壁としてどちらの方向が内側か(外側か)を判断することになります。
2.面を構成する頂点と法線の関係
面は最低3頂点で面積を持ち法線が定義できます。STLファイルでは、3頂点で面を構成します。
ので、まず頂点の並びと法線の関係を定義します。(STLファイル内の定義と一致するはず)
下図で、P1,P2,P3の順で面が構成される場合、Z+方向が法線方向(図中矢印)となります。(スライサー座標軸)
この場合の法線は (X,Y,Z)=(0,0,1)となります。(単位ベクトル化)
3.法線の算出式
法線の算出はベクトルの外積を利用しました。
ソースコードの関連部分を抜粋します。
T_VERは構造体で、X,Y,Z値を保持する頂点を表します。
// ベクトルの長さ算出(3D-xyz)
double VectorUtil::Length(T_VER v)
{
return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
// 2頂点からベクトル算出(3D-xyz)
T_VER VectorUtil::Vector(T_VER p0, T_VER p1)
{
T_VER v;
v.x = p1.x - p0.x;
v.y = p1.y - p0.y;
v.z = p1.z - p0.z;
return v;
}
// ベクトルの正規化(3D-xyz)
T_VER VectorUtil::Normalize(T_VER v)
{
double l;
T_VER n;
l = Length(v);
if (l) {
n.x = v.x / l;
n.y = v.y / l;
n.z = v.z / l;
} else {
n.x = 0;
n.y = 0;
n.z = 0;
}
return n;
}
// ベクトルの外積
T_VER VectorUtil::CrossProduct(T_VER p0, T_VER p1, T_VER p2)
{
T_VER v[2];
T_VER n;
v[0] = Normalize(Vector(p1, p0));
v[1] = Normalize(Vector(p2, p0));
n.x = v[0].y * v[1].z - v[0].z * v[1].y;
n.y = v[0].z * v[1].x - v[0].x * v[1].z;
n.z = v[0].x * v[1].y - v[0].y * v[1].x;
return Normalize(n);
}
4.2次元での法線(?)
スライスするとき、最初に3次元のモデルデータを特定のZ位置でスライスし、あとは2次元での演算(考え)が主となります。
このとき、ある稜線に対し、前記法線と同様の考えを導入しています。
これは、その稜線に対しどちらが内側(外側)か?を判断するためです。
頂点を連続的に結んでいき閉領域を定義しますが、このとき、内側がどちらなのかということを法線の概念で保持していることになります。
下図をあるスライス面とします。
P1~P4の頂点で構成され、P1~P4には順番に意味をもたせます。このとき稜線P1-P2の場合、図で下側(ベクトル進行方向の右手が外側(左手が内側))として処理します。
演算は、稜線の頂点からベクトルを算出し、Z+軸で90度回転させた方向を法線扱いしています。
5.おわりに
法線などについては、ググるとでてきます。大丈夫だと思いますが誤りなどがあれば教えてください。