EasyX IMAGE 对象旋转,获取图像上某一点旋转后的坐标
[編輯] [转简体] (简体译文)概要
正文
之前在 2.5D Racing Car 项目里面用到的一个算法,算是简易的图像旋转算法吧
当时我没想出这个算法,后来期末考试期间的一个晚自习想出来了,哈哈。其实不难,我只是后知后觉罢了。
不过之前忘记把这个算法单独发出来,现在补一下,代码如下。
// 2D 坐标旋转 POINT Rotate2D(int x, int y, double radian) { if (radian == 0) return { x,y }; return { (int)(x * cos(radian) - y * sin(radian)), (int)(x * sin(radian) + y * cos(radian)) }; } // 获取一图像坐标在旋转后的新坐标位置(自适应长宽式旋转) // width, height 原图像宽高 // x, y 为旋转点原坐标 // radian 旋转弧度 POINT GetRotatedImagePos(int width, int height, int x, int y, double radian) { int left = 0, button = 0; POINT points[3] = { {width,0},{0,height},{width,height} }; // 由于 IMAGE 对象的坐标系 y 轴向下,通用坐标系 y 轴向上, // 故旋转时需要以相反角度旋转才等效于在 IMAGE 对象的坐标系中正常旋转 for (int j = 0; j < 3; j++) { points[j] = Rotate2D(points[j].x, points[j].y, -radian); } POINT p = Rotate2D(x, y, -radian); // 记录最左和最下的端点 for (int j = 0; j < 3; j++) { if (points[j].x < points[left].x) { left = j; } if (points[j].y < points[button].y) { button = j; } } // 若图像旋转后端点坐标越界,将其补回正常位置 if (points[left].x < 0) p.x += -points[left].x; if (points[button].y < 0) p.y += -points[button].y; return p; }
其实和普通的 2D 旋转都差不多,但是多了一道平移的工序,因为 IMAGE 对象旋转完了要自适应长宽嘛,这也正是唯一要注意的地方。
说原理之前,先贴个实现相同功能的乐色算法吧……
// 获取小车在旋转后的地图中的位置 // NOTE: 旧方法,耗时 60ms 左右,不推荐使用。 POINT GetRotatedCarPosition(int width, int height, int x, int y, double radian) { IMAGE p(width, height), p2; int nw, nh; SetWorkingImage(&p); fillrectangle(x, y, x + 2, y + 2); rotateimage(&p2, &p, radian, BLACK, true, false); nw = p2.getwidth(); nh = p2.getheight(); SetWorkingImage(); DWORD* buf = GetImageBuffer(&p2); for (int i = 0; i < nw * nh; i++) { if (buf[i] == WHITE) { return { i - (i / nw) * nw,i / nw }; } } // 寻找失败 return { -1,-1 }; }
这个就是直接再开了一张新的 IMAGE 对象做相同的旋转,然后在图上找点来取坐标,非常的 low。
一开始我就写的是这个,当时周末没太多时间搞出算法来,实属无奈之举。
浅谈原理,其实不难。
首先要明确的是:IMAGE 对象的坐标系中 y 轴是向下的,就意味着图片中的一个点旋转多少度,在平面直角坐标系中进行计算的时候,要旋转相反的角度,这样一来,这个坐标放回到 IMAGE 对象坐标系中,新坐标才是正常的。
关于 2D 旋转这里就不说了。
其次,就是自适应图像大小的问题。其实也很简单了,画个图看看
这是 IMAGE 对象的坐标系:
——————————————> x
| |
| |
| 这是图像 |
| |
|____________|
|
↓
y
然后你假设它绕原点,逆时针,旋转个 60 度吧?那是不是图像有一部分到 x 轴上面去了?
这个时候为了实现图像自适应,就要把图像往下平移吧?把 x 轴上面多出来的一块,给他正好平移到与 x 轴相切的位置,即 y=0 处。
然后你再想,要是转角更大一点,270 度?请想象一下,这个时候有一部分图像到 y 轴左侧了吧?同样是给他平移,向右平移回来,平移到 x=0 处。
这样图像大小自适应就好了。
那么这一步操作放到平面直角坐标系的旋转当中,请想一想。
因为这两个坐标系 y 轴方向相反嘛,逆时针旋转的 60 度,现在应该变成顺时针了,原先在 IMAGE 坐标系里面的 “x 轴上面多出来的一块”就变成 x 轴下面多出来的一块了,对吧?
那继续旋转,由于 x 轴方向还是一样的,继续旋转的话就会超出到 y 轴左侧,和在 IMAGE 坐标系中是一样的。
所以总结一下:
一个点在 IMAGE 对象的坐标系中旋转一定角度,就是在平面直角坐标系中旋转相反的角度。
在平面直角坐标系中,一个点旋转完毕后,需要判定这个点所属的整个图像,是否超出第一象限(除非旋转角度是 360° 的倍数,否则都会超出的,大家都懂),这个时候就要把超出的部分平移,直到整个图像回到第一象限,此时被旋转的点的坐标就被平移了,得到最终的旋转坐标。
在代码中实现的时候,2D 旋转就不用说了,平移的那部分的实现方法,我是把那张图像的 除了在原点外的 其余三个顶点也跟着进行了旋转,然后得到三个顶点中的 x 坐标最小值,y 坐标最小值,如果这个值是负数,那么就把 旋转后的点的坐标(x 或 y 坐标)相应地加上这个值的相反数,就完成了图像大小的自适应。
是不是很简单~~