2つの点A(x1、y1、z1)とB(x2、y2、z2)と点p(x、y、z)で定義された線分があります。ポイントがラインセグメント上にあるかどうかを確認するにはどうすればよいですか?
行のポイントisの場合:
(x - x1) / (x2 - x1) = (y - y1) / (y2 - y1) = (z - z1) / (z2 - z1)
3つの値をすべて計算し、それらが(ある程度の許容範囲で)同じである場合、ポイントは直線上にあります。
ポイントがライン上だけでなくセグメント内にあるかどうかをテストするには、次のことを確認できます。
x1 < x < x2, assuming x1 < x2, or
y1 < y < y2, assuming y1 < y2, or
z1 < z < z2, assuming z1 < z2
線の両端の点A、Bから点Pまでの距離を求めます。AB= AP + PBの場合、Pは線分ABにあります。
AB = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1));
AP = sqrt((x-x1)*(x-x1)+(y-y1)*(y-y1)+(z-z1)*(z-z1));
PB = sqrt((x2-x)*(x2-x)+(y2-y)*(y2-y)+(z2-z)*(z2-z));
if(AB == AP + PB)
return true;
まず ABとAPの外積を取る 。それらが同一直線上にある場合、それは0になります。
この時点では、それはまだBを超えてまたはAの前に伸びるより大きな線上にある可能性があるため、pzがazとbzの間にあるかどうかを確認できるはずです。
これは 重複しているように見える で、実際に、そして回答の1つが言及しているように、それは Beautiful Code にあります。
誰かがインラインバージョンを探す場合:
public static bool PointOnLine2D (this Vector2 p, Vector2 a, Vector2 b, float t = 1E-03f)
{
// ensure points are collinear
var zero = (b.x - a.x) * (p.y - a.y) - (p.x - a.x) * (b.y - a.y);
if (zero > t || zero < -t) return false;
// check if x-coordinates are not equal
if (a.x - b.x > t || b.x - a.x > t)
// ensure x is between a.x & b.x (use tolerance)
return a.x > b.x
? p.x + t > b.x && p.x - t < a.x
: p.x + t > a.x && p.x - t < b.x;
// ensure y is between a.y & b.y (use tolerance)
return a.y > b.y
? p.y + t > b.y && p.y - t < a.y
: p.y + t > a.y && p.y - t < b.y;
}
セグメントはパラメトリック方程式で最もよく定義されています
セグメント上のすべてのポイントについて、次の方程式が成り立ちます:x = x1 +(x2-x1)* p y = y1 +(y2-y1)* p z = z1 +(z2-z1)* p
ここで、pは[0; 1]の数値です
したがって、ポイント座標がこれらの3つの方程式を満たすようなpがある場合、ポイントはこの線上にあります。そして、pは0から1の間であり、ラインセグメント上にもあります。
2DケースのC#コードは次のとおりです。
public static bool PointOnLineSegment(PointD pt1, PointD pt2, PointD pt, double epsilon = 0.001)
{
if (pt.X - Math.Max(pt1.X, pt2.X) > epsilon ||
Math.Min(pt1.X, pt2.X) - pt.X > epsilon ||
pt.Y - Math.Max(pt1.Y, pt2.Y) > epsilon ||
Math.Min(pt1.Y, pt2.Y) - pt.Y > epsilon)
return false;
if (Math.Abs(pt2.X - pt1.X) < epsilon)
return Math.Abs(pt1.X - pt.X) < epsilon || Math.Abs(pt2.X - pt.X) < epsilon;
if (Math.Abs(pt2.Y - pt1.Y) < epsilon)
return Math.Abs(pt1.Y - pt.Y) < epsilon || Math.Abs(pt2.Y - pt.Y) < epsilon;
double x = pt1.X + (pt.Y - pt1.Y) * (pt2.X - pt1.X) / (pt2.Y - pt1.Y);
double y = pt1.Y + (pt.X - pt1.X) * (pt2.Y - pt1.Y) / (pt2.X - pt1.X);
return Math.Abs(pt.X - x) < epsilon || Math.Abs(pt.Y - y) < epsilon;
}
これを使用して、点aとbの間の距離ABを計算します。
static void Main(string[] args)
{
double AB = segment(0, 1, 0, 4);
Console.WriteLine("Length of segment AB: {0}",AB);
}
static double segment (int ax,int ay, int bx, int by)
{
Vector a = new Vector(ax,ay);
Vector b = new Vector(bx,by);
Vector c = (a & b);
return Math.Sqrt(c.X + c.Y);
}
struct Vector
{
public readonly float X;
public readonly float Y;
public Vector(float x, float y)
{
this.X = x;
this.Y = y;
}
public static Vector operator &(Vector a, Vector b)
{
return new Vector((b.X - a.X) * (b.X - a.X), (b.Y - a.Y) * (b.Y - a.Y));
}
}
上記のKonstantinの回答に基づいて、ポイントが実際にFINITEラインセグメント上にあるかどうかを確認するためのCコードを次に示します。これは、水平/垂直の線分を考慮に入れています。これはまた、浮動小数点数を互いに比較するときに、実際には「正確」ではないことを考慮に入れています。ほとんどの場合、デフォルトのイプシロン0.001fで十分です。これは2Dライン用です。 "Z"を追加するのは簡単です。 PointFクラスはGDI +からのもので、基本的には次のとおりです:struct PointF{float X,Y};
お役に立てれば!
#define DEFFLEQEPSILON 0.001
#define FLOAT_EQE(x,v,e)((((v)-(e))<(x))&&((x)<((v)+(e))))
static bool Within(float fl, float flLow, float flHi, float flEp=DEFFLEQEPSILON){
if((fl>flLow) && (fl<flHi)){ return true; }
if(FLOAT_EQE(fl,flLow,flEp) || FLOAT_EQE(fl,flHi,flEp)){ return true; }
return false;
}
static bool PointOnLine(const PointF& ptL1, const PointF& ptL2, const PointF& ptTest, float flEp=DEFFLEQEPSILON){
bool bTestX = true;
const float flX = ptL2.X-ptL1.X;
if(FLOAT_EQE(flX,0.0f,flEp)){
// vertical line -- ptTest.X must equal ptL1.X to continue
if(!FLOAT_EQE(ptTest.X,ptL1.X,flEp)){ return false; }
bTestX = false;
}
bool bTestY = true;
const float flY = ptL2.Y-ptL1.Y;
if(FLOAT_EQE(flY,0.0f,flEp)){
// horizontal line -- ptTest.Y must equal ptL1.Y to continue
if(!FLOAT_EQE(ptTest.Y,ptL1.Y,flEp)){ return false; }
bTestY = false;
}
// found here: http://stackoverflow.com/a/7050309
// x = x1 + (x2 - x1) * p
// y = y1 + (y2 - y1) * p
// solve for p:
const float pX = bTestX?((ptTest.X-ptL1.X)/flX):0.5f;
const float pY = bTestY?((ptTest.Y-ptL1.Y)/flY):0.5f;
return Within(pX,0.0f,1.0f,flEp) && Within(pY,0.0f,1.0f,flEp);
}
V1をベクトル(B-A)、V2 =(p-A)とし、V1とV2の両方を正規化します。
V1 ==(-V2)の場合、ポイントpはライン上にありますが、Aの前にあり、したがってセグメント内にはありません。 V1 == V2の場合、ポイントpは直線上にあります。 (p-A)の長さを取得し、これが(B-A)の長さ以下かどうかを確認します。そうである場合、ポイントはセグメント上にあり、それ以外の場合はBを超えています。
外積(B-A)×(p-A)は、B-Aよりはるかに短くする必要があります。理想的には、外積はゼロですが、有限精度浮動小数点ハードウェアではそうではありません。