Bresenhamのラインアルゴリズムを浮動小数点エンドポイントに一般化するにはどうすればよいですか?


12

私は2つのことを組み合わせようとしています。私はゲームを書いていますが、浮動小数点の端点を持つ線上にあるグリッドの正方形を決定する必要があります。

ライントラフグリッド

さらに、接触するすべてのグリッド正方形(つまり、ブレゼンハムの線だけでなく、青の線)を含める必要があります。

ブレゼンハムvsフルスイープ

誰かが私にそれを行う方法に関する洞察を提供できますか?明白な解決策は、単純なラインアルゴリズムを使用することですが、より最適化された(高速な)ものはありますか?


リンクがオフラインになった場合は、グーグルで「レイトレーシング用の高速ボクセルトラバーサルアルゴリズム」
グスタボマシエル14

回答:


9

グリッドトラバーサルアルゴリズムを探しています。このペーパーは適切な実装を提供します。

以下は、論文で見つかった2Dの基本的な実装です。

loop {
    if(tMaxX < tMaxY) {
        tMaxX= tMaxX + tDeltaX;
        X= X + stepX;
    } else {
        tMaxY= tMaxY + tDeltaY;
        Y= Y + stepY;
    }
    NextVoxel(X,Y);
}

紙には3Dレイキャスティングバージョンもあります。

リンクが腐敗した場合、次の名前の多くのミラーを見つけることができます。レイトレーシング用の高速ボクセルトラバーサルアルゴリズム


まあ、厄介です。答えをあなたに切り替えて、ltjaxに賛成票を投じるでしょう。その論文へのリンクに基づいて解決したからです。
SmartK8 14

5

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;
  }
}

ここで、他の象限については、++cxor ++cyおよびループ条件を変更するだけです。これを衝突に使用する場合は、おそらく4つのバージョンすべてを実装する必要があります。それ以外の場合は、開始点と終了点を適切に交換することで2つのバージョンを回避できます。


Gustavo Macielが提供するアルゴリズムは、もう少し効率的です。最初のTsを決定してから、垂直または水平に1を追加するだけで、セルのサイズだけTsをシフトします。しかし、彼はそれを回答に変換しなかったので、これを最も近い回答として受け入れます。
SmartK8 14

3

あなたの仮定は、必ずしもセルを見つけることではなく、このグリッド上で交差する線を見つけることです。

たとえば、画像を撮影する場合、セルではなく、セルが交差するグリッドの行を強調表示できます。

RedLines

これは、グリッド線と交差する場合、この線の両側のセルが塗りつぶされていることを示しています。

交差アルゴリズムを使用して、ポイントをピクセルにスケーリングすることにより、浮動小数点線がこれらを交差するかどうかを確認できます。浮動座標とピクセルの比率が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; }

こんにちは、グリッドトラバーサルは、グリッド全体の数千のライン交差を最適化するためのものです。これは、数千の交差点では解決できません。プレイヤーが横断できない地上線があるゲームのマップがあります。これらは何千もの可能性があります。高価な交差点を計算する対象を決定する必要があります。これらを決定するために、プレーヤーの動きに沿ったもの(または光源からの光)の交点のみを計算します。あなたの場合、各ラウンドで〜256x256x2の線分との交点を決定する必要があります。それはまったく最適化されません。
SmartK8 14

それでも答えてくれてありがとう。技術的には機能し、正しいです。しかし、私には実現不可能です。
SmartK8 14

3
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デモ:

イグル


1
これは、浮動小数点数値エラーのために失敗しました(ループは、次の整数で最も小さな部分に対して追加の反復を行い、ラインのエンドポイントを「終了」位置を超えてプッシュします)。簡単な修正方法は、最初にdistをceilとして計算し、dx、dyをループの反復回数で除算することです(これは、forループでceil(dist)を失う可能性があることを意味します)。
PeteB

0

私は今日、同じ問題に遭遇したとモル丘のうち、スパゲッティのかなり大きな山を作ったが、作品その何かになってしまった:https://github.com/SnpM/Pan-Line-Algorithm

ReadMeから:

このアルゴリズムの中心概念は、一方の軸で1単位ずつ増加し、他方の軸で増加をテストするという点で、ブレゼンハムに似ています。ただし、分数を増やすことはかなり難しくなり、多くのピザを追加する必要がありました。たとえば、X = .21からX = 1.21に5の勾配で増分すると、複雑な問題が発生します(これらの厄介な数字の間の座標パターン予測するのは困難です)が、5の勾配で1から2に増やすと、問題が発生しやすくなります。整数間の座標パターンは非常に簡単に解決できます(増分軸に垂直な線のみ)。簡単な問題を得るために、増分は整数値にオフセットされ、すべての計算は小数部分に対して個別に行われます。.21で増分を開始する代わりに、

ReadMeは、コードよりもはるかに優れたソリューションを説明しています。頭痛を誘発しないように修正する予定です。

私はこの質問に1年ほど遅れていることを知っていますが、これがこの問題の解決策を探している他の人に届くことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.