2つの線が交差するかどうか、また交差する場合はどのx、y点で交差するかをどのように判断しますか?
2つの線が交差するかどうか、また交差する場合はどのx、y点で交差するかをどのように判断しますか?
回答:
ベクトルのクロス積を使用するこの問題への素晴らしいアプローチがあります。2次元ベクトルクロス積v × wをv x w y − v y w xと定義します。
2つの線分セグメントがpからp + rまで、およびqからq + sまでと仮定します。次に、1行目の任意の点はp + t r(スカラーパラメーター tの場合)として、2行目の任意の点はq + u s(スカラーパラメーターuの場合)として表現できます 。
2つの線は、次のようなtとuが見つかると交差します。
p + t r = q + u s
両側をsで交差させ、
(p + t r)× s =(q + u s)× s
そしてs × s = 0なので、これは
t (r × s)=(q − p)× s
したがって、tについて解く:
t =(q − p)× s /(r × s)
同様に、uを解くことができます。
(p + t r)× r =(q + u s)× r
u (s × r)=(p − q)× r
u =(p − q)× r /(s × r)
計算ステップの数を減らすには、これを次のように書き直すと便利です(s × r = − r × sである)。
u =(q − p)× r /(r × s)
現在、4つのケースがあります。
もしr × s = 0及び(Q - P)× R = 0、2本の線が同一直線上にあります。
この場合、2番目のセグメントの終点(qおよびq + s)を、最初の線分の方程式( p + t r)。
t 0 =(q − p)・ r /(r ・ r)
t 1 =(q + s − p)・ r /(r ・ r)= t 0 + s ・ r /(r ・ r)
tの間隔が 0とT 1つの交差区間[0、1]次に、線分が同一直線上と重複しています。そうでなければ、それらは同一線上にあり、互いに素です。
sとrが反対方向を指している場合、s ・ r <0なので、チェックされる間隔は[ t 0、t 1 ] ではなく[ t 1、t 0 ]であることに注意してください。
場合、R × S = 0及び(Q - P)× R ≠0、2本の線が平行と非交差です。
もしR × S ≠0かつ0≤ T ≤1,0≤ U ≤1、2つの線分は、点で接するP + T R = Q + U S。
そうでない場合、2つの線分は平行ではありませんが交差しません。
クレジット:この方法は、Ronald Goldmanの記事「3スペースでの2本の線の交差」のGraphics Gems、304ページに掲載されている3D線交差アルゴリズムの2次元の特殊化です。3次元では、通常のケースは線は歪んでいます(平行でも交差でもありません)。この場合、メソッドは2つの線の最接近点を示します。
/ (r × s)
ますが、(r × s)
右、ベクトルのですか?ベクトル(0, 0, rx * sy - ry * sx)
。左側も同様にz軸に平行なベクトルです。では、zコンポーネントを他のzコンポーネントで除算するだけですか?tの式は実際に|(q − p) × s| / |(r × s)|
ですか?
FWIW、次の関数(C)は、線の交点を検出し、交点を決定します。これは、Andre LeMotheの「Windows Game Programming Gurusのトリック」のアルゴリズムに基づいています。これは、他の回答におけるいくつかのアルゴリズム(Garethのアルゴリズムなど)と似ています。LeMotheはその後、クラマーの法則(私に尋ねないでください)を使用して、方程式自体を解きます。
私の微弱な小惑星クローンで機能することを証明でき、Elemental、Dan、Wodzuによる他の回答で説明されているエッジのケースを正しく処理しているようです。また、乗算と除算で平方根がないため、KingNestorが投稿したコードよりもおそらく高速です。
私の場合は問題になりませんでしたが、ゼロで除算される可能性はあると思います。とにかくクラッシュを回避するために変更するのは簡単です。
// Returns 1 if the lines intersect, otherwise 0. In addition, if the lines
// intersect the intersection point may be stored in the floats i_x and i_y.
char get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y,
float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
float s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x; s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x; s2_y = p3_y - p2_y;
float s, t;
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
{
// Collision detected
if (i_x != NULL)
*i_x = p0_x + (t * s1_x);
if (i_y != NULL)
*i_y = p0_y + (t * s1_y);
return 1;
}
return 0; // No collision
}
ところで、私はLeMotheの本の中で彼は明らかにアルゴリズムを正しく理解しているが、彼が間違った数のプラグインと間違った計算をしていることを示す具体的な例を言わなければなりません。例えば:
(4 *(4-1)+ 12 *(7-1))/(17 * 4 + 12 * 10)
= 844 / 0.88
= 0.44
それは私を何時間も混乱させた。:(
s
とt
直接二分母と分母の関係をテストします。線が交差していることが確認された場合にのみ、実際にt
(ではなくs
)の値を計算する必要があります。
問題はこの質問に帰着します:AからBへとCからDへの2本の線が交差していますか?次に、それを4回(線と長方形の4辺のそれぞれの間で)質問できます。
これを行うためのベクトル数学は次のとおりです。AからBへの線が問題の線であり、CからDへの線が長方形の線の1つであると想定しています。私の表記はAx
、「Aのx座標」とCy
「Cのy座標」です。そして「*
」はドット積を意味するので、例えばA*B = Ax*Bx + Ay*By
。
E = B-A = ( Bx-Ax, By-Ay )
F = D-C = ( Dx-Cx, Dy-Cy )
P = ( -Ey, Ex )
h = ( (A-C) * P ) / ( F * P )
このh
数字が鍵です。場合h
の間にある0
と1
、ラインが交差し、それ以外の場合はそうではありません。もしF*P
ゼロであり、もちろんあなたは計算を行うことはできませんが、この場合にはラインが平行に、したがって、唯一の交差明白な例です。
正確な交点はC + F*h
です。
もっと楽しく:
場合h
で正確 0
か1
、線が終点で接触する。これは、「交差点」と考えるか、必要に応じて考えることができます。
具体的には h
は、他のラインに正確に接触するためにラインの長さを掛ける必要がある量です。
したがって、の場合h<0
、長方形の線は特定の線の「後ろ」にあり(「方向」は「AからBへ」です)、h>1
長方形の線が特定の線の「前」にあることを意味します。
導出:
AとCは、線の始点を指すベクトルです。EとFは、ラインを形成するAとCの端からのベクトルです。
平面内の任意の2つの非平行線のために、正確に一つのスカラーのペアが存在しなければならないg
とh
、この式が成立するように:
A + E*g = C + F*h
どうして?平行ではない2本の線が交差する必要があるため、両方の線をそれぞれある程度拡大して互いに接触させることができます。
(最初は、これは2つの未知数を含む単一の方程式のように見えます! しかし、これが2Dベクトル方程式であると考えると、これは実際にはx
、y
ます。)
これらの変数の1つを排除する必要があります。簡単な方法は、E
項をゼロにすることです。これを行うには、Eでゼロにドット化するベクトルを使用して、方程式の両側のドット積を計算します。このベクトルは、P
上記で呼び出し、Eの明らかな変換を行いました。
あなたは今持っています:
A*P = C*P + F*P*h
(A-C)*P = (F*P)*h
( (A-C)*P ) / (F*P) = h
A + E*g = C + F*h
2つの線が交差するのは、その方程式の解(平行でない場合)に両方がg
ありh
、0と1の間にある場合(終点での接触をカウントするかどうかに応じて、内部または排他的)です。
私は上記のJasonによってとてもエレガントに説明されたアルゴリズムを実装しようとしました。残念ながら、デバッグで数学を使って作業しているときに、それが機能しない多くのケースを見つけました。
たとえば、点A(10,10)B(20,20)C(10,1)D(1,10)がh = .5を与えることを考慮しますが、これらのセグメントがそれぞれの近くにないことを調べることにより、明らかですその他。
これをグラフにすると、0 <h <1の基準は、インターセプトポイントが存在する場合にCDにあることを示すだけで、そのポイントがABにあるかどうかは何も伝えないことを示しています。クロスポイントがあることを確認するには、変数gに対して対称的な計算を行う必要があり、インターセプトの要件は次のとおりです。0<g <1 AND 0 <h <1
これがギャビンの答えの改善です。marcpのソリューションも同様ですが、どちらも分割を延期します。
これは実際には、Gareth Reesの回答の実用的なアプリケーションでもあることがわかります。これは、2Dのクロス積に相当するものがperp-dot-productであり、このコードが3つ使用しているためです。3Dに切り替えて、クロス積を使用し、最後にsとtの両方を補間すると、3Dの線の間に2つの最も近い点が生じます。とにかく、2Dソリューション:
int get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y,
float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
float s02_x, s02_y, s10_x, s10_y, s32_x, s32_y, s_numer, t_numer, denom, t;
s10_x = p1_x - p0_x;
s10_y = p1_y - p0_y;
s32_x = p3_x - p2_x;
s32_y = p3_y - p2_y;
denom = s10_x * s32_y - s32_x * s10_y;
if (denom == 0)
return 0; // Collinear
bool denomPositive = denom > 0;
s02_x = p0_x - p2_x;
s02_y = p0_y - p2_y;
s_numer = s10_x * s02_y - s10_y * s02_x;
if ((s_numer < 0) == denomPositive)
return 0; // No collision
t_numer = s32_x * s02_y - s32_y * s02_x;
if ((t_numer < 0) == denomPositive)
return 0; // No collision
if (((s_numer > denom) == denomPositive) || ((t_numer > denom) == denomPositive))
return 0; // No collision
// Collision detected
t = t_numer / denom;
if (i_x != NULL)
*i_x = p0_x + (t * s10_x);
if (i_y != NULL)
*i_y = p0_y + (t * s10_y);
return 1;
}
基本的にそれは最後の瞬間まで除算を延期し、特定の計算が行われる前にほとんどのテストを移動し、それによりアーリーアウトを追加します。最後に、ラインが平行な場合に発生するゼロによる除算も回避されます。
また、ゼロとの比較ではなく、イプシロン検定の使用を検討することもできます。平行に非常に近い線は、わずかにずれた結果を生成する可能性があります。これはバグではなく、浮動小数点演算の制限です。
s32_y
それがどのようなものであるかを説明するものの代わりにそのような曖昧な変数名を使用する理由を教えてくれpoint2YDifference
ますか?
同じトピックを検索しましたが、答えに満足できませんでした。そこで、2つの線分が多くの画像と交差するかどうかを確認する方法を非常に詳細に説明する記事を書きました。完全な(そしてテスト済みの)Javaコードがあります。
これが最も重要な部分にトリミングされた記事です:
線分aが線分bと交差するかどうかをチェックするアルゴリズムは、次のようになります。
バウンディングボックスとは何ですか?2つの線分の2つの境界ボックスを次に示します。
両方の境界ボックスに交点がある場合は、1つの点が(0 | 0)になるように線分aを移動します。これで、aで定義された原点を通る線ができました。次に、線分bを同じ方法で移動し、線分bの新しい点が線aの異なる側にあるかどうかを確認します。この場合は、逆に確認してください。この場合も、線分が交差しています。そうでない場合、それらは交差しません。
2つの線分aとbが交差していることがわかります。それがわからない場合は、「質問C」で提供したツールで確認してください。
これで、いくつかのケースを通過し、7年生の数学で解を得ることができます(コードとインタラクティブな例を参照)。
さんがあなたのポイントとしましょうA = (x1, y1)
、ポイントをB = (x2, y2)
、C = (x_3, y_3)
、D = (x_4, y_4)
。最初の行はAB(A!= B)で定義され、2行目はCD(C!= D)で定義されます。
function doLinesIntersect(AB, CD) {
if (x1 == x2) {
return !(x3 == x4 && x1 != x3);
} else if (x3 == x4) {
return true;
} else {
// Both lines are not parallel to the y-axis
m1 = (y1-y2)/(x1-x2);
m2 = (y3-y4)/(x3-x4);
return m1 != m2;
}
}
交差するかどうかは、質問Bで確認してください。
線aとbは、各線の2つの点によって定義されます。基本的に、質問Aで使用したのと同じロジックを適用できます。
ここで一度受け入れられた答えは正しくありません(それ以降受け入れられていないので、残念です!)。すべての非交差を正しく削除するわけではありません。当然、動作するように見えるかもしれませんが、特に0と1がhに対して有効であると見なされる場合は、失敗する可能性があります。
次のケースを考えてみましょう:
(4,1)-(5,1)および(0,0)-(0,2)の行
これらは明らかに重ならない垂直線です。
A =(4,1)
B =(5,1)
C =(0,0)
D =(0,2)
E =(5,1)-(4,1)=(-1,0)
F = (0,2)-(0,0)=(0、-2)
P =(0,1)
h =((4,1)-(0,0))ドット(0,1)/((0 、-2)ドット(0,1))= 0
上記の回答によれば、これらの2つの線分は端点(値0および1)で交わります。そのエンドポイントは次のようになります。
(0,0)+(0、-2)* 0 =(0,0)
したがって、2つの線分は(0,0)で交わるように見えます。(0,0)は線CDにありますが、線ABにはありません。では、何が問題になっていますか?その答えは、0と1の値は無効であり、エンドポイントの交差を正しく予測するために時々発生するだけであるということです。1本の線の延長(他の線ではない)が線セグメントと交わる場合、アルゴリズムは線セグメントの交差を予測しますが、これは正しくありません。AB対CDでテストし、次にCD対ABでテストすることにより、この問題は解消されると思います。両方が0と1の間に収まる場合にのみ、それらは交差していると言えます。
エンドポイントを予測する必要がある場合は、ベクトル外積法を使用することをお勧めします。
-ダン
iMalcの答えのPythonバージョン:
def find_intersection( p0, p1, p2, p3 ) :
s10_x = p1[0] - p0[0]
s10_y = p1[1] - p0[1]
s32_x = p3[0] - p2[0]
s32_y = p3[1] - p2[1]
denom = s10_x * s32_y - s32_x * s10_y
if denom == 0 : return None # collinear
denom_is_positive = denom > 0
s02_x = p0[0] - p2[0]
s02_y = p0[1] - p2[1]
s_numer = s10_x * s02_y - s10_y * s02_x
if (s_numer < 0) == denom_is_positive : return None # no collision
t_numer = s32_x * s02_y - s32_y * s02_x
if (t_numer < 0) == denom_is_positive : return None # no collision
if (s_numer > denom) == denom_is_positive or (t_numer > denom) == denom_is_positive : return None # no collision
# collision detected
t = t_numer / denom
intersection_point = [ p0[0] + (t * s10_x), p0[1] + (t * s10_y) ]
return intersection_point
denom = float(...)
2つのラインセグメントの正しい交点を見つけることは、多くのエッジケースを伴う重要なタスクです。これは、Javaで十分に文書化され、機能し、テストされたソリューションです。
本質的に、2つのラインセグメントの交点を見つけると、次の3つのことが起こります。
セグメントが交差していません
ユニークな交差点があります
交差点は別のセグメントです
注:コードでは、x1 = x2およびy1 = y2のラインセグメント(x1、y1)、(x2、y2)が有効なラインセグメントであると想定しています。数学的に言えば、線分は別個の点で構成されますが、完全を期すために、この実装では線分を点にできます。
コードは私のgithubリポジトリから取得されます
/**
* This snippet finds the intersection of two line segments.
* The intersection may either be empty, a single point or the
* intersection is a subsegment there's an overlap.
*/
import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.util.ArrayList;
import java.util.List;
public class LineSegmentLineSegmentIntersection {
// Small epsilon used for double value comparison.
private static final double EPS = 1e-5;
// 2D Point class.
public static class Pt {
double x, y;
public Pt(double x, double y) {
this.x = x;
this.y = y;
}
public boolean equals(Pt pt) {
return abs(x - pt.x) < EPS && abs(y - pt.y) < EPS;
}
}
// Finds the orientation of point 'c' relative to the line segment (a, b)
// Returns 0 if all three points are collinear.
// Returns -1 if 'c' is clockwise to segment (a, b), i.e right of line formed by the segment.
// Returns +1 if 'c' is counter clockwise to segment (a, b), i.e left of line
// formed by the segment.
public static int orientation(Pt a, Pt b, Pt c) {
double value = (b.y - a.y) * (c.x - b.x) -
(b.x - a.x) * (c.y - b.y);
if (abs(value) < EPS) return 0;
return (value > 0) ? -1 : +1;
}
// Tests whether point 'c' is on the line segment (a, b).
// Ensure first that point c is collinear to segment (a, b) and
// then check whether c is within the rectangle formed by (a, b)
public static boolean pointOnLine(Pt a, Pt b, Pt c) {
return orientation(a, b, c) == 0 &&
min(a.x, b.x) <= c.x && c.x <= max(a.x, b.x) &&
min(a.y, b.y) <= c.y && c.y <= max(a.y, b.y);
}
// Determines whether two segments intersect.
public static boolean segmentsIntersect(Pt p1, Pt p2, Pt p3, Pt p4) {
// Get the orientation of points p3 and p4 in relation
// to the line segment (p1, p2)
int o1 = orientation(p1, p2, p3);
int o2 = orientation(p1, p2, p4);
int o3 = orientation(p3, p4, p1);
int o4 = orientation(p3, p4, p2);
// If the points p1, p2 are on opposite sides of the infinite
// line formed by (p3, p4) and conversly p3, p4 are on opposite
// sides of the infinite line formed by (p1, p2) then there is
// an intersection.
if (o1 != o2 && o3 != o4) return true;
// Collinear special cases (perhaps these if checks can be simplified?)
if (o1 == 0 && pointOnLine(p1, p2, p3)) return true;
if (o2 == 0 && pointOnLine(p1, p2, p4)) return true;
if (o3 == 0 && pointOnLine(p3, p4, p1)) return true;
if (o4 == 0 && pointOnLine(p3, p4, p2)) return true;
return false;
}
public static List<Pt> getCommonEndpoints(Pt p1, Pt p2, Pt p3, Pt p4) {
List<Pt> points = new ArrayList<>();
if (p1.equals(p3)) {
points.add(p1);
if (p2.equals(p4)) points.add(p2);
} else if (p1.equals(p4)) {
points.add(p1);
if (p2.equals(p3)) points.add(p2);
} else if (p2.equals(p3)) {
points.add(p2);
if (p1.equals(p4)) points.add(p1);
} else if (p2.equals(p4)) {
points.add(p2);
if (p1.equals(p3)) points.add(p1);
}
return points;
}
// Finds the intersection point(s) of two line segments. Unlike regular line
// segments, segments which are points (x1 = x2 and y1 = y2) are allowed.
public static Pt[] lineSegmentLineSegmentIntersection(Pt p1, Pt p2, Pt p3, Pt p4) {
// No intersection.
if (!segmentsIntersect(p1, p2, p3, p4)) return new Pt[]{};
// Both segments are a single point.
if (p1.equals(p2) && p2.equals(p3) && p3.equals(p4))
return new Pt[]{p1};
List<Pt> endpoints = getCommonEndpoints(p1, p2, p3, p4);
int n = endpoints.size();
// One of the line segments is an intersecting single point.
// NOTE: checking only n == 1 is insufficient to return early
// because the solution might be a sub segment.
boolean singleton = p1.equals(p2) || p3.equals(p4);
if (n == 1 && singleton) return new Pt[]{endpoints.get(0)};
// Segments are equal.
if (n == 2) return new Pt[]{endpoints.get(0), endpoints.get(1)};
boolean collinearSegments = (orientation(p1, p2, p3) == 0) &&
(orientation(p1, p2, p4) == 0);
// The intersection will be a sub-segment of the two
// segments since they overlap each other.
if (collinearSegments) {
// Segment #2 is enclosed in segment #1
if (pointOnLine(p1, p2, p3) && pointOnLine(p1, p2, p4))
return new Pt[]{p3, p4};
// Segment #1 is enclosed in segment #2
if (pointOnLine(p3, p4, p1) && pointOnLine(p3, p4, p2))
return new Pt[]{p1, p2};
// The subsegment is part of segment #1 and part of segment #2.
// Find the middle points which correspond to this segment.
Pt midPoint1 = pointOnLine(p1, p2, p3) ? p3 : p4;
Pt midPoint2 = pointOnLine(p3, p4, p1) ? p1 : p2;
// There is actually only one middle point!
if (midPoint1.equals(midPoint2)) return new Pt[]{midPoint1};
return new Pt[]{midPoint1, midPoint2};
}
/* Beyond this point there is a unique intersection point. */
// Segment #1 is a vertical line.
if (abs(p1.x - p2.x) < EPS) {
double m = (p4.y - p3.y) / (p4.x - p3.x);
double b = p3.y - m * p3.x;
return new Pt[]{new Pt(p1.x, m * p1.x + b)};
}
// Segment #2 is a vertical line.
if (abs(p3.x - p4.x) < EPS) {
double m = (p2.y - p1.y) / (p2.x - p1.x);
double b = p1.y - m * p1.x;
return new Pt[]{new Pt(p3.x, m * p3.x + b)};
}
double m1 = (p2.y - p1.y) / (p2.x - p1.x);
double m2 = (p4.y - p3.y) / (p4.x - p3.x);
double b1 = p1.y - m1 * p1.x;
double b2 = p3.y - m2 * p3.x;
double x = (b2 - b1) / (m1 - m2);
double y = (m1 * b2 - m2 * b1) / (m1 - m2);
return new Pt[]{new Pt(x, y)};
}
}
次に簡単な使用例を示します。
public static void main(String[] args) {
// Segment #1 is (p1, p2), segment #2 is (p3, p4)
Pt p1, p2, p3, p4;
p1 = new Pt(-2, 4); p2 = new Pt(3, 3);
p3 = new Pt(0, 0); p4 = new Pt(2, 4);
Pt[] points = lineSegmentLineSegmentIntersection(p1, p2, p3, p4);
Pt point = points[0];
// Prints: (1.636, 3.273)
System.out.printf("(%.3f, %.3f)\n", point.x, point.y);
p1 = new Pt(-10, 0); p2 = new Pt(+10, 0);
p3 = new Pt(-5, 0); p4 = new Pt(+5, 0);
points = lineSegmentLineSegmentIntersection(p1, p2, p3, p4);
Pt point1 = points[0], point2 = points[1];
// Prints: (-5.000, 0.000) (5.000, 0.000)
System.out.printf("(%.3f, %.3f) (%.3f, %.3f)\n", point1.x, point1.y, point2.x, point2.y);
}
良い説明と明確な解決策がNumeric Recipesシリーズにあることを述べたかっただけです。私は第3版を持っています。答えは1117ページのセクション21.4にあります。異なる命名法の別の解決策は、Marina Gavrilova Reliable Line Section Intersection Testingの論文にあります。彼女の解決策は、私の考えでは、少し単純です。
私の実装は以下です:
bool NuGeometry::IsBetween(const double& x0, const double& x, const double& x1){
return (x >= x0) && (x <= x1);
}
bool NuGeometry::FindIntersection(const double& x0, const double& y0,
const double& x1, const double& y1,
const double& a0, const double& b0,
const double& a1, const double& b1,
double& xy, double& ab) {
// four endpoints are x0, y0 & x1,y1 & a0,b0 & a1,b1
// returned values xy and ab are the fractional distance along xy and ab
// and are only defined when the result is true
bool partial = false;
double denom = (b0 - b1) * (x0 - x1) - (y0 - y1) * (a0 - a1);
if (denom == 0) {
xy = -1;
ab = -1;
} else {
xy = (a0 * (y1 - b1) + a1 * (b0 - y1) + x1 * (b1 - b0)) / denom;
partial = NuGeometry::IsBetween(0, xy, 1);
if (partial) {
// no point calculating this unless xy is between 0 & 1
ab = (y1 * (x0 - a1) + b1 * (x1 - x0) + y0 * (a1 - x1)) / denom;
}
}
if ( partial && NuGeometry::IsBetween(0, ab, 1)) {
ab = 1-ab;
xy = 1-xy;
return true;
} else return false;
}
上記のソリューションはたくさんありますが、以下のソリューションはかなりシンプルで理解しやすいと思います。
2つのセグメントVector ABとVector CDが交差するのは、
より具体的には、2つのトリプルa、c、dとb、c、dの1つが反時計回りの順序である場合に限り、aとbはセグメントCDの反対側にあります。
Intersect(a, b, c, d)
if CCW(a, c, d) == CCW(b, c, d)
return false;
else if CCW(a, b, c) == CCW(a, b, d)
return false;
else
return true;
ここでCCWは反時計回りを表し、ポイントの方向に基づいてtrue / falseを返します。
出典:http : //compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf ページ2
CCW
テストはどのように定義されていますか?外積のサイン付き?
ガレス・リースの回答に基づく
const AGKLine AGKLineZero = (AGKLine){(CGPoint){0.0, 0.0}, (CGPoint){0.0, 0.0}};
AGKLine AGKLineMake(CGPoint start, CGPoint end)
{
return (AGKLine){start, end};
}
double AGKLineLength(AGKLine l)
{
return CGPointLengthBetween_AGK(l.start, l.end);
}
BOOL AGKLineIntersection(AGKLine l1, AGKLine l2, CGPoint *out_pointOfIntersection)
{
// http://stackoverflow.com/a/565282/202451
CGPoint p = l1.start;
CGPoint q = l2.start;
CGPoint r = CGPointSubtract_AGK(l1.end, l1.start);
CGPoint s = CGPointSubtract_AGK(l2.end, l2.start);
double s_r_crossProduct = CGPointCrossProductZComponent_AGK(r, s);
double t = CGPointCrossProductZComponent_AGK(CGPointSubtract_AGK(q, p), s) / s_r_crossProduct;
double u = CGPointCrossProductZComponent_AGK(CGPointSubtract_AGK(q, p), r) / s_r_crossProduct;
if(t < 0 || t > 1.0 || u < 0 || u > 1.0)
{
if(out_pointOfIntersection != NULL)
{
*out_pointOfIntersection = CGPointZero;
}
return NO;
}
else
{
if(out_pointOfIntersection != NULL)
{
CGPoint i = CGPointAdd_AGK(p, CGPointMultiply_AGK(r, t));
*out_pointOfIntersection = i;
}
return YES;
}
}
CGFloat CGPointCrossProductZComponent_AGK(CGPoint v1, CGPoint v2)
{
return v1.x * v2.y - v1.y * v2.x;
}
CGPoint CGPointSubtract_AGK(CGPoint p1, CGPoint p2)
{
return (CGPoint){p1.x - p2.x, p1.y - p2.y};
}
CGPoint CGPointAdd_AGK(CGPoint p1, CGPoint p2)
{
return (CGPoint){p1.x + p2.x, p1.y + p2.y};
}
CGFloat CGPointCrossProductZComponent_AGK(CGPoint v1, CGPoint v2)
{
return v1.x * v2.y - v1.y * v2.x;
}
CGPoint CGPointMultiply_AGK(CGPoint p1, CGFloat factor)
{
return (CGPoint){p1.x * factor, p1.y * factor};
}
関数と構造体の多くはプライベートですが、何が起こっているかを簡単に知ることができるはずです。これはこのリポジトリで公開されていますhttps://github.com/hfossli/AGGeometryKit/
これは私にとってはうまく機能しています。ここから撮影。
// calculates intersection and checks for parallel lines.
// also checks that the intersection point is actually on
// the line segment p1-p2
Point findIntersection(Point p1,Point p2,
Point p3,Point p4) {
float xD1,yD1,xD2,yD2,xD3,yD3;
float dot,deg,len1,len2;
float segmentLen1,segmentLen2;
float ua,ub,div;
// calculate differences
xD1=p2.x-p1.x;
xD2=p4.x-p3.x;
yD1=p2.y-p1.y;
yD2=p4.y-p3.y;
xD3=p1.x-p3.x;
yD3=p1.y-p3.y;
// calculate the lengths of the two lines
len1=sqrt(xD1*xD1+yD1*yD1);
len2=sqrt(xD2*xD2+yD2*yD2);
// calculate angle between the two lines.
dot=(xD1*xD2+yD1*yD2); // dot product
deg=dot/(len1*len2);
// if abs(angle)==1 then the lines are parallell,
// so no intersection is possible
if(abs(deg)==1) return null;
// find intersection Pt between two lines
Point pt=new Point(0,0);
div=yD2*xD1-xD2*yD1;
ua=(xD2*yD3-yD2*xD3)/div;
ub=(xD1*yD3-yD1*xD3)/div;
pt.x=p1.x+ua*xD1;
pt.y=p1.y+ua*yD1;
// calculate the combined length of the two segments
// between Pt-p1 and Pt-p2
xD1=pt.x-p1.x;
xD2=pt.x-p2.x;
yD1=pt.y-p1.y;
yD2=pt.y-p2.y;
segmentLen1=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);
// calculate the combined length of the two segments
// between Pt-p3 and Pt-p4
xD1=pt.x-p3.x;
xD2=pt.x-p4.x;
yD1=pt.y-p3.y;
yD2=pt.y-p4.y;
segmentLen2=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);
// if the lengths of both sets of segments are the same as
// the lenghts of the two lines the point is actually
// on the line segment.
// if the point isn’t on the line, return null
if(abs(len1-segmentLen1)>0.01 || abs(len2-segmentLen2)>0.01)
return null;
// return the valid intersection
return pt;
}
class Point{
float x,y;
Point(float x, float y){
this.x = x;
this.y = y;
}
void set(float x, float y){
this.x = x;
this.y = y;
}
}
私はこれらの答えのいくつかを試しましたが、それらは私(申し訳ありませんが)にはうまくいきませんでした。もう少しネット検索した後、私はこれを見つけました。
彼のコードを少し変更して、交点を返すか、交点が見つからない場合は-1、-1を返すこの関数を用意しました。
Public Function intercetion(ByVal ax As Integer, ByVal ay As Integer, ByVal bx As Integer, ByVal by As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal dx As Integer, ByVal dy As Integer) As Point
'// Determines the intersection point of the line segment defined by points A and B
'// with the line segment defined by points C and D.
'//
'// Returns YES if the intersection point was found, and stores that point in X,Y.
'// Returns NO if there is no determinable intersection point, in which case X,Y will
'// be unmodified.
Dim distAB, theCos, theSin, newX, ABpos As Double
'// Fail if either line segment is zero-length.
If ax = bx And ay = by Or cx = dx And cy = dy Then Return New Point(-1, -1)
'// Fail if the segments share an end-point.
If ax = cx And ay = cy Or bx = cx And by = cy Or ax = dx And ay = dy Or bx = dx And by = dy Then Return New Point(-1, -1)
'// (1) Translate the system so that point A is on the origin.
bx -= ax
by -= ay
cx -= ax
cy -= ay
dx -= ax
dy -= ay
'// Discover the length of segment A-B.
distAB = Math.Sqrt(bx * bx + by * by)
'// (2) Rotate the system so that point B is on the positive X axis.
theCos = bx / distAB
theSin = by / distAB
newX = cx * theCos + cy * theSin
cy = cy * theCos - cx * theSin
cx = newX
newX = dx * theCos + dy * theSin
dy = dy * theCos - dx * theSin
dx = newX
'// Fail if segment C-D doesn't cross line A-B.
If cy < 0 And dy < 0 Or cy >= 0 And dy >= 0 Then Return New Point(-1, -1)
'// (3) Discover the position of the intersection point along line A-B.
ABpos = dx + (cx - dx) * dy / (dy - cy)
'// Fail if segment C-D crosses line A-B outside of segment A-B.
If ABpos < 0 Or ABpos > distAB Then Return New Point(-1, -1)
'// (4) Apply the discovered position to line A-B in the original coordinate system.
'*X=Ax+ABpos*theCos
'*Y=Ay+ABpos*theSin
'// Success.
Return New Point(ax + ABpos * theCos, ay + ABpos * theSin)
End Function
Gatin の回答にはいくらか興味があるようで、cortijon がコメントで javascriptバージョンを提案し、iMalcは計算が少し少ないバージョンを提供しましたです。さまざまなコード提案の欠点を指摘する人もいれば、一部のコード提案の効率性についてコメントする人もいます。
Gavinの答えを介してiMalcが提供するアルゴリズムは、現在JavaScriptプロジェクトで使用しているものであり、誰かを助けることができる場合は、ここでクリーンアップされたバージョンを提供したかっただけです。
// Some variables for reuse, others may do this differently
var p0x, p1x, p2x, p3x, ix,
p0y, p1y, p2y, p3y, iy,
collisionDetected;
// do stuff, call other functions, set endpoints...
// note: for my purpose I use |t| < |d| as opposed to
// |t| <= |d| which is equivalent to 0 <= t < 1 rather than
// 0 <= t <= 1 as in Gavin's answer - results may vary
var lineSegmentIntersection = function(){
var d, dx1, dx2, dx3, dy1, dy2, dy3, s, t;
dx1 = p1x - p0x; dy1 = p1y - p0y;
dx2 = p3x - p2x; dy2 = p3y - p2y;
dx3 = p0x - p2x; dy3 = p0y - p2y;
collisionDetected = 0;
d = dx1 * dy2 - dx2 * dy1;
if(d !== 0){
s = dx1 * dy3 - dx3 * dy1;
if((s <= 0 && d < 0 && s >= d) || (s >= 0 && d > 0 && s <= d)){
t = dx2 * dy3 - dx3 * dy2;
if((t <= 0 && d < 0 && t > d) || (t >= 0 && d > 0 && t < d)){
t = t / d;
collisionDetected = 1;
ix = p0x + t * dx1;
iy = p0y + t * dy1;
}
}
}
};
t = dx2 * dy3 - dx3 * dy2;
...
t/d
に入っています。
crossProduct = (line1XDifference * line2YDifference) - (line2XDifference * line1YDifference)
とscaledResult = crossProduct / dotProduct
?
p1x, p1y
などは、x値とy値でポイントを説明するためのものなのでp1x
、の略語point1x
でもあります。同様d1x
に、私の心では、ギリシャ文字の略語deltaX
か、と言うこともできますdifferenceInX
。(詳細)
この問題にはもっと簡単な解決策があると思います。今日は別のアイデアを思いつきましたが、それは(少なくとも今のところ2Dでは)うまく機能しているようです。あなたがしなければならないのは、2本の線の間の交点を計算し、計算された交点が両方の線分の境界ボックス内にあるかどうかを確認することです。そうである場合、線分は交差します。それでおしまい。
編集:
これは私が交差点を計算する方法です(私はこのコードスニペットをどこで見つけたかもうわかりません)
Point3D
から来た
System.Windows.Media.Media3D
public static Point3D? Intersection(Point3D start1, Point3D end1, Point3D start2, Point3D end2) {
double a1 = end1.Y - start1.Y;
double b1 = start1.X - end1.X;
double c1 = a1 * start1.X + b1 * start1.Y;
double a2 = end2.Y - start2.Y;
double b2 = start2.X - end2.X;
double c2 = a2 * start2.X + b2 * start2.Y;
double det = a1 * b2 - a2 * b1;
if (det == 0) { // lines are parallel
return null;
}
double x = (b2 * c1 - b1 * c2) / det;
double y = (a1 * c2 - a2 * c1) / det;
return new Point3D(x, y, 0.0);
}
これは私の(答えのために簡略化した)BoundingBoxクラスです。
public class BoundingBox {
private Point3D min = new Point3D();
private Point3D max = new Point3D();
public BoundingBox(Point3D point) {
min = point;
max = point;
}
public Point3D Min {
get { return min; }
set { min = value; }
}
public Point3D Max {
get { return max; }
set { max = value; }
}
public bool Contains(BoundingBox box) {
bool contains =
min.X <= box.min.X && max.X >= box.max.X &&
min.Y <= box.min.Y && max.Y >= box.max.Y &&
min.Z <= box.min.Z && max.Z >= box.max.Z;
return contains;
}
public bool Contains(Point3D point) {
return Contains(new BoundingBox(point));
}
}
このソリューションは役立つかもしれません
public static float GetLineYIntesept(PointF p, float slope)
{
return p.Y - slope * p.X;
}
public static PointF FindIntersection(PointF line1Start, PointF line1End, PointF line2Start, PointF line2End)
{
float slope1 = (line1End.Y - line1Start.Y) / (line1End.X - line1Start.X);
float slope2 = (line2End.Y - line2Start.Y) / (line2End.X - line2Start.X);
float yinter1 = GetLineYIntesept(line1Start, slope1);
float yinter2 = GetLineYIntesept(line2Start, slope2);
if (slope1 == slope2 && yinter1 != yinter2)
return PointF.Empty;
float x = (yinter2 - yinter1) / (slope1 - slope2);
float y = slope1 * x + yinter1;
return new PointF(x, y);
}
上記のクリスの答えをJavaScriptに移植しました。多くの異なる答えを試みた後、彼は正しいポイントを提供しました。自分が必要なポイントを獲得できていなかったことに夢中になっていたと思いました。
function getLineLineCollision(p0, p1, p2, p3) {
var s1, s2;
s1 = {x: p1.x - p0.x, y: p1.y - p0.y};
s2 = {x: p3.x - p2.x, y: p3.y - p2.y};
var s10_x = p1.x - p0.x;
var s10_y = p1.y - p0.y;
var s32_x = p3.x - p2.x;
var s32_y = p3.y - p2.y;
var denom = s10_x * s32_y - s32_x * s10_y;
if(denom == 0) {
return false;
}
var denom_positive = denom > 0;
var s02_x = p0.x - p2.x;
var s02_y = p0.y - p2.y;
var s_numer = s10_x * s02_y - s10_y * s02_x;
if((s_numer < 0) == denom_positive) {
return false;
}
var t_numer = s32_x * s02_y - s32_y * s02_x;
if((t_numer < 0) == denom_positive) {
return false;
}
if((s_numer > denom) == denom_positive || (t_numer > denom) == denom_positive) {
return false;
}
var t = t_numer / denom;
var p = {x: p0.x + (t * s10_x), y: p0.y + (t * s10_y)};
return p;
}
私はいろいろな方法を試しましたが、自分で書くことにしました。だからここにあります:
bool IsBetween (float x, float b1, float b2)
{
return ( ((x >= (b1 - 0.1f)) &&
(x <= (b2 + 0.1f))) ||
((x >= (b2 - 0.1f)) &&
(x <= (b1 + 0.1f))));
}
bool IsSegmentsColliding( POINTFLOAT lineA,
POINTFLOAT lineB,
POINTFLOAT line2A,
POINTFLOAT line2B)
{
float deltaX1 = lineB.x - lineA.x;
float deltaX2 = line2B.x - line2A.x;
float deltaY1 = lineB.y - lineA.y;
float deltaY2 = line2B.y - line2A.y;
if (abs(deltaX1) < 0.01f &&
abs(deltaX2) < 0.01f) // Both are vertical lines
return false;
if (abs((deltaY1 / deltaX1) -
(deltaY2 / deltaX2)) < 0.001f) // Two parallel line
return false;
float xCol = ( ( (deltaX1 * deltaX2) *
(line2A.y - lineA.y)) -
(line2A.x * deltaY2 * deltaX1) +
(lineA.x * deltaY1 * deltaX2)) /
((deltaY1 * deltaX2) - (deltaY2 * deltaX1));
float yCol = 0;
if (deltaX1 < 0.01f) // L1 is a vertical line
yCol = ((xCol * deltaY2) +
(line2A.y * deltaX2) -
(line2A.x * deltaY2)) / deltaX2;
else // L1 is acceptable
yCol = ((xCol * deltaY1) +
(lineA.y * deltaX1) -
(lineA.x * deltaY1)) / deltaX1;
bool isCol = IsBetween(xCol, lineA.x, lineB.x) &&
IsBetween(yCol, lineA.y, lineB.y) &&
IsBetween(xCol, line2A.x, line2B.x) &&
IsBetween(yCol, line2A.y, line2B.y);
return isCol;
}
これらの2つの数式に基づいて:(私は直線と他の数式からそれらを簡略化しました)
これはGareth Reeの回答に基づいています。また、ラインセグメントが重複している場合は、それらを返します。C ++でコーディングされたVは、単純なベクトルクラスです。2Dの2つのベクトルの外積が単一のスカラーを返す場合。それは私の学校の自動テストシステムによってテストされ、合格しました。
//Required input point must be colinear with the line
bool on_segment(const V& p, const LineSegment& l)
{
//If a point is on the line, the sum of the vectors formed by the point to the line endpoints must be equal
V va = p - l.pa;
V vb = p - l.pb;
R ma = va.magnitude();
R mb = vb.magnitude();
R ml = (l.pb - l.pa).magnitude();
R s = ma + mb;
bool r = s <= ml + epsilon;
return r;
}
//Compute using vector math
// Returns 0 points if the lines do not intersect or overlap
// Returns 1 point if the lines intersect
// Returns 2 points if the lines overlap, contain the points where overlapping start starts and stop
std::vector<V> intersect(const LineSegment& la, const LineSegment& lb)
{
std::vector<V> r;
//http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
V oa, ob, da, db; //Origin and direction vectors
R sa, sb; //Scalar values
oa = la.pa;
da = la.pb - la.pa;
ob = lb.pa;
db = lb.pb - lb.pa;
if (da.cross(db) == 0 && (ob - oa).cross(da) == 0) //If colinear
{
if (on_segment(lb.pa, la) && on_segment(lb.pb, la))
{
r.push_back(lb.pa);
r.push_back(lb.pb);
dprintf("colinear, overlapping\n");
return r;
}
if (on_segment(la.pa, lb) && on_segment(la.pb, lb))
{
r.push_back(la.pa);
r.push_back(la.pb);
dprintf("colinear, overlapping\n");
return r;
}
if (on_segment(la.pa, lb))
r.push_back(la.pa);
if (on_segment(la.pb, lb))
r.push_back(la.pb);
if (on_segment(lb.pa, la))
r.push_back(lb.pa);
if (on_segment(lb.pb, la))
r.push_back(lb.pb);
if (r.size() == 0)
dprintf("colinear, non-overlapping\n");
else
dprintf("colinear, overlapping\n");
return r;
}
if (da.cross(db) == 0 && (ob - oa).cross(da) != 0)
{
dprintf("parallel non-intersecting\n");
return r;
}
//Math trick db cross db == 0, which is a single scalar in 2D.
//Crossing both sides with vector db gives:
sa = (ob - oa).cross(db) / da.cross(db);
//Crossing both sides with vector da gives
sb = (oa - ob).cross(da) / db.cross(da);
if (0 <= sa && sa <= 1 && 0 <= sb && sb <= 1)
{
dprintf("intersecting\n");
r.push_back(oa + da * sa);
return r;
}
dprintf("non-intersecting, non-parallel, non-colinear, non-overlapping\n");
return r;
}
これは、C#でのラインセグメントの基本的な実装と、対応する交差点検出コードです。これにはと呼ばれる2Dベクトル/ポイント構造体Vector2f
が必要ですが、これをX / Yプロパティを持つ他のタイプに置き換えることができます。また、置き換えることができfloat
てdouble
、より良いスーツがニーズいる場合。
このコードは、.NET物理ライブラリーBoingで使用されています。
public struct LineSegment2f
{
public Vector2f From { get; }
public Vector2f To { get; }
public LineSegment2f(Vector2f @from, Vector2f to)
{
From = @from;
To = to;
}
public Vector2f Delta => new Vector2f(To.X - From.X, To.Y - From.Y);
/// <summary>
/// Attempt to intersect two line segments.
/// </summary>
/// <remarks>
/// Even if the line segments do not intersect, <paramref name="t"/> and <paramref name="u"/> will be set.
/// If the lines are parallel, <paramref name="t"/> and <paramref name="u"/> are set to <see cref="float.NaN"/>.
/// </remarks>
/// <param name="other">The line to attempt intersection of this line with.</param>
/// <param name="intersectionPoint">The point of intersection if within the line segments, or empty..</param>
/// <param name="t">The distance along this line at which intersection would occur, or NaN if lines are collinear/parallel.</param>
/// <param name="u">The distance along the other line at which intersection would occur, or NaN if lines are collinear/parallel.</param>
/// <returns><c>true</c> if the line segments intersect, otherwise <c>false</c>.</returns>
public bool TryIntersect(LineSegment2f other, out Vector2f intersectionPoint, out float t, out float u)
{
var p = From;
var q = other.From;
var r = Delta;
var s = other.Delta;
// t = (q − p) × s / (r × s)
// u = (q − p) × r / (r × s)
var denom = Fake2DCross(r, s);
if (denom == 0)
{
// lines are collinear or parallel
t = float.NaN;
u = float.NaN;
intersectionPoint = default(Vector2f);
return false;
}
var tNumer = Fake2DCross(q - p, s);
var uNumer = Fake2DCross(q - p, r);
t = tNumer / denom;
u = uNumer / denom;
if (t < 0 || t > 1 || u < 0 || u > 1)
{
// line segments do not intersect within their ranges
intersectionPoint = default(Vector2f);
return false;
}
intersectionPoint = p + r * t;
return true;
}
private static float Fake2DCross(Vector2f a, Vector2f b)
{
return a.X * b.Y - a.Y * b.X;
}
}
与えられた2つの線分が交差するかどうかをチェックするC ++プログラム
#include <iostream>
using namespace std;
struct Point
{
int x;
int y;
};
// Given three colinear points p, q, r, the function checks if
// point q lies on line segment 'pr'
bool onSegment(Point p, Point q, Point r)
{
if (q.x <= max(p.x, r.x) && q.x >= min(p.x, r.x) &&
q.y <= max(p.y, r.y) && q.y >= min(p.y, r.y))
return true;
return false;
}
// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are colinear
// 1 --> Clockwise
// 2 --> Counterclockwise
int orientation(Point p, Point q, Point r)
{
// See 10th slides from following link for derivation of the formula
// http://www.dcs.gla.ac.uk/~pat/52233/slides/Geometry1x1.pdf
int val = (q.y - p.y) * (r.x - q.x) -
(q.x - p.x) * (r.y - q.y);
if (val == 0) return 0; // colinear
return (val > 0)? 1: 2; // clock or counterclock wise
}
// The main function that returns true if line segment 'p1q1'
// and 'p2q2' intersect.
bool doIntersect(Point p1, Point q1, Point p2, Point q2)
{
// Find the four orientations needed for general and
// special cases
int o1 = orientation(p1, q1, p2);
int o2 = orientation(p1, q1, q2);
int o3 = orientation(p2, q2, p1);
int o4 = orientation(p2, q2, q1);
// General case
if (o1 != o2 && o3 != o4)
return true;
// Special Cases
// p1, q1 and p2 are colinear and p2 lies on segment p1q1
if (o1 == 0 && onSegment(p1, p2, q1)) return true;
// p1, q1 and p2 are colinear and q2 lies on segment p1q1
if (o2 == 0 && onSegment(p1, q2, q1)) return true;
// p2, q2 and p1 are colinear and p1 lies on segment p2q2
if (o3 == 0 && onSegment(p2, p1, q2)) return true;
// p2, q2 and q1 are colinear and q1 lies on segment p2q2
if (o4 == 0 && onSegment(p2, q1, q2)) return true;
return false; // Doesn't fall in any of the above cases
}
// Driver program to test above functions
int main()
{
struct Point p1 = {1, 1}, q1 = {10, 1};
struct Point p2 = {1, 2}, q2 = {10, 2};
doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";
p1 = {10, 0}, q1 = {0, 10};
p2 = {0, 0}, q2 = {10, 10};
doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";
p1 = {-5, -5}, q1 = {0, 0};
p2 = {1, 1}, q2 = {10, 10};
doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";
return 0;
}
@Gareth Reesの回答に基づいて、Pythonのバージョン:
import numpy as np
def np_perp( a ) :
b = np.empty_like(a)
b[0] = a[1]
b[1] = -a[0]
return b
def np_cross_product(a, b):
return np.dot(a, np_perp(b))
def np_seg_intersect(a, b, considerCollinearOverlapAsIntersect = False):
# /programming/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282
# http://www.codeproject.com/Tips/862988/Find-the-intersection-point-of-two-line-segments
r = a[1] - a[0]
s = b[1] - b[0]
v = b[0] - a[0]
num = np_cross_product(v, r)
denom = np_cross_product(r, s)
# If r x s = 0 and (q - p) x r = 0, then the two lines are collinear.
if np.isclose(denom, 0) and np.isclose(num, 0):
# 1. If either 0 <= (q - p) * r <= r * r or 0 <= (p - q) * s <= * s
# then the two lines are overlapping,
if(considerCollinearOverlapAsIntersect):
vDotR = np.dot(v, r)
aDotS = np.dot(-v, s)
if (0 <= vDotR and vDotR <= np.dot(r,r)) or (0 <= aDotS and aDotS <= np.dot(s,s)):
return True
# 2. If neither 0 <= (q - p) * r = r * r nor 0 <= (p - q) * s <= s * s
# then the two lines are collinear but disjoint.
# No need to implement this expression, as it follows from the expression above.
return None
if np.isclose(denom, 0) and not np.isclose(num, 0):
# Parallel and non intersecting
return None
u = num / denom
t = np_cross_product(v, s) / denom
if u >= 0 and u <= 1 and t >= 0 and t <= 1:
res = b[0] + (s*u)
return res
# Otherwise, the two line segments are not parallel but do not intersect.
return None
t3chb0tの答えに基づいて:
int intersezione_linee(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int& p_x, int& p_y)
{
//L1: estremi (x1,y1)(x2,y2) L2: estremi (x3,y3)(x3,y3)
int d;
d = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
if(!d)
return 0;
p_x = ((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4))/d;
p_y = ((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4))/d;
return 1;
}
int in_bounding_box(int x1, int y1, int x2, int y2, int p_x, int p_y)
{
return p_x>=x1 && p_x<=x2 && p_y>=y1 && p_y<=y2;
}
int intersezione_segmenti(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int& p_x, int& p_y)
{
if (!intersezione_linee(x1,y1,x2,y2,x3,y3,x4,y4,p_x,p_y))
return 0;
return in_bounding_box(x1,y1,x2,y2,p_x,p_y) && in_bounding_box(x3,y3,x4,y4,p_x,p_y);
}
これらのアルゴリズムを「マルチビュージオメトリ」という本から読みました
次のテキストを使用して
'転置記号として
*ドット積として
xをクロス演算子として、演算子として使用する場合
点x_vec =(x、y) 'はax + by + c = 0の直線上にあります
L =(a、b、c) '、点を(x、y、1)'として同次座標として表す
線方程式は次のように書くことができます
(x、y、1)(a、b、c) '= 0またはx' * L = 0
L1 =(a1、b1、c1) '、L2 =(a2、b2、c2)'の2つの行があります
xが点、ベクトルであり、x = L1 x L2(L1クロス積L2)であると仮定します。
注意してください。xは常に2Dの点です。(L1xL2)が3要素ベクトルであり、xが2D座標であると混乱する場合は、同次座標をお読みください。
トリプル積によれば、
L1 *(L1 x L2)= 0、およびL2 *(L1 x L2)= 0、L1、L2同一平面のため
(L1xL2)をベクトルxで置き換えると、L1 * x = 0、L2 * x = 0となります。これは、xがL1とL2の両方にあることを意味し、xは交点です。
ここで、xは同次座標です。xの最後の要素がゼロの場合、L1とL2が平行であることを意味します。
多くの回答がすべての計算を1つの関数にまとめています。コードの他の場所で使用するために線の傾き、y切片、またはx切片を計算する必要がある場合は、それらの計算を重複して行うことになります。それぞれの関数を分離し、明白な変数名を使用し、コードをコメントしてわかりやすくしました。ラインがエンドポイントを超えて無限に交差するかどうかを知る必要があったので、JavaScriptで:
http://jsfiddle.net/skibulk/evmqq00u/
var point_a = {x:0, y:10},
point_b = {x:12, y:12},
point_c = {x:10, y:0},
point_d = {x:0, y:0},
slope_ab = slope(point_a, point_b),
slope_bc = slope(point_b, point_c),
slope_cd = slope(point_c, point_d),
slope_da = slope(point_d, point_a),
yint_ab = y_intercept(point_a, slope_ab),
yint_bc = y_intercept(point_b, slope_bc),
yint_cd = y_intercept(point_c, slope_cd),
yint_da = y_intercept(point_d, slope_da),
xint_ab = x_intercept(point_a, slope_ab, yint_ab),
xint_bc = x_intercept(point_b, slope_bc, yint_bc),
xint_cd = x_intercept(point_c, slope_cd, yint_cd),
xint_da = x_intercept(point_d, slope_da, yint_da),
point_aa = intersect(slope_da, yint_da, xint_da, slope_ab, yint_ab, xint_ab),
point_bb = intersect(slope_ab, yint_ab, xint_ab, slope_bc, yint_bc, xint_bc),
point_cc = intersect(slope_bc, yint_bc, xint_bc, slope_cd, yint_cd, xint_cd),
point_dd = intersect(slope_cd, yint_cd, xint_cd, slope_da, yint_da, xint_da);
console.log(point_a, point_b, point_c, point_d);
console.log(slope_ab, slope_bc, slope_cd, slope_da);
console.log(yint_ab, yint_bc, yint_cd, yint_da);
console.log(xint_ab, xint_bc, xint_cd, xint_da);
console.log(point_aa, point_bb, point_cc, point_dd);
function slope(point_a, point_b) {
var i = (point_b.y - point_a.y) / (point_b.x - point_a.x);
if (i === -Infinity) return Infinity;
if (i === -0) return 0;
return i;
}
function y_intercept(point, slope) {
// Horizontal Line
if (slope == 0) return point.y;
// Vertical Line
if (slope == Infinity)
{
// THE Y-Axis
if (point.x == 0) return Infinity;
// No Intercept
return null;
}
// Angled Line
return point.y - (slope * point.x);
}
function x_intercept(point, slope, yint) {
// Vertical Line
if (slope == Infinity) return point.x;
// Horizontal Line
if (slope == 0)
{
// THE X-Axis
if (point.y == 0) return Infinity;
// No Intercept
return null;
}
// Angled Line
return -yint / slope;
}
// Intersection of two infinite lines
function intersect(slope_a, yint_a, xint_a, slope_b, yint_b, xint_b) {
if (slope_a == slope_b)
{
// Equal Lines
if (yint_a == yint_b && xint_a == xint_b) return Infinity;
// Parallel Lines
return null;
}
// First Line Vertical
if (slope_a == Infinity)
{
return {
x: xint_a,
y: (slope_b * xint_a) + yint_b
};
}
// Second Line Vertical
if (slope_b == Infinity)
{
return {
x: xint_b,
y: (slope_a * xint_b) + yint_a
};
}
// Not Equal, Not Parallel, Not Vertical
var i = (yint_b - yint_a) / (slope_a - slope_b);
return {
x: i,
y: (slope_a * i) + yint_a
};
}