ベクトル演算ライブラリ¶
ここでは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
などが用意されています。
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)が取得できます。
またベクトルを加減算して新たな点を計算でき、点と点の差を取るとベクトルとなります。
- 点 + ベクトル = 点
- 点 ― ベクトル = 点
- 点 ― 点 = ベクトル
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の同時座標が作成されます。
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次元空間上の点を取得することができます。
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成分はその点にかかる重みと考えることもでき、同次座標同士の和はその加重平均に対応します。
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)
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
を使うと空間内での任意の回転が容易に記述できます。
// 回転軸を中心に指定した角度だけ回転
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)することができます。
// グローバル座標系で(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
からその変換行列を取得することもできます。
// 先程のコードの続き
// ローカライズに対応する行列を取得し、その行列で座標変換を行う
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)に内包されているかどうかを取得できます。
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
を |||
演算子で得ることもできます。
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
は含みません。
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
IPoint3d と IBoundary3d¶
IBoundary3d
と IPoint3d
という2つの重要なインターフェイスについて説明します。
これらは次のように定義されています(C#)。
public interface IBoundary3d
{
Box3d BoundingBox { get; }
Sphere3d BoundingSphere { get; }
}
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<T> | MoNo.Geometries.dll |
Triangle<T> | MoNo.Geometries.dll |
Polyline<T> | MoNo.Geometries.dll |
Cloud<T> | MoNo.Geometries.dll |
Soup<T> | MoNo.Geometries.dll |
Mesh<T> | 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
は[-π, π) の範囲を表現する構造体です。
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
の場合と比較してみて下さい。
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