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

时间:2013-02-14 17:28:46

标签: c++ algorithm math coordinates coordinate-systems

对于我的游戏,我需要在两个坐标系之间进行转换的功能。那么它主要是数学问题,但我需要的是C ++代码,以及如何解决我的问题的一些解释。

屏幕坐标:

a)左上角是0,0

b)没有负值

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

d)底部+ = y

笛卡尔2D坐标:

a)中间点是(0,0)

b)确实存在负值

c)右+ = x

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

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

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

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

我确信有很多方法可以将屏幕坐标转换为笛卡尔坐标,反之亦然,但我在思考这个负值时做错了。

5 个答案:

答案 0 :(得分:12)

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

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,直到所有笛卡尔坐标都适合屏幕。

您也可以允许平移坐标空间,这意味着允许笛卡尔空间的中心偏离屏幕中心。这也可以帮助您使zoom_factor保持尽可能紧,同时也适合不均匀分布在笛卡尔空间原点周围的数据。

这会将算法更改为

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

答案 1 :(得分:2)

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

转换为笛卡儿:

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;

答案 2 :(得分:1)

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

然后你可以这样做:

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

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

答案 3 :(得分:1)

您将始终遇到结果可能在屏幕外的问题 - 无论是作为负值,还是作为大于可用屏幕尺寸的值。

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

答案 4 :(得分:0)

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