Làm cách nào để tôi khái quát thuật toán dòng của Bresenham đến các điểm cuối dấu phẩy động?


12

Tôi đang cố gắng kết hợp hai thứ. Tôi đang viết một trò chơi và tôi cần xác định các ô vuông nằm trên một đường thẳng với các điểm cuối dấu phẩy động.

Lưới máng

Ngoài ra, tôi cần nó để bao gồm tất cả các ô vuông mà nó chạm vào (tức là không chỉ đường của Bresenham mà là đường màu xanh):

Bresenham vs quét toàn bộ

Ai đó có thể cung cấp cho tôi cái nhìn sâu sắc về cách làm điều đó? Giải pháp rõ ràng là sử dụng thuật toán dòng ngây thơ, nhưng có điều gì tối ưu hơn (nhanh hơn) không?


Trong trường hợp liên kết ngoại tuyến, chỉ cần google cho "Thuật toán truyền tải voxel nhanh hơn cho raytracing"
Gustavo Maciel

Câu trả lời:


9

Bạn đang tìm kiếm một thuật toán truyền tải lưới. Bài viết này cho một thực hiện tốt;

Đây là cách triển khai cơ bản trong 2D được tìm thấy trên giấy:

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

Ngoài ra còn có một phiên bản đúc tia 3D trên giấy.

Trong trường hợp các liên kết rots , bạn có thể tìm thấy nhiều gương với tên của nó: Một thuật toán truyền tải voxel nhanh hơn cho raytracing .


Chà, vụng về. Tôi đoán, tôi sẽ chuyển câu trả lời cho bạn và bỏ phiếu ltjax. Bởi vì tôi đã giải quyết dựa trên liên kết của bạn đến bài báo đó.
SmartK8

5

Ý tưởng của Blue là tốt, nhưng việc thực hiện thì hơi vụng về. Trong thực tế, bạn có thể dễ dàng làm điều đó mà không cần sqrt. Giả sử hiện tại bạn loại trừ các trường hợp suy biến ( BeginX==EndX || BeginY==EndY) và chỉ tập trung vào các hướng dòng trong góc phần tư thứ nhất, vì vậy BeginX < EndX && BeginY < EndY. Bạn cũng sẽ phải triển khai một phiên bản cho ít nhất một góc phần tư khác, nhưng nó rất giống với phiên bản cho góc phần tư thứ nhất - bạn chỉ kiểm tra các cạnh khác. Trong mã giả 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;
  }
}

Bây giờ đối với các góc phần tư khác, bạn chỉ cần thay đổi ++cxhoặc++cy và điều kiện vòng lặp. Nếu bạn sử dụng điều này để va chạm, có lẽ bạn phải thực hiện cả 4 phiên bản, nếu không, bạn có thể thoát khỏi hai bằng cách hoán đổi điểm bắt đầu và điểm kết thúc một cách thích hợp.


Thuật toán Gustavo Maciel cung cấp hiệu quả hơn một chút. Nó chỉ xác định Ts đầu tiên và sau đó chỉ cần thêm 1 vào dọc hoặc ngang và dịch chuyển Ts theo kích thước của ô. Nhưng vì anh ấy đã không chuyển đổi nó thành một câu trả lời, tôi sẽ chấp nhận câu trả lời này là câu trả lời gần nhất.
SmartK8

3

Giả định của bạn không nhất thiết phải tìm các ô mà là các dòng nó giao nhau trên lưới này.

Ví dụ: lấy hình ảnh của bạn, chúng ta có thể làm nổi bật không phải các ô, mà là các đường của lưới mà nó đi qua:

RedLines

Điều này sau đó cho thấy rằng nếu nó vượt qua một đường lưới mà các ô ở hai bên của dòng này là những ô được lấp đầy.

