/// <summary> /// 光線r = p + tdが円sと交差しているかどうかを判定 /// </summary> /// <param name="vP">基点</param> /// <param name="vD">方向ベクトル</param> /// <param name="circle">対象とする円</param> /// <return>交差している場合、true返す</return> public static bool RayCircle(MCVector2 vP, MCVector2 vD, MCCircle2 circle) { MCVector2 vM = vP - circle.C; float fC = vM.Dot() - circle.R * circle.R; // 少なくとも1つの実数解が存在している場合、交差している if (fC <= 0.0f) { return(true); } float fB = vM.Dot(vD); // 光線の原点が円の外側にあり光線が円から離れた方向を指している場合には早期に終了 if (fB > 0.0f) { return(false); } float fDiscr = fB * fB - fC; // 負の判別式は光線が円を外れていることに一致 if (fDiscr < 0.0f) { return(false); } // これで光線は円と交差している return(true); }
/// <summary> /// 光線r = p + td, |rvD| = 1が円sに対して交差しているかどうか。 /// </summary> /// <param name="rvP">基点</param> /// <param name="rvD">方向ベクトル</param> /// <param name="rS">対象とする円</param> /// <param name="time">0≦t≦Tmax</param> /// <param name="vIntersect">交差した位置</param> /// <return>交差している場合、交差している*pTの値および交差点*pQを返す</return> bool IntersectRayCircle(MCVector2 rvP, MCVector2 rvD, MCCircle2 rS, out float time, out MCVector2 vIntersect) { time = 0; vIntersect = new MCVector2(); MCVector2 vM = rvP - rS.C; float fB = vM.Dot(rvD); float fC = vM.Dot() - rS.R * rS.R; // rの原点が*rSの外側にあり(c > 0)、rが*rSから離れていく方向を指している場合(fB > 0)に終了 if (fC > 0.0f && fB > 0.0f) { return(false); } float fDiscr = fB * fB - fC; // 負の判別式は光線が円を外れていることに一致 if (fDiscr < 0.0f) { return(false); } // これで光線は円と交差していることが分かり、交差する最小の値*pTを計算 time = -fB - (float)System.Math.Sqrt(fDiscr); // *pTが負である場合、光線は円の内側から開始しているので*pTをゼロにクランプ if (time < 0.0f) { time = 0.0f; } vIntersect = rvP + time * rvD; return(true); }
/// <summary> /// マトリックスと2要素のベクトルを掛ける /// </summary> /// <param name="v">対象とするベクトル</param> /// <return>演算結果を返す</return> public MCVector2 MultiplyD3DXVECTOR2(MCVector2 v) { MCVector2 o = new MCVector2(); o.X = (M11 * v.X) + (M12 * v.Y); o.Y = (M21 * v.X) + (M22 * v.Y); return(o); }
/// <summary> /// 正規化する /// </summary> void Normalize() { float f = 1.0f / vNormal.Length(); if (f != 0) { vNormal *= f; distance *= f; } }
/// <summary> /// 線分ABが平面p(this)と交差しているかどうかを判定。交差していれば交差点を返す /// </summary> /// <param name="vA">始点</param> /// <param name="vB">終点</param> /// <param name="time">平面と交差する方向のある直線abと交差する値</param> /// <param name="pOut">交差点</param> /// <return>交差の値tおよび交差点Qとともにtrueを返す。そうでなければfalseを返す</return> bool IntersectSegment(MCVector2 vA, MCVector2 vB, out float time, out MCVector2 vIntersect) { vIntersect = new MCVector2(); // 平面と交差する方向のある直線abと交差するtの値を計算 MCVector2 vAB = vB - vA; time = (distance - vNormal.Dot(vA)) / vNormal.Dot(vAB); // tが[0..1]の中にある場合、交差点を計算して返す if (time >= 0.0f && time <= 1.0f) { vIntersect = vA + (vAB * time); return(true); } // そうでない場合tは+INF, -INF, NaN, あるいは[0..1]の中にはないので、交差なし return(false); }
/// <summary> /// 光線r = p + tdが円sと交差しているかどうかを判定(静止している円rS1に対して交差) /// </summary> /// <param name="c1">円1</param> /// <param name="c2">円2</param> /// <param name="v1">方向ベクトル</param> /// <param name="v2">対象とする円</param> /// <return>trueを返す場合、交差していて、衝突の時間が*pfTに格納される</return> bool MCMovingCircleCircle(MCCircle2 c1, MCCircle2 c2, MCVector2 v1, MCVector2 v2, out float time) { time = 0; // 円s1をs0の半径にまで拡張 MCCircle2 S1Ex = c2; S1Ex.R += c1.R; // s0およびs1の両方からs1の運動を引き算し、s1を静止させる MCVector2 v = v1 - v2; // これで、方向のある線分 s = s0.c + tv, v = (*pV0-*pV1)/||*pV0-*pV1|| を // 拡張した円に対して交差させることができる MCVector2 vIntersect; float fVLen = v.Length(); v /= fVLen; if (IntersectRayCircle(c1.C, v, S1Ex, out time, out vIntersect)) { return(time <= fVLen); } return(false); }
/// <summary> /// 線分ABが平面p(this)と交差しているかどうかを判定。交差していれば交差点を返す /// </summary> /// <param name="vA">始点</param> /// <param name="vVel">ベロシティー</param> /// <param name="fEpsilon">平面の厚み</param> /// <param name="time">平面と交差する方向のある直線abと交差する値</param> /// <param name="vIntersect">交差点</param> /// <return>交差の値tおよび交差点Qとともにtrueを返す。そうでなければfalseを返す</return> int IntersectLine(MCVector2 vA, MCVector2 vVel, float fEpsilon, out float time, MCVector2 vIntersect) { vIntersect = new MCVector2(); time = 0; // 平面交差点の間隔を得る: // 球体位置から平面までの距離を計算します。 float signedDistToPlane = DistanceTo(vA); // float normalDotVelocity = vNormal.Dot(vVel); // 球体が平面に平行をになっているか?: if (normalDotVelocity == 0.0f) { if (System.Math.Abs(signedDistToPlane) >= fEpsilon) { // 厚みfEpsilon内の平面に埋め込まれていない // 衝突してない return(0); } // 球体は平面に埋め込まれています。 time = 0.0f; vIntersect = GetClosestPtPoint(vA); return(2); } else { // 平面と交差する方向のある直線abと交差するtの値を計算 time = (distance - vNormal.Dot(vA)) / vNormal.Dot(vVel); // tが[0..1]の中にある場合、交差点を計算して返す if (time >= 0.0f && time <= 1.0f) { vIntersect = vA + (vVel * time); return(1); } // そうでない場合tは+INF, -INF, NaN, あるいは[0..1]の中にはないので、交差なし } return(0); }
/// <summary> /// 光線r = p + td, |vDir| = 1が円sに対して交差しているかどうか。 /// </summary> /// <param name="position">基点</param> /// <param name="vDir">方向ベクトル</param> /// <param name="rS">対象とする円</param> /// <param name="vIntersect1">接触点1</param> /// <param name="vIntersect2">接触点2</param> /// <return>交差している場合、交差点の数を返す</return> int IntersectRayCircle(MCVector2 position, MCVector2 vDir, out MCVector2 vIntersect1, out MCVector2 vIntersect2) { vIntersect1 = new MCVector2(); vIntersect2 = new MCVector2(); float t; MCVector2 vM = position - C; float a = vM.Dot(vDir); float b = vM.Dot() - R * R; // rの原点が*rSの外側にあり(c > 0)、rが*rSから離れていく方向を指している場合(a > 0)に終了 if (b > 0 && a > 0) { return(0); } float discr = a * a - b; // 負の判別式は光線が円を外れている if (discr < 0) { return(0); } // これで光線は円と交差していることが分かり、交差する最小の値*pTを計算 t = -a - (float)System.Math.Sqrt(discr); // tが負である場合、光線は円の内側 if (t < 0) { vIntersect1 = position + (t * -1) * vDir; return(1); } else if (discr == 0) { vIntersect1 = position + t * vDir; return(1); } vIntersect1 = position + t * vDir; vIntersect2 = position + (-a + (float)System.Math.Sqrt(discr)) * vDir; return(2); }
/// <summary> /// 与えられた厚みのイプシロンにより厚みのある平面に対して点vPを分類 /// </summary> /// <param name="vP">基点</param> /// <param name="planeThicknessEpsilon">イプシロン値</param> /// <return>平面に対しての点の位置を返す</return> public POSITION_PLANE_POINT ClassifyPointToPlane(MCVector2 vP, float planeThicknessEpsilon) { //================================ // 点の平面からの符号付距離を計算 //================================ float fDist = DistanceTo(vP); //================================ // 符号付距離を基にしてvPを分類 //================================ if (fDist > planeThicknessEpsilon) { // 平面の厚さのイプシロン値より大きかった return(POSITION_PLANE_POINT.POINT_IN_FRONT_OF_PLANE); } else if (fDist < -planeThicknessEpsilon) { // 平面の厚さのイプシロン値より小さかった return(POSITION_PLANE_POINT.POINT_BEHIND_PLANE); } return(POSITION_PLANE_POINT.POINT_ON_PLANE); }
/// <summary> /// vA,b,dの値をセットする /// </summary> /// <param name="fa">値vA</param> /// <param name="fb">値b</param> /// <param name="fd">値d</param> public void Set(float fa, float fb, float fd) { vNormal = new MCVector2(fa, fb); distance = fd; }
/// <summary> /// 光線r = p + td, |rvD| = 1が円sに対して交差しているかどうか。 /// </summary> /// <param name="vP">基点</param> /// <param name="vD">方向ベクトル</param> /// <param name="time">0≦t≦Tmax</param> /// <param name="vIntersect">交差した位置</param> /// <return>交差している場合、交差している*pTの値および交差点*pQを返す</return> public bool IntersectRayCircle(MCVector2 vP, MCVector2 vD, out float time, out MCVector2 vIntersect) { return(IntersectRayCircle(vP, vD, this, out time, out vIntersect)); }
/// <summary> /// 表面に向いているか? /// </summary> /// <param name="vN">法線</param> /// <return>直線が存在しない場合はfalseを返す。</return> bool IsFrontFacingTo(MCVector2 vN) { return(vNormal.Dot(vN) <= 0.0f); }
/// <summary> /// 平面に対してpvPointの点を垂直射影したときの点pOutを出力する@n /// </summary> /// 平面の法線は正規化されている物とする。 /// <param name="rV">垂直射影する点</param> /// <return>平面から指定した点までの距離</return> MCVector2 GetClosestPtPoint(MCVector2 vPoint) { return(vPoint - (vNormal.Dot(vPoint) - distance) * vNormal); }
/// <summary> /// 平面に対してvPointの点を垂直射影したときの点との大きさpTを返す@n /// </summary> /// 戻り値がマイナスの場合は裏面、プラスの時は表面に点vPointがある@n /// 平面の法線は正規化されている物とする。 /// <param name="vPoint">垂直射影する点</param> /// <return>平面から指定した点までの距離</return> float DistanceTo(MCVector2 vPoint) { return(vNormal.Dot(vPoint) - distance); }
/// <summary> /// 基点と法線からセットする /// </summary> /// <param name="vOrigin">基点</param> /// <param name="vN">法線</param> public void Set(MCVector2 vOrigin, MCVector2 vN) { vNormal = vN; distance = vNormal.X * vOrigin.X + vNormal.Y * vOrigin.Y; }
/// <summary> /// 光線r = p + tdが自身の円と交差しているかどうかを判定 /// </summary> /// <param name="vP">基点</param> /// <param name="vD">方向ベクトル</param> /// <return>交差している場合、true返す</return> public bool RayCircle(MCVector2 vP, MCVector2 vD) { return(RayCircle(vP, vD, this)); }
/// <summary> /// コンストラクタ /// </summary> /// <param name="v"></param> /// <param name="fd"></param> public MCPlane2(MCVector2 v, float fd) { vNormal = v; distance = fd; }
/// <summary> /// セットする /// </summary> /// <param name="v">2次元ベクトル</param> /// <param name="fr">半径</param> public void Set(MCVector2 v, float fr) { C = v; R = fr; }
/// <summary> /// コンストラクタ /// </summary> /// <param name="v">2次元ベクトル</param> /// <param name="fr">半径</param> public MCCircle2(MCVector2 v, float fr) { C = v; R = fr; }
/// <summary> /// コンストラクタ /// </summary> /// <param name="X"></param> /// <param name="Y"></param> /// <param name="Z"></param> /// <param name="d"></param> public MCPlane2(float X, float Y, float fd) { vNormal = new MCVector2(X, Y); distance = fd; }
/// <summary> /// v, dの値をセットする /// </summary> /// <param name="v">2要素ベクトル</param> /// <param name="fd">値d</param> public void Set(MCVector2 v, float fd) { vNormal = v; distance = fd; }
/// <summary> /// コンストラクタ /// </summary> /// <param name="vOrigin"></param> /// <param name="vN"></param> public MCPlane2(MCVector2 vOrigin, MCVector2 vN) { vNormal = vN; distance = vNormal.X * vOrigin.X + vNormal.Y * vOrigin.Y; }
/// <summary> /// 円 と 点によるあたり判定 /// </summary> /// <param name="point">点</param> /// <return>重なっている場合は trueを返し、 重なっていない場合はfalseを返す</return> public bool CirclePoint(MCVector2 point) { float len = (point - C).Length(); return(R >= len); }