我有一个角度为[0, 2pi] x [0, 2pi]
的2D空间,它具有环状拓扑(水平边缘相互对应,垂直边缘也是如此)。我在这个空间有两个点,我想在这两个点之间绘制一条线段。
在某些情况下,此线段是明显的线段,从一个点到另一个点。在其他情况下,线段应该绕过边缘"而不是走很长的路,通过中间":
+--------+
| |
| A--B |
| |
+--------+
+--------+
| |
|-A B-|
| |
+--------+
虽然这些案例容易处理,但有一个案例对我来说真的很烦人,到目前为止我的代码无法正确处理:
+-----------+
| / |
| B |
| |
| A /|
| / / |
+-----------+
即。如果线绕两个方向缠绕,它有时会缠绕在对面的角落。我不完全确定是否有更多这些棘手的案件。
到目前为止,我唯一能够可靠运行的算法是将中点计算为(A + B) / 2
,同时适当使用模数算术,在此位置绘制一个点,然后递归细分类似地,左和右间隔,直到点之间的距离小于单个像素。显然,这不会很快。
我的另一种方法是(分别针对x和y)检测短距离是直接还是围绕边缘,然后绘制一个或两个线段。这不能正确处理第三种情况,除非该行被分为两部分,并且中点位于示例图像右下角的部分。我不确定如何有效地检测到这一点,或者如何计算中点的位置,因为只是半点中的点并不总是有效,它可能会与其中一个端点一起在边缘处结束,如果它们各自与边缘的距离不相等。
有更好的算法吗?有没有明显的解决方案,我没有看到?我甚至不确定如何谷歌解决这个问题。我不想实现我自己的行光栅化算法,我只想将这个问题分解为Euclidean直线并使用OpenGL或GDI或其他方法绘制它们。
到目前为止我的代码是:
void Draw_WrappedSegment(float f_x0, float f_y0, float f_x1, float f_y1)
{
const float s = 2 * f_pi;
f_x0 = fmod(fmod(f_x0, s) + s, s);
f_y0 = fmod(fmod(f_y0, s) + s, s);
f_x1 = fmod(fmod(f_x1, s) + s, s);
f_y1 = fmod(fmod(f_y1, s) + s, s);
// make sure the coordinates end up being positive and modulo 2pi
float f_ydist0 = fabs(f_y0 - f_y1);
float f_ydist1 = fabs(fmod(f_y0 + s - f_y1, s));
float f_ydist2 = fabs(fmod(f_y1 - f_y0 + s, s));
float f_xdist0 = fabs(f_x0 - f_x1);
float f_xdist1 = fabs(fmod(f_x0 + s - f_x1, s));
float f_xdist2 = fabs(fmod(f_x1 - f_x0 + s, s));
// 0 2pi 4pi
//p1'' | p0 p1 | p0' p1' |
// <---f_dist0--->
// <-f_dist1->
// <-f_dist2->
const float f_epsilon = 1e-3f; // sometimes the modulo causes an error and even though the díst 0 and dist 2 should equal, dist 2 is slightly smaller
if(f_xdist0 <= f_xdist1 + f_epsilon && f_xdist0 <= f_xdist2 + f_epsilon) {
if(f_ydist0 <= f_ydist1 + f_epsilon && f_ydist0 <= f_ydist2 + f_epsilon) {
MoveTo(f_x0, f_y0);
LineTo(f_x1, f_y1); // the "short" way in both directions
} else {
float f_sign = (f_y0 < f_y1)? 1 : -1; // swap the lower and upper edge if the points are not sorted by y
MoveTo(f_x0, f_y0);
LineTo(f_x1, f_y1 - f_sign * s); // from point 0 to the lower edge
MoveTo(f_x1, f_y1);
LineTo(f_x0, f_y0 + f_sign * s); // from point 1 to the upper edge
}
} else {
if(f_ydist0 <= f_ydist1 + f_epsilon && f_ydist0 <= f_ydist2 + f_epsilon) {
float f_sign = (f_x0 < f_x1)? 1 : -1; // swap the left and right edge if the points are not sorted by x
MoveTo(f_x0, f_y0);
LineTo(f_x1 - f_sign * s, f_y1); // from point 0 to the left edge
MoveTo(f_x1, f_y1);
LineTo(f_x0 + f_sign * s, f_y0); // from point 1 to the right edge
} else {
float f_sign_x = (f_x0 < f_x1)? 1 : -1; // swap the left and right edge if the points are not sorted by x
float f_sign_y = (f_y0 < f_y1)? 1 : -1; // swap the lower and upper edge if the points are not sorted by y
MoveTo(f_x0, f_y0);
LineTo(f_x1 - f_sign_x * s, f_y1 - f_sign_y * s); // from point 0 to one edge
MoveTo(f_x1, f_y1);
LineTo(f_x0 + f_sign_x * s, f_y0 + f_sign_y * s); // from point 1 to the other edge
}
}
}
答案 0 :(得分:1)
不要只使用方格[0, 2pi] x [0, 2pi]
,而是尝试使用此方块的九个副本(如tic-tac-toe board)平铺空间[-2pi,4pi] x [-2pi,4pi]
。将A放在中心正方形,然后在九个正方形的每一个中放置B的副本(根据需要将坐标平移±2pi)。选择最接近A的B的副本,然后将A中的行绘制到B的副本。此行在通过正方形时可能有多个段。只需将这些片段“翻译”回中心广场,您就可以获得所需的图表。