Bạn có thể sử dụng thuật toán giao nhau để tìm xem đường dấu phẩy động của bạn có vượt qua các điểm này hay không bằng cách chia tỷ lệ điểm của bạn thành pixel. Nếu bạn có tỷ lệ tọa độ nổi 1,0: 1: pixel thì bạn đã được sắp xếp và bạn chỉ có thể dịch nó trực tiếp. Sử dụng thuật toán giao cắt phân đoạn Đường, bạn có thể kiểm tra xem đường dưới bên trái (1,7) (2,7) của bạn có giao nhau với đường của bạn không (1.3,6.2) (6.51.2.9).http://alienryderflex.com/intersect/

Một số bản dịch từ c sang C # sẽ cần thiết nhưng bạn có thể lấy ý tưởng từ bài báo đó. Tôi sẽ đặt mã bên dưới trong trường hợp ngắt liên kết.

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

Nếu bạn chỉ cần tìm ra khi nào (và ở đâu) các đoạn đường giao nhau, bạn có thể sửa đổi hàm như sau:

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

Xin chào, truyền tải lưới chính xác cho mục đích tối ưu hóa hàng ngàn giao điểm đường trên toàn lưới. Điều này không thể được giải quyết bằng hàng ngàn giao điểm đường. Tôi có một bản đồ trong một trò chơi với các đường mặt đất mà người chơi không thể vượt qua. Có thể có hàng ngàn trong số này. Tôi cần xác định cái nào để tính ngã tư đắt tiền. Để xác định những điều này tôi chỉ muốn tính toán các giao điểm của những người trong dòng chuyển động của người chơi (hoặc ánh sáng từ nguồn sáng). Trong trường hợp của bạn, tôi sẽ cần xác định các giao điểm với các đoạn đường ~ 256x256x2 mỗi vòng. Điều đó sẽ không được tối ưu hóa ở tất cả.
SmartK8

Nhưng vẫn cảm ơn bạn đã trả lời. Về mặt kỹ thuật nó hoạt động và là chính xác. Nhưng chỉ là không khả thi đối với tôi.
SmartK8

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;

Bản trình diễn JS:

Imgur


1
Điều này không thành công đối với tôi do lỗi số dấu phẩy động (vòng lặp sẽ thực hiện một phép lặp bổ sung cho phần nhỏ nhất so với số nguyên tiếp theo sẽ đẩy điểm cuối của dòng vượt ra khỏi vị trí 'end'). Cách khắc phục đơn giản là tính toán dist như một trần ở vị trí đầu tiên để dx, dy được chia cho số lần lặp nguyên của vòng lặp (điều này có nghĩa là bạn có thể mất trần (dist) trong vòng lặp for).
PeteB

0

Hôm nay tôi gặp vấn đề tương tự và tạo ra một núi mì spaghetti khá lớn từ một ngọn đồi nốt ruồi nhưng cuối cùng lại có một thứ có tác dụng: https://github.com/SnpM/Pan-Line-Alacticm .

Từ ReadMe:

Khái niệm cốt lõi của thuật toán này tương tự như của Bresenham ở chỗ nó tăng thêm 1 đơn vị trên một trục và kiểm tra mức tăng trên trục kia. Tuy nhiên, phân số làm cho việc tăng lên trở nên khó khăn hơn đáng kể, và rất nhiều pizza phải được thêm vào. Ví dụ, việc tăng từ X = 0,21 đến X = 1,21 với độ dốc 5 tạo ra một vấn đề phức tạp (phối hợp các mẫu giữa các số khó chịu đó khó dự đoán) nhưng việc tăng từ 1 lên 2 với độ dốc là 5 khiến cho vấn đề trở nên dễ dàng. Mô hình tọa độ giữa các số nguyên rất dễ giải quyết (chỉ là một đường thẳng vuông góc với trục tăng). Để có được bài toán dễ, gia số được bù vào một số nguyên với tất cả các phép tính được thực hiện riêng biệt cho phần phân số. Vì vậy, thay vì bắt đầu tăng dần vào .21,

ReadMe giải thích giải pháp tốt hơn nhiều so với mã. Tôi đang lên kế hoạch sửa đổi nó để giảm bớt đau đầu.

Tôi biết tôi trễ khoảng một năm cho câu hỏi này nhưng tôi hy vọng điều này sẽ đến với những người khác đang tìm kiếm giải pháp cho vấn đề này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.