如何将任意线条投影到屏幕边缘?

时间:2016-01-02 19:40:07

标签: c++ math geometry

我希望能够做到的任何两点(在典型的屏幕坐标系中,X向右增加,Y增加向下)并返回两点,如果该线延长它会碰到屏幕的边缘。我在这里尝试的方法是首先将点转换为象限1(其中Y向上增加),然后计算线的y截距。如果它在屏幕上,则使用0 X值。如果它不在屏幕上,那么该线必须首先穿过X轴...所以在这种情况下Y将是0而x是x截距。然后我将所有点转换为象限3并做同样的事情。然后,同样的程序应返回我的另一端的点。

代码似乎在象限1中起作用,但对象限3返回奇怪的结果。任何想法?

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

float _line_slope( int x1, int y1, int x2, int y2 )
{
    return ((float)(y1-y2)) / ((float)(x1-x2));
}

float _y_intercept( float m, int x, int y )
{
    // y = mx + b
    float mx = m * x;

    // we need to get b by itself, so if mx is positive we need to subtract it from y,
    // if its negative we need to add it to y

    return (mx > 0.0) ? (float)y - mx : (float)y + mx;
}

float _x_intercept( float m, float b )
{
    return -b / m;
}

void _screen_to_quad1( uint32_t w, uint32_t h, int& x, int& y )
{
    y = (h - y);
}

void _quad1_to_screen( uint32_t w, uint32_t h, int& x, int& y )
{
    y = (h - y);
}

void _screen_to_quad3( uint32_t w, uint32_t h, int& x, int& y )
{
    x = (x - w);
    y = -y;
}

void _quad3_to_screen( uint32_t w, uint32_t h, int& x, int& y )
{
    x = (x + w);
    y = abs(y);
}

struct point { int x; int y; };

void _sort_points( int x1, int y1, int x2, int y2, struct point& a, struct point& b )
{
    if( x1 < x2 )
    {
        a.x = x1; a.y = y1;
        b.x = x2; b.y = y2;
    }
    else
    {
        a.x = x2; a.y = y2;
        b.x = x1; b.y = y1;
    }
}

void _project_line( uint32_t w, uint32_t h, int x1, int y1, int x2, int y2, int& ox1, int& oy1, int& ox2, int& oy2 )
{
    struct point a, b;
    _sort_points( x1, y1, x2, y2, a, b );

    _screen_to_quad1( w, h, a.x, a.y );
    _screen_to_quad1( w, h, b.x, b.y );

    {
        float slope = _line_slope( a.x, a.y, b.x, b.y );
        float yint = _y_intercept( slope, a.x, a.y );
        if( yint >= 0 && yint < h )
        {
            ox1 = 0;
            oy1 = yint;
        }
        else
        {
            ox1 = _x_intercept( slope, yint );
            oy1 = 0;
        }
    }

    _quad1_to_screen( w, h, a.x, a.y );
    _quad1_to_screen( w, h, b.x, b.y );
    _quad1_to_screen( w, h, ox1, oy1 );

    _screen_to_quad3( w, h, a.x, a.y );
    _screen_to_quad3( w, h, b.x, b.y );

    {
        float slope = _line_slope( a.x, a.y, b.x, b.y );
        float yint = _y_intercept( slope, a.x, a.y );
        if( yint > -(int)h && yint < 0 )
        {
            ox2 = 0;
            oy2 = yint;
        }
        else
        {
            ox2 = _x_intercept( slope, yint );
            oy2 = 0;
        }
    }

    _quad3_to_screen( w, h, a.x, a.y );
    _quad3_to_screen( w, h, b.x, b.y );
    _quad3_to_screen( w, h, ox2, oy2 );

    printf("ox1=%d, oy1=%d, ox2=%d, oy2=%d\n",ox1,oy1,ox2,oy2);
}

    int main( int argc, char* argv[] )
    {
        int ox1, oy1, ox2, oy2;
        _project_line( 640, 480, 30, 50, 70, 20, ox1, oy1, ox2, oy2 );
        return 0;
    }

以下是ideone上的链接:http://ideone.com/LaejBy

输出如下:

ox1 = 0,oy1 = 73,ox2 = 1316,oy2 = 0

第一个点看起来没问题,第二个点的y对于这一行是正确的,但是第二个点X是偏离的。

1 个答案:

答案 0 :(得分:1)

这是一种概念上更简单的方法。调用点A和B.使用参数形式:

x = xA + t(xB - xA)
y = yA + t(yB - yA)

现在通过求解x方程找到线条触及左右边界xL和xR的t值

tL = (xL - xA) / (xB - xA)
tR = (xR - xA) / (xB - xA)

如果线是垂直的,请跳过此计算。

然后使用y等式对顶部和底部执行相同的操作:

tB = (yB - yA) / (yB - yA)
tT = (yT - yA) / (yB - yA)

如果线条是水平线,请跳过此处。

现在从4个值tL,tR,tB,tT(如果输入是水平或垂直,则为2),一半的值将为负值,一半为正值(为什么?)。在每种情况下选择最小的绝对值。这将直接告诉您交叉点的边界:左,右,底,顶部基于相应的t值。由此,每个交叉点(x或y)的一个坐标是显而易见的。要找到另一个,请用相关的原始参数方程代替。