線分の交差判定

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

線分の交差判定

#1

投稿記事 by みけCAT » 12年前

Windows Vista Home Premium SP2 32ビット
Dev-C++5.2.0.3
gcc4.7.0

線分の交差判定のプログラムを作っています。
参考までに、方程式を使った古いプログラムを載せます。
► スポイラーを表示
さて、問題はこの古いコードではなく、この新しいコードです。

コード:

int cross(int x1,int y1,int x2,int y2,
		int x3,int y3,int x4,int y4) {
	/* ベクトル */ 
	long long b12x,b12y,b34x,b34y;
	long long b24x,b24y;
	long long b13x,b13y,b14x,b14y,b32x,b32y;
	/* ベクトルの外積のz成分 */ 
	long long g1213,g1214,g3431,g3432;
	/* ベクトルの内積 */ 
	long long n1213,n1214,n3431,n3432;
	long long n2123,n2124,n4341,n4342;
	/* 座標を引き算してベクトルを計算する */ 
	b12x=x2-x1;b12y=y2-y1;
	b34x=x4-x3;b34y=y4-y3;
	b13x=x3-x1;b13y=y3-y1;
	b14x=x4-x1;b14y=y4-y1;
	b32x=x2-x3;b32y=y2-y3;
	b24x=x4-x2;b24y=y4-y2;
	/* 平面ベクトルをz成分が0の空間ベクトルとみなし、
	   外積のz成分を計算する */ 
	g1213=b12x*b13y-b13x*b12y;
	g1214=b12x*b14y-b14x*b12y;
	g3431=b34x*(-b13y)-(-b13x)*b34y;
	g3432=b34x*b32y-b32x*b34y;
	/* ベクトルの内積を計算する */ 
	n1213=b12x*b13x+b12y*b13y;
	n1214=b12x*b14x+b12y*b14y;
	n3431=b34x*(-b13x)+b34y*(-b13y);
	n3432=b34x*b32x+b34y*b32y;
	n2123=(-b12x)*(-b32x)+(-b12y)*(-b32y);
	n2124=(-b12x)*b24x+(-b12y)*b24y;
	n4341=(-b34x)*(-b14x)+(-b34y)*(-b14y);
	n4342=(-b34x)*(-b24x)+(-b34y)*(b24y);

	/* ベクトルの外積の符号が違っていれば、交わっている */
	if(
		((g1213<0 && g1214>0) || (g1213>0 && g1214<0)) &&
		((g3431<0 && g3432>0) || (g3431>0 && g3432<0))
	) return 1;
	/* 線分の端が他方の線分上にあるので、交わっている */
	if(
		(g1213==0 && n1213>=0 && n2123>=0) ||
		(g1214==0 && n1214>=0 && n2124>=0) ||
		(g3431==0 && n3431>=0 && n4341>=0) ||
		(g3432==0 && n3432>=0 && n4342>=0)
	) return 1;
	/* 交わっていない */ 
	return 0;
}
※引数の型が古い関数ではdoubleで、新しい関数ではintですが、仕様です

「ベクトルの外積の符号が違っていれば、交わっている」の部分では、
点3と点4が直線12(点1と点2を結ぶ直線)に関して反対側にあり、
かつ点1と点2が直線34に関して反対側にある場合に、「交わっている」と判定しています。

この考え方と実装で本当に正しく動作するでしょうか?

DXライブラリを用いて視覚化したプログラムを添付します。
動かしたい点の数字のキー(テンキーはだめ)を押しながらカーソルキーで点を動かせます。
このとき、シフトキーを押していると1ドットづつ動かせます。

よろしくお願いします。
添付ファイル
crosstest.zip
視覚化プログラム
(1.61 MiB) ダウンロード数: 127 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 線分の交差判定

#2

投稿記事 by みけCAT » 12年前

なかなか返信が来ないので、マルチポストさせていただきました。
http://sasuke.main.jp/bbs/index.cgi?mod ... &rev=&no=0
オフトピック
禁止でしたっけ?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 線分の交差判定

#3

投稿記事 by みけCAT » 12年前

POJのJack Strawsでこの判定コードを使用したところ、Wrong Answerになりました。
しかし、バグを修正してもう一度Submitすると、きちんとAcceptedになりました。
したがって、これで正しいものとみなします。

バグを修正したコード

コード:

