17

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

屏幕坐标:

a) 左上角是 0,0

b) 没有负值

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

d) 底部 +=y

笛卡尔二维坐标:

a) 中点为 (0, 0)

b) 负值确实存在

c) 右 += x

d) 底部 -= y (y 越少,底部越多)

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

但是存在一个问题,笛卡尔坐标中的某个点在转换为屏幕坐标后,屏幕坐标中的位置可能是负数,这是胡说八道。我不能将屏幕坐标的左上角放在(-inifity,+infinity)笛卡尔坐标中......

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

我确信有办法将屏幕坐标转换为笛卡尔坐标,反之亦然,但我的想法是用负值做错了。

4

5 回答 5

20

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

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
于 2013-02-14T17:40:53.667 回答
6

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

转换为笛卡尔:

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;
于 2013-02-14T17:33:42.693 回答
4

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

然后你可以这样做:

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

和:

screenX =  cartX + (width / 2);
screenY = -cartY + (height / 2);
于 2013-02-14T17:33:02.783 回答
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;
于 2013-02-14T17:38:34.817 回答
0

根据微软文章: https ://msdn.microsoft.com/en-us/library/jj635757(v=vs.85).aspx,我为你准备了一些 boost c++

您只需要知道坐标系中的两个屏幕点和两个点。然后,您可以将点从一个系统转换为另一个系统。

#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;
}
于 2017-03-07T08:53:45.773 回答