.. :tocdepth:2 ============================ ベクトル演算ライブラリ ============================ ここではMoNo.RAILに用意されている幾何計算を行うのに便利な型を紹介します。 型名について ============================ 多くの型は2次元版と3次元版が用意されています。 また、要素として倍精度浮動小数点数型、単精度浮動小数点数型、整数型のものがあります。 それぞれ型名に接尾語として2,3およびd,f,iがついています。 例えば ``Point3d`` はX,Y,Zの座標値を倍精度浮動小数点数型として持つ3次元空間上の点を表す構造体です。 また、 ``Vector2i`` はX,Yの要素を整数型として持つ2次元のベクトルを表す構造体です。 本項では主に3次元版の倍精度浮動小数点数型のもの(接尾語が3dのもの)について説明します。 点とベクトル ============================ 点を位置ベクトルとみなして同じ型で表現するライブラリもありますが、MoNo.RAILでは点とベクトルを幾何オブジェクトとして明確に区別します。 ``Vector3d`` は3次元空間での方向ベクトルを表す構造体です。 X,Y,Z各成分を持ち、一般的なベクトル演算が行えます。 ゼロベクトルを表す ``Zero`` , 各単位ベクトルを表す ``Ex`` , ``Ey`` , ``Ez`` などが用意されています。 .. code-block:: fsharp let v1 = Vector3d(1.0, 2.0, 3.0) let v2 = Vector3d(2.0, 4.0, 3.0) // 和 v1 + v2 |> printfn "%A" // (3, 6, 6) // 差 v1 - v2 |> printfn "%A" // (-1, -2, 0) // スカラー倍 2.0 * vec1 |> printfn "%A" // (2, 4, 6) // 内積 Vector3d.Dot(v1, v2) |> printfn "%f" // 19.000000 // 外積 v1 * v2 |> printfn "%A" // (-6, 3, 0) // 長さ(ノルム) v1.Length |> printfn "%f" // 3.741657 // X方向単位ベクトル Vector3d.Ex |> printfn "%A" // (1, 0, 0) ... ``Point3d`` は3次元空間中の点を表す構造体です。 X,Y,Zの各成分を持ちます。 ``Distance`` メソッドで引数に与えられた点との距離を計算でき、スタティックプロパティ ``Point3d.Zero`` で原点(0.0, 0.0, 0.0)が取得できます。 またベクトルを加減算して新たな点を計算でき、点と点の差を取るとベクトルとなります。 * 点 + ベクトル = 点 * 点 ― ベクトル = 点 * 点 ― 点 = ベクトル .. code-block:: fsharp let p1 = Point3d(1.0, 2.0, 3.0) p1.X |> printfn "%f" // 1.000000 p1.Y |> printfn "%f" // 2.000000 p1.Z |> printfn "%f" // 3.000000 let p2 = Point3d(1.0, 5.0, 7.0) p2.Distance(p1) |> printfn "%f" // 5.000000 Point3d.Zero |> printfn "%A" // (0, 0, 0) let vec = Vector3d(1.0, 1.0, 1.0) p1 + vec |> printfn "%A" // (2, 3, 4) : Point3d p1 - vec |> printfn "%A" // (0, 1, 2) : Point3d p1 - p2 |> printfn "%A" // (0, -3, -4) : Vector3d 同次座標 ================================= ``HmCod3d`` は3次元の同次座標を表す構造体です。斉次座標や射影座標と呼ばれることもあります。 通常、3次元空間上の点やベクトルは(X,Y,Z)といったように3つの数字で表されますが、同次座標では(X,Y,Z;W)と4つの数字で表します。 ``Point3d`` から変換すると、Wの値が1の同次座標が作成されます。 また ``Vector3d`` から変換するとWの値が0の同時座標が作成されます。 .. code-block:: fsharp let hm = HmCod3d(1.0, 2.0, 3.0, 1.0) hm |> printfn "%A" // (1, 2, 3; 1) let pnt = Point3d(1.0, 1.0, 1.0) pnt |> HmCod3d |> printfn "%A" // (1, 1, 1; 1) let vec = Vector3d(1.0, 1.0, 1.0) vec |> HmCod3d |> printfn "%A" // (1, 1, 1; 0) 同次座標(X,Y,Z;W)は点(X/W, Y/W, Z/W)に対応します。点とW値を指定して同次座標を作成することもできます。 また ``Project()`` メソッドで対応する3次元空間上の点を取得することができます。 .. code-block:: fsharp let pnt = Point3d(1.0, 2.0, 3.0) let hm = HmCod3d(pnt, 2.0) hm |> printfn "%A" // (2, 3, 6; 2) hm.Project() |> printfn "%A" // (1, 2, 3) 同次座標のW成分はその点にかかる重みと考えることもでき、同次座標同士の和はその加重平均に対応します。 .. code-block:: fsharp let pnt1 = Point3d.Zero let pnt2 = Point3d(1.0, 1.0, 1.0) let hm1 = HmCod3d(pnt1, 1.0) + HmCod3d(pnt2, 1.0) // 同じ重みの加重平均=中点 hm1.Project() |> printfn "%A" // (0.5, 0.5, 0.5) let hm2 = HmCod3d(pnt1, 1.0) + HmCod3d(pnt2, 4.0) // pnt2に4倍の重みをつけた加重平均 hm2.Project() |> printfn "%A" // (0.8, 0.8, 0.8) QuaternionとRotation =================================== ``Quaternion`` は四元数を表す構造体です。 四元数とは3つの虚数単位i, j, kを使って W + Xi + Yj + Zk (W, X, Y, Zはそれぞれ実数) と表される数です。(i*i = j*j = k*k = ijk = -1) .. code-block:: fsharp let q1 = Quaternion(1.0, 1.0, 1.0, 1.0) let q2 = Quaternion(0.0, 1.0, 2.0, 3.0) // 和 (q1 + q2) |> printfn "%A" // 1 + 2 i + 3 j + 4 k // 差 (q1 - q2) |> printfn "%A" // 1 + 0 i + -1 j + -2 k // 積 (q1 * q2) |> printfn "%A" // -6 + 2 i + 0 j + 4 k // 定数倍 (2.0 * q1) |> printfn "%A" // 2 + 2 i + 2 j + 2 k // 絶対値 q1.Abs() |> printfn "%f" // 2.0 // 単位化 let normalized = q1.Normalize() normalized |> printfn "%A" // 0.5 + 0.5 i + 0.5 j + 0.5 k normalized.Abs() |> printfn "%A" // 1.0 // 逆元 let inv = q1.Inverse() inv |> printfn "%A" // 0.25 + -0.25 i + -0.25 j + -0.25 k (q1 * inv) |> printfn "%A" // 1 + 0 i + 0 j + 0 k (inv * q1) |> printfn "%A" // 1 + 0 i + 0 j + 0 k さて、この四元数ですが三次元空間の回転を表現するために利用できることが知られています。 とはいえそのためには四元数の数学的理解が必要となります。 そこでMoNo.RAILでは ``Quaternion`` をラップして3次元空間の回転を扱いやすくした構造体 ``Rotation3d`` が用意されています。 ``Rotation3d`` を使うと空間内での任意の回転が容易に記述できます。 .. code-block:: fsharp // 回転軸を中心に指定した角度だけ回転 let axis = Vector3d(1.0, 1.0, 1.0) // 回転軸 let radius = 2.0*Math.PI/3.0 // 回転角度 let r1 = Rotation3d(axis, radius) r1.Rotate(Point3d(1.0, 0.0, 0.0)) |> printfn "%A" // (0, 1, 0) // from方向をto方向に向けるような回転 let fromVec = Vector3d(1.0, 1.0, 0.0) let toVec = Vector3d(0.0, 0.0, 1.0) let r2 = Rotation3d(fromVec, toVec) // 回転軸 r2.Axis |> printfn "%A" // (0.707106781186547, -0.707106781186547, 0) // 回転角 r2.Radian |> printfn "%A" // 1.570796327 なお、回転の中心は原点で回転方向は回転軸に対して右ねじの向きになります。 CodSys3d ============================= ``CodSys3d`` はローカル座標系(局所座標系)を表す構造体です。 グローバル座標(絶対座標系)における原点位置 ``Point3d`` と、ローカル座標系がグローバル座標系に対してどのように回転しているかの ``Rotation`` を持ちます。 ローカル座標系を定義すると、グローバル座標系における座標をローカル座標系から見た座標に変換(Localize)したり、ローカル座標系における座標をグローバル座標系から見た座標に変換(Globalize)することができます。 .. code-block:: fsharp // グローバル座標系で(1, 2, 3)を原点に持ち各座標軸はグローバル座標系と同じローカル座標系を定義 let codSys = CodSys3d(Rotation3d.Unit, Point3d(1.0, 2.0, 3.0)) codSys |> printfn "%A" // O = (1, 2, 3); U = (1, 0, 0); V = (0, 1, 0); N = (0, 0, 1) // グローバル座標での原点座標をローカル座標に変換 let globalPos1 = Point3d.Zero let localPos1 = codSys.Localize(globalPos1) localPos1 |> printfn "%A" // (-1, -2, -3) // ローカル座標をグローバル座標に変換 let localPos2 = Point3d(1.0, 1.0, 1.0) let globalPos2 = codSys.Globalize(localPos2) globalPos2 |> printfn "%A" // (2, 3, 4) また座標系の変換は、同次座標 ``HmCod3d`` に4x4行列 ``HmMatrix3d`` を掛けることでも行えます。 ``CodSys3d`` からその変換行列を取得することもできます。 .. code-block:: fsharp // 先程のコードの続き // ローカライズに対応する行列を取得し、その行列で座標変換を行う let localizeHmMat = codSys.ToLocalizeMatrix() (localizeHmMat * HmCod3d(globalPos1)).Project() |> printfn "%A" // (-1, -2, -3) // グローバライズに対応する行列を取得し、その行列で座標変換を行う let globalizeHmMat = codSys.ToGlobalizeMatrix() (globalizeHmMat * HmCod3d(localPos2)).Project() |> printfn "%A" // (-1, -2, -3) RangeとBoxおよびSphere ============================ ``Range`` は数直線上のある区間を表現する構造体です。下限 ``Lower`` と上限 ``Upper`` を持ちます。 ``Includes`` メソッドで数値がその半開区間[Lower, Upper)に内包されているかを取得できます。 ``Interior`` メソッドでは開区間(Lower, Upper)に内包されているかどうかを取得できます。 .. code-block:: fsharp let range = Range(1.0, 2.0, -3.0, 4.0) range |> printfn "%A" // [-3, 4) let x1 = -3.0 let x2 = 5.0 range.Includes(-3.0) |> printfn "%A" // true range.Includes(5.0) |> printfn "%A" // false range.Includes(4.0) |> printfn "%A" // false range.Interior(-3.0) |> printfn "%A" // false ``Box2d`` および ``Box3d`` は ``Range`` の2次元版/3次元版です。 ``Box3d`` はX軸、Y軸、3次元版ではZ軸も含め、各軸にそれぞれ区間を持ち、``Range`` の直積と捉えられます。 視覚的には矩形/直方体の領域となります。 ``Lower`` は各軸の下限を組にした点、 ``Upper`` は各軸の上限を組にした点で、 ``Size`` プロパティで領域矩形/直方体の大きさを取得できます。 ``Size`` プロパティで取得できる型 ``Size2d`` / ``Size3d`` はプロパティ ``X`` , ``Y`` , ``Z`` を持ち、それぞれの軸向きの長さを持ちます。 2つの ``Box`` の共通部分を ``&&&`` 演算子で、2つの ``Box`` をともに含む最小の ``Box`` を ``|||`` 演算子で得ることもできます。 .. code-block:: fsharp let box1 = Box3d(Range(-1.0, 2.0), Range(1.0, 3.0), Range(-4.0, -2.0)) box1.Lower |> printfn "%A" // (-1, 1, -4) box1.Upper |> printfn "%A" // (2, 3, -2) box1.Includes(Point3d(1.0, 2.0, 3.0)) |> printfn "%A" // false box1.Includes(Point3d(1.0, 1.0, -3.0)) |> printfn "%A" // true box1.Interior(Point3d(-1.0, 1.0, -4.0)) |> printfn "%A" // false box1.Size |> printfn "%A" // (3, 2, 2) let box2 = Box3d(Range(-1.0, 3.0), Range(2.0, 6.0), Range(-3.0, 3.0)) (box1 &&& box2) |> printfn "%A" // Lower = (-1, 2, -3), Upper = (2, 3, -2) (box1 ||| box2) |> printfn "%A" // Lower = (-1, 1, -4), Upper = (3, 6, 3) ``Sphere3d`` も領域を表す構造体です。これは中心と半径を持つ球形の領域です。 Boxと同様、 ``Includes`` や ``Interior`` メソッドでその領域に点を含むかどうかを判定できます。 ``Includes`` は境界上の点(球面上の点)を含みますが ``Interior`` は含みません。 .. code-block:: fsharp let sphere = Sphere3d(Point3d.Zero, 10.0) sphere.Includes(Point3d(5.0, 0.0, 0.0)) |> printfn "%A" // true sphere.Includes(Point3d(0.0, 10.0, 0.0)) |> printfn "%A" // true sphere.Interior(Point3d(0.0, 10.0, 0.0)) |> printfn "%A" // false .. _vecmath_ipoint3d_and_iboundary3d: IPoint3d と IBoundary3d ============================ ``IBoundary3d`` と ``IPoint3d`` という2つの重要なインターフェイスについて説明します。 これらは次のように定義されています(C#)。 .. code-block:: csharp :caption: IBoundary3d public interface IBoundary3d { Box3d BoundingBox { get; } Sphere3d BoundingSphere { get; } } .. code-block:: csharp :caption: IPoint3d public interface IPoint3d : IBoundary3d { Point3d Position { get; } } * ``IBoundary3d`` とは: 「位置と大きさ」を持つ幾何データを表すインターフェイスです。 幾何オブジェクトの軸平行境界ボックス(``BoundingBox``)や境界球(``BoundingSphere``)を取得する機能を持ちます。 このインターフェイスはMoNo.RAILの広い範囲で重要な役割を果たします。 特に3Dビューの視野錐体(Viewing Frustum)の設定やフィットの処理において重要です。 MoNo.RAILの3Dビューは、オブジェクトが実装する ``IBoundary3d`` インターフェイスから描画対象の大きさを取得し、適切に視野錐体を設定したりフィットの処理を行ったりします。ユーザーが独自にエンティティ型を定義し3Dビューに表示したい場合には、そのエンティティ型に ``IBoundary3d`` を実装する必要があります。 * ``IPoint3d`` とは: 「点」を表すインターフェイスです。 例えば ``Point3d`` と ``Point3f`` は異なるデータ型ですが、どちらも点を表すデータとして統一的に扱いたいというニーズがあります。そのため、これらのデータ型は共に ``IPoint3d`` インターフェイスを実装し統一的に扱える設計となっています。 ``IPoint3d`` は ``IBoundary3d`` を継承しています。 ``IPoint3d`` は「点」であって大きさを持ちませんので、``BoundingBox`` は大きさを保たないボックス(``Lower`` と ``Upper`` が共に ``Position``)を、``BoundingSphere`` は半径ゼロの球(``Center == Position`` かつ ``Radius == 0``)を返します。 ``IBoundary3d`` を実装する代表的なデータ型を下記の表に示します。 特に ``Box3d`` や ``Sphere3d`` は ``IBoundary3d`` と相互依存した定義となっています。 =========== =========================== Type Assembly =========== =========================== Box3d MoNo.dll Box3f MoNo.dll Sphere3d MoNo.dll Sphere3f MoNo.dll Segment3d MoNo.dll Segment MoNo.Geometries.dll Triangle MoNo.Geometries.dll Polyline MoNo.Geometries.dll Cloud MoNo.Geometries.dll Soup MoNo.Geometries.dll Mesh MoNo.Geometries.dll CG.MeshObj MoNo.Geometries.dll =========== =========================== ``IPoint3d`` を実装する MoNo.dll 中のデータ型を下記の表に示します。 特に ``Point3d`` は ``IPoint3d`` と相互依存した定義となっています。 ======================= ==================================== Type Position { get; } の実装 ======================= ==================================== Point3d return this; Point3f return this.To3d(); PointNormal3d return this.Point; PointNormal3f return this.Point.To3d(); PointNormalUV3f return this.Point.To3d(); PointUV3f return this.Point.To3d(); ColoredPoint3f return this.Point.To3d(); ColoredPointNormal3f return this.Point.To3d(); ======================= ==================================== AngleとAngular ============================ ``Angle`` と ``Angular`` は角度を表現するのに特化した構造体です。弧度法(radian)および度数法(degree)の変換をサポートします。 ``Angle`` は[-π, π) の範囲を表現する構造体です。 .. code-block:: fsharp let angle1 = Angle.FromDegree(90.0) angle1.Degree |> printfn "%A" // 90.0 angle1.Radian |> printfn "%A" // 1.5707963237 let angle2 = Angle.FromRadian(Math.PI/4.0) angle2.Degree |> printfn "%A" // 45.0 angle2.Radian |> printfn "%A" // 0.7853981634 let angle3 = angle1 + angle2 angle3.Degree |> printfn "%A" // 135.0 angle3.Radian |> printfn "%A" // 2.35619449 let angle4 = angle1 + angle3 angle4.Degree |> printfn "%A" // -135.0 angle4.Radian |> printfn "%A" // -2.35619449 ``Angular`` は[-π, π)を超える範囲を表現できます。 ``Angle`` の場合と比較してみて下さい。 .. code-block:: fsharp let angle1 = Angular.FromDegree(90.0) angle1.Degree |> printfn "%A" // 90.0 angle1.Radian |> printfn "%A" // 1.5707963237 let angle2 = Angular.FromRadian(Math.PI/4.0) angle2.Degree |> printfn "%A" // 45.0 angle2.Radian |> printfn "%A" // 0.7853981634 let angle3 = angle1 + angle2 angle3.Degree |> printfn "%A" // 135.0 angle3.Radian |> printfn "%A" // 2.35619449 let angle4 = angle1 + angle3 angle4.Degree |> printfn "%A" // 225.0 angle4.Radian |> printfn "%A" // 3.926990817