首页 文章

在环形包裹的空间上绘制线段

提问于
浏览
1

我有一个包围的角度为 [0, 2pi] x [0, 2pi] 的2D空间,具有类似环形的环形(水平边缘相互对应,垂直边缘也是如此) . 我在这个空间有两点,我想在这两点之间画一条线 .

在某些情况下,此线段是明显的线段,从一个点到另一个点 . 在其他情况下,线段应该“绕过边缘”,而不是“走很长的路,穿过中间”:

+--------+
|        |
|  A--B  |
|        |
+--------+

+--------+
|        |
|-A    B-|
|        |
+--------+

虽然这些案例容易处理,但有一个案例对我来说真的很烦人,到目前为止我的代码无法正确处理:

+-----------+
|        /  |
|       B   |
|           |
|  A       /|
| /       / |
+-----------+

即如果线绕两个方向缠绕,它有时会缠绕在对面的角落 . 我不完全确定是否有更多这些棘手的案件 .

到目前为止,我唯一可靠的算法是将中点计算为 (A + B) / 2 ,同时适当使用模数算术,在此位置绘制一个点,然后类似地递归细分左右间隔,直到距离为止点之间小于一个像素 . 显然,这不会很快 .

我的另一种方法是检测(分别为x和y)短距离是直接还是围绕边缘,然后绘制一个或两个线段 . 这不能正确处理第三种情况,除非该行被分为两部分,并且中点位于示例图像右下角的部分 . 我不确定如何有效地检测到这一点,或者如何计算中点的位置,因为只是半点中的点并不总是起作用,它可能与其中一个 endpoints 一起最终位于边缘,如果它们各自距离边缘的距离不相等 .

有更好的算法吗?有没有明显的解决方案,我没有看到?我甚至不确定如何谷歌这个问题 . 我不想实现我自己的行光栅化算法,我只想将这个问题分解为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
        }
    }
}

1 回答

  • 1

    而不是只使用方形 [0, 2pi] x [0, 2pi] ,尝试用这个方块的九个副本(如tic-tac-toe板)平铺空间 [-2pi,4pi] x [-2pi,4pi] . 将A放在中心正方形,然后在九个正方形的每一个中放置B的副本(根据需要将坐标平移±2pi) . 选择最接近A的B的副本,然后将A中的线绘制到B的副本 . 此行在通过正方形时可能有多个段 . 只需"untranslate"这些段回到中心广场,你就会得到你想要的图表 .

相关问题