在笛卡尔坐标系和屏幕坐标系之间进行转换

19
为了我的游戏,我需要函数来在两个坐标系之间进行转换。虽然这主要是数学问题,但我需要的是C++代码以及一些解释如何解决我的问题。
屏幕坐标:
a)左上角为0,0
b)没有负值
c)右侧增加x(x值越大,点越靠右)
d)底部增加y
笛卡尔2D坐标:
a)中心点为(0,0)
b)存在负值
c)右侧增加x
d)底部减少y(y越少,点越靠下)
我需要一种简单的方法来在两个系统之间进行转换。为此,我认为需要一些知识,如(0,0)[屏幕坐标中的左上角]在笛卡尔坐标系中的位置。
然而,有一个问题,即对于笛卡尔坐标系中的某些点,在将其转换为屏幕坐标后,屏幕坐标中的位置可能为负数,这是不合理的。我不能将屏幕坐标的左上角放在(-inifity,+infinity)的笛卡尔坐标中......
我该怎么解决这个问题?我能想到的唯一解决方案是将屏幕(0,0)放置在笛卡尔(0,0)中,并仅使用笛卡尔系统的第IV象限,但在这种情况下,使用笛卡尔系统是毫无意义的......
我相信有方法可以将屏幕坐标转换为笛卡尔坐标,反之亦然,但在处理负值时,我的思维方式可能存在问题。

