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

How can I project an arbitrary line to the edges of the screen?

本文关键字:屏幕 边缘 投影 任意线      更新时间:2023-10-16

我想做的是给任意两个点(在典型的屏幕坐标系中,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=%dn",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对这条线来说是正确的,但第二个点差了很多。

有一种概念上更简单的方法。调用点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)的一个坐标是显而易见的。要找到另一个,请代入相关的原始参数方程。