int cross(int x1,int y1,int x2,int y2,
		int x3,int y3,int x4,int y4) {
	/* ベクトル */ 
	long long b12x,b12y,b34x,b34y;
	long long b24x,b24y;
	long long b13x,b13y,b14x,b14y,b32x,b32y;
	/* ベクトルの外積のz成分 */ 
	long long g1213,g1214,g3431,g3432;
	/* ベクトルの内積 */ 
	long long n1213,n1214,n3431,n3432;
	long long n2123,n2124,n4341,n4342;
	/* 座標を引き算してベクトルを計算する */ 
	b12x=x2-x1;b12y=y2-y1;
	b34x=x4-x3;b34y=y4-y3;
	b13x=x3-x1;b13y=y3-y1;
	b14x=x4-x1;b14y=y4-y1;
	b32x=x2-x3;b32y=y2-y3;
	b24x=x4-x2;b24y=y4-y2;
	/* 平面ベクトルをz成分が0の空間ベクトルとみなし、
	   外積のz成分を計算する */ 
	g1213=b12x*b13y-b13x*b12y;
	g1214=b12x*b14y-b14x*b12y;
	g3431=b34x*(-b13y)-(-b13x)*b34y;
	g3432=b34x*b32y-b32x*b34y;
	/* ベクトルの内積を計算する */ 
	n1213=b12x*b13x+b12y*b13y;
	n1214=b12x*b14x+b12y*b14y;
	n3431=b34x*(-b13x)+b34y*(-b13y);
	n3432=b34x*b32x+b34y*b32y;
	n2123=(-b12x)*(-b32x)+(-b12y)*(-b32y);
	n2124=(-b12x)*b24x+(-b12y)*b24y;
	n4341=(-b34x)*(-b14x)+(-b34y)*(-b14y);
	n4342=(-b34x)*(-b24x)+(-b34y)*(-b24y);

	/* ベクトルの外積の符号が違っていれば、交わっている */
	if(
		((g1213<0 && g1214>0) || (g1213>0 && g1214<0)) &&
		((g3431<0 && g3432>0) || (g3431>0 && g3432<0))
	) return 1;
	/* 線分の端が他方の線分上にあるので、交わっている */
	if(
		(g1213==0 && n1213>=0 && n2123>=0) ||
		(g1214==0 && n1214>=0 && n2124>=0) ||
		(g3431==0 && n3431>=0 && n4341>=0) ||
		(g3432==0 && n3432>=0 && n4342>=0)
	) return 1;
	/* 交わっていない */ 
	return 0;
}
検証用コード

コード:

#include <stdio.h>

#define UNION_FIND_MAX 12

int uf_table[UNION_FIND_MAX];

void uf_init(void) {
	int i;
	for(i=0;i<UNION_FIND_MAX;i++)uf_table[i]=0;
}

void uf_merge(int a,int b) {
	int next;
	while(uf_table[a]>0) {
		next=uf_table[a]-1;
		if(uf_table[next]>0)uf_table[a]=uf_table[next];
		a=next;
	}
	while(uf_table[b]>0) {
		next=uf_table[b]-1;
		if(uf_table[next]>0)uf_table[b]=uf_table[next];
		b=next;
	}
	if(a!=b)uf_table[a]=b+1;
}

int uf_issame(int a,int b) {
	int next;
	while(uf_table[a]>0) {
		next=uf_table[a]-1;
		if(uf_table[next]>0)uf_table[a]=uf_table[next];
		a=next;
	}
	while(uf_table[b]>0) {
		next=uf_table[b]-1;
		if(uf_table[next]>0)uf_table[b]=uf_table[next];
		b=next;
	}
	return a==b;
}

int cross(int x1,int y1,int x2,int y2,
		int x3,int y3,int x4,int y4) {
	long long b12x,b12y,b34x,b34y;
	long long b24x,b24y;
	long long b13x,b13y,b14x,b14y,b32x,b32y;
	long long g1213,g1214,g3431,g3432;
	long long n1213,n1214,n3431,n3432;
	long long n2123,n2124,n4341,n4342;

	b12x=x2-x1;b12y=y2-y1;
	b34x=x4-x3;b34y=y4-y3;
	b13x=x3-x1;b13y=y3-y1;
	b14x=x4-x1;b14y=y4-y1;
	b32x=x2-x3;b32y=y2-y3;
	b24x=x4-x2;b24y=y4-y2;

	g1213=b12x*b13y-b13x*b12y;
	g1214=b12x*b14y-b14x*b12y;
	g3431=b34x*(-b13y)-(-b13x)*b34y;
	g3432=b34x*b32y-b32x*b34y;

	n1213=b12x*b13x+b12y*b13y;
	n1214=b12x*b14x+b12y*b14y;
	n3431=b34x*(-b13x)+b34y*(-b13y);
	n3432=b34x*b32x+b34y*b32y;
	n2123=(-b12x)*(-b32x)+(-b12y)*(-b32y);
	n2124=(-b12x)*b24x+(-b12y)*b24y;
	n4341=(-b34x)*(-b14x)+(-b34y)*(-b14y);
	n4342=(-b34x)*(-b24x)+(-b34y)*(-b24y);

	if(
		((g1213<0 && g1214>0) || (g1213>0 && g1214<0)) &&
		((g3431<0 && g3432>0) || (g3431>0 && g3432<0))
	) return 1;
	if(
		(g1213==0 && n1213>=0 && n2123>=0) ||
		(g1214==0 && n1214>=0 && n2124>=0) ||
		(g3431==0 && n3431>=0 && n4341>=0) ||
		(g3432==0 && n3432>=0 && n4342>=0)
	) return 1;

	return 0;
}

int n;
int x1[12],y1[12],x2[12],y2[12];

int main(void) {
	int i,j;
	while(1) {
		scanf("%d",&n);
		if(n==0)break;
		for(i=0;i<n;i++)scanf("%d%d%d%d",&x1[i],&y1[i],&x2[i],&y2[i]);
		uf_init();
		for(i=0;i<n;i++) {
			for(j=0;j<n;j++) {
				if(cross(x1[i],y1[i],x2[i],y2[i],
					x1[j],y1[j],x2[j],y2[j]))uf_merge(i,j);
			}
		}
		while(1) {
			scanf("%d%d",&i,&j);
			if(i==0 && j==0)break;
			if(!uf_issame(i-1,j-1))printf("NOT ");
			puts("CONNECTED");
		}
	}
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

閉鎖

“C言語何でも質問掲示板” へ戻る