屏幕坐标是笛卡尔坐标系吗?它何时变为非笛卡尔坐标系的? - thang
他想要负坐标。 - sgonzalez
@thang,屏幕上的Y轴与笛卡尔坐标系中的不同。 - user1873947
1
我相信你所说的,但我认为笛卡尔只是指带有L2度量的R^2坐标系(http://en.wikipedia.org/wiki/Cartesian_coordinate_system)。 只要翻转/变换是等距的,您可以以任何方式翻转它。 - thang
@thang,你说得对,但我认为这个问题是可以理解的。 - user1873947
5个回答

21

将笛卡尔坐标系转换为屏幕坐标系的基本算法如下:

screenX = cartX + screen_width/2
screenY = screen_height/2 - cartY

但正如您所提到的,笛卡尔空间是无限的,而屏幕空间不是。这可以通过更改屏幕空间和笛卡尔空间之间的分辨率来轻松解决。以上算法使得笛卡尔空间中的1个单位=屏幕空间中的1个单位/像素。如果您允许其他比例,则可以“缩放”您的屏幕空间以覆盖所有必要的笛卡尔空间。

这将改变上述算法为:

screenX = zoom_factor*cartX + screen_width/2
screenY = screen_height/2 - zoom_factor*cartY

现在您可以通过修改缩放因子来处理负数(或过大的)screenX和screenY,直到所有笛卡尔坐标都适合于屏幕。

您还可以允许坐标空间的平移,也就是允许笛卡尔空间的中心偏离屏幕中心。这也有助于使您的缩放因子尽可能紧凑,同时适合于不均匀分布在笛卡尔空间原点周围的数据。

这将改变算法为

screenX = zoom_factor*cartX + screen_width/2 + offsetX
screenY = screen_height/2 - zoom_factor*cartY + offsetY

7

为了进行转换,您必须知道屏幕的尺寸。

转换为笛卡尔坐标:

cartesianx = screenx - screenwidth / 2;
cartesiany = -screeny + screenheight / 2;

转化为屏幕:

screenx = cartesianx + screenwidth / 2;
screeny = -cartesiany + screenheight / 2;

对于屏幕值为负的情况: 不必担心,这个内容将被简单地裁剪,因此用户无法看到。如果这是一个问题,可以添加一些约束条件来防止笛卡尔坐标过大。另一个解决方案是,由于边缘不能是正负无穷,可以缩放坐标(例如,1像素=10笛卡尔),让我们称之为“scalefactor”。现在的方程式如下:
使用缩放因子转换为笛卡尔坐标:
cartesianx = scalefactor*screenx - screenwidth / 2;
cartesiany = -scalefactor*screeny + screenheight / 2;

使用比例因子将其转换为屏幕:

screenx = (cartesianx + screenwidth / 2) / scalefactor;
screeny = (-cartesiany + screenheight / 2) / scalefactor;

5

您需要知道屏幕的宽度和高度。

然后您可以执行以下操作:

cartX =   screenX - (width / 2);
cartY = -(screenY - (height / 2));

并且:

screenX =  cartX + (width / 2);
screenY = -cartY + (height / 2);

3
您总会遇到一个问题,即结果可能超出屏幕--要么是负值,要么是大于可用屏幕大小的值。有时这并不重要:例如,如果您的图形API接受负值并为您剪辑绘图,则不会影响结果。但有时会很重要,对于这些情况,您应该编写一个函数来检查一组屏幕坐标是否在屏幕上。
您还可以编写自己的剪切功能,以尝试对超出屏幕的坐标进行合理处理(例如将负屏幕坐标截断为0,并将太大无法在屏幕上显示的坐标缩小到最大屏幕坐标)。但是,请记住,“合理”取决于您想要做什么,因此最好在实际需要时再定义此类函数。
无论如何,正如其他答案所指出的那样,您可以通过以下方式在坐标系之间进行转换:
cart.x = screen.x - width/2;
cart.y = height/2 - screen.y;

并且

screen.x = cart.x + width/2;
screen.y = height/2 - cart.y;

0

我有一些基于微软文章的Boost C++代码可以提供给你: https://msdn.microsoft.com/en-us/library/jj635757(v=vs.85).aspx

你只需要知道两个屏幕点和两个坐标系中的点,就可以将一个系统中的点转换到另一个系统中。

#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/vector_proxy.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/triangular.hpp>
#include <boost/numeric/ublas/lu.hpp>
#include <boost/numeric/ublas/io.hpp>

 /* Matrix inversion routine.
 Uses lu_factorize and lu_substitute in uBLAS to invert a matrix */
template<class T>
bool InvertMatrix(const boost::numeric::ublas::matrix<T>& input, boost::numeric::ublas::matrix<T>& inverse)
{
    typedef boost::numeric::ublas::permutation_matrix<std::size_t> pmatrix;

    // create a working copy of the input
    boost::numeric::ublas::matrix<T> A(input);

    // create a permutation matrix for the LU-factorization
    pmatrix pm(A.size1());

    // perform LU-factorization
    int res = lu_factorize(A, pm);
    if (res != 0)
        return false;

    // create identity matrix of "inverse"
    inverse.assign(boost::numeric::ublas::identity_matrix<T> (A.size1()));

    // backsubstitute to get the inverse
    lu_substitute(A, pm, inverse);

    return true;
}

PointF ConvertCoordinates(PointF pt_in,
    PointF pt1, PointF pt2, PointF pt1_, PointF pt2_)
{

    float matrix1[]={
         pt1.X,           pt1.Y,           1.0f,           0.0f,
        -pt1.Y,           pt1.X,           0.0f,           1.0f,
         pt2.X,           pt2.Y,           1.0f,           0.0f,
        -pt2.Y,           pt2.X,           0.0f,           1.0f
    };

    boost::numeric::ublas::matrix<float> M(4, 4);
    CopyMemory(&M.data()[0], matrix1, sizeof(matrix1));

    boost::numeric::ublas::matrix<float> M_1(4, 4);
    InvertMatrix<float>(M, M_1);

    double vector[] = {
        pt1_.X,
        pt1_.Y,
        pt2_.X,
        pt2_.Y
    };

    boost::numeric::ublas::vector<float> u(4);
    boost::numeric::ublas::vector<float> u1(4);
    u(0) = pt1_.X;
    u(1) = pt1_.Y;
    u(2) = pt2_.X;
    u(3) = pt2_.Y;

    u1 = boost::numeric::ublas::prod(M_1, u);

    PointF pt;
    pt.X = u1(0)*pt_in.X + u1(1)*pt_in.Y + u1(2);
    pt.Y = u1(1)*pt_in.X - u1(0)*pt_in.Y + u1(3);
    return pt;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接