回答:
グリッドトラバーサルアルゴリズムを探しています。このペーパーは適切な実装を提供します。
以下は、論文で見つかった2Dの基本的な実装です。
loop {
if(tMaxX < tMaxY) {
tMaxX= tMaxX + tDeltaX;
X= X + stepX;
} else {
tMaxY= tMaxY + tDeltaY;
Y= Y + stepY;
}
NextVoxel(X,Y);
}
紙には3Dレイキャスティングバージョンもあります。
リンクが腐敗した場合、次の名前の多くのミラーを見つけることができます。レイトレーシング用の高速ボクセルトラバーサルアルゴリズム。
Blueのアイデアは優れていますが、実装は少し不器用です。実際、sqrtなしで簡単に実行できます。今のところ、縮退したケース(BeginX==EndX || BeginY==EndY
)を除外し、第1象限の行方向のみに焦点を当てると仮定しますBeginX < EndX && BeginY < EndY
。少なくとも1つの他の象限のバージョンも実装する必要がありますが、それは最初の象限のバージョンと非常に似ています-他のエッジのみをチェックします。C'ish擬似コードの場合:
int cx = floor(BeginX); // Begin/current cell coords
int cy = floor(BeginY);
int ex = floor(EndX); // End cell coords
int ey = floor(EndY);
// Delta or direction
double dx = EndX-BeginX;
double dy = EndY-BeginY;
while (cx < ex && cy < ey)
{
// find intersection "time" in x dir
float t0 = (ceil(BeginX)-BeginX)/dx;
float t1 = (ceil(BeginY)-BeginY)/dy;
visit_cell(cx, cy);
if (t0 < t1) // cross x boundary first=?
{
++cx;
BeginX += t0*dx;
BeginY += t0*dy;
}
else
{
++cy;
BeginX += t1*dx;
BeginY += t1*dy;
}
}
ここで、他の象限については、++cx
or ++cy
およびループ条件を変更するだけです。これを衝突に使用する場合は、おそらく4つのバージョンすべてを実装する必要があります。それ以外の場合は、開始点と終了点を適切に交換することで2つのバージョンを回避できます。
あなたの仮定は、必ずしもセルを見つけることではなく、このグリッド上で交差する線を見つけることです。
たとえば、画像を撮影する場合、セルではなく、セルが交差するグリッドの行を強調表示できます。
これは、グリッド線と交差する場合、この線の両側のセルが塗りつぶされていることを示しています。
交差アルゴリズムを使用して、ポイントをピクセルにスケーリングすることにより、浮動小数点線がこれらを交差するかどうかを確認できます。浮動座標とピクセルの比率が1.0:1の場合、並べ替えられ、直接変換できます。ラインセグメント交差アルゴリズムを使用して、左下のライン(1,7)(2,7)がライン(1.3,6.2)(6.51,2.9)と交差するかどうかを確認できます。http://alienryderflex.com/intersect/
cからC#への翻訳が必要になりますが、その論文からアイデアを得ることができます。リンクが壊れた場合に備えて、以下のコードを配置します。
// public domain function by Darel Rex Finley, 2006
// Determines the intersection point of the line defined by points A and B with the
// line 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.
bool lineIntersection(
double Ax, double Ay,
double Bx, double By,
double Cx, double Cy,
double Dx, double Dy,
double *X, double *Y) {
double distAB, theCos, theSin, newX, ABpos ;
// Fail if either line is undefined.
if (Ax==Bx && Ay==By || Cx==Dx && Cy==Dy) return NO;
// (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=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 the lines are parallel.
if (Cy==Dy) return NO;
// (3) Discover the position of the intersection point along line A-B.
ABpos=Dx+(Cx-Dx)*Dy/(Dy-Cy);
// (4) Apply the discovered position to line A-B in the original coordinate system.
*X=Ax+ABpos*theCos;
*Y=Ay+ABpos*theSin;
// Success.
return YES; }
線分が交差するタイミング(および場所)のみを調べる必要がある場合は、次のように関数を変更できます。
// public domain function by Darel Rex Finley, 2006
// 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.
bool lineSegmentIntersection(
double Ax, double Ay,
double Bx, double By,
double Cx, double Cy,
double Dx, double Dy,
double *X, double *Y) {
double distAB, theCos, theSin, newX, ABpos ;
// Fail if either line segment is zero-length.
if (Ax==Bx && Ay==By || Cx==Dx && Cy==Dy) return NO;
// Fail if the segments share an end-point.
if (Ax==Cx && Ay==Cy || Bx==Cx && By==Cy
|| Ax==Dx && Ay==Dy || Bx==Dx && By==Dy) {
return NO; }
// (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=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. && Dy<0. || Cy>=0. && Dy>=0.) return NO;
// (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. || ABpos>distAB) return NO;
// (4) Apply the discovered position to line A-B in the original coordinate system.
*X=Ax+ABpos*theCos;
*Y=Ay+ABpos*theSin;
// Success.
return YES; }
float difX = end.x - start.x;
float difY = end.y - start.y;
float dist = abs(difX) + abs(difY);
float dx = difX / dist;
float dy = difY / dist;
for (int i = 0, int x, int y; i <= ceil(dist); i++) {
x = floor(start.x + dx * i);
y = floor(start.y + dy * i);
draw(x,y);
}
return true;
JSデモ:
私は今日、同じ問題に遭遇したとモル丘のうち、スパゲッティのかなり大きな山を作ったが、作品その何かになってしまった:https://github.com/SnpM/Pan-Line-Algorithm。
ReadMeから:
このアルゴリズムの中心概念は、一方の軸で1単位ずつ増加し、他方の軸で増加をテストするという点で、ブレゼンハムに似ています。ただし、分数を増やすことはかなり難しくなり、多くのピザを追加する必要がありました。たとえば、X = .21からX = 1.21に5の勾配で増分すると、複雑な問題が発生します(これらの厄介な数字の間の座標パターン予測するのは困難です)が、5の勾配で1から2に増やすと、問題が発生しやすくなります。整数間の座標パターンは非常に簡単に解決できます(増分軸に垂直な線のみ)。簡単な問題を得るために、増分は整数値にオフセットされ、すべての計算は小数部分に対して個別に行われます。.21で増分を開始する代わりに、
ReadMeは、コードよりもはるかに優れたソリューションを説明しています。頭痛を誘発しないように修正する予定です。
私はこの質問に1年ほど遅れていることを知っていますが、これがこの問題の解決策を探している他の人に届くことを願っています。