笛卡尔坐标和屏幕坐标之间的转换

Translating between cartesian and screen coordinates

本文关键字:转换 之间 屏幕坐标 坐标 笛卡尔      更新时间:2023-10-16

对于我的游戏,我需要在两个坐标系之间转换的函数。这主要是数学问题,但我需要的是C++代码来做这件事,并解释如何解决我的问题。

屏幕坐标:

a) 左上角为0,0

b) 无负值

c) 右+=x(x值越多,右边的点越多)

d) 底部+=y

笛卡尔二维坐标:

a) 中点是(0,0)

b) 负值确实存在

c) 右侧+=x

d) bottom-=y(y越小,底部的点越多)

我需要一种简单的方法来从一个系统转换到另一个系统,反之亦然。要做到这一点,(我想)我需要一些知识,比如笛卡尔坐标中的(0,0)[屏幕坐标的左上角]在哪里。

然而,有一个问题是,对于笛卡尔坐标中的某个点,在将其转换为屏幕坐标后,屏幕坐标中的位置可能是负的,这是无稽之谈。我不能把屏幕左上角的坐标放在(-inifity,+infinity)笛卡尔坐标。。。

我该如何解决这个问题?我能想到的唯一解决方案是将屏幕(0,0)放在笛卡尔(0,O)中,并且只使用笛卡尔系统的IV四分之一,但在这种情况下,使用笛卡尔系统是毫无意义的。。。

我确信有一些方法可以将屏幕坐标转换为笛卡尔坐标,反之亦然,但我在思考负值时犯了一些错误。

从笛卡尔坐标转换为屏幕坐标的基本算法是

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

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

这将把上述算法改为

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

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

您也可以允许平移坐标空间,也就是说,允许笛卡尔空间的中心偏离屏幕的中心。这也有助于让zoom_factor尽可能地保持紧凑,但也可以适应笛卡尔空间原点周围分布不均匀的数据。

这将把算法改为

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

您必须知道屏幕的大小才能转换

转换为笛卡尔坐标:

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;

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

然后你可以做:

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

和:

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

您总是会遇到这样的问题,即结果可能不在屏幕上——要么是负值,要么是大于可用屏幕大小的值。

有时这并不重要:例如,如果图形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;

我为您提供了一些基于微软文章的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;
}