蜂窝状网格的定位方法(转)
源:蜂窩狀網格的定位方法
所謂蜂窩狀網格,也就是由多個六邊形組成的類似蜂窩的網格,在一些游戲地圖編輯器,手機觸摸屏,泡泡龍游戲等場景可以看到使用這種蜂窩網格。對于普通的矩形網格來說(例如俄羅斯方塊,貪吃蛇的棋盤),由于屏幕和位圖在邏輯上的點陣模型,使得矩形網格的定位非常簡便。矩形網格如果按照邊連接,具有 4 臨域(上下左右),而按照頂點連接,具有 8 臨域(在前者基礎上加上對角線);蜂窩網格的行間是一種錯位關系,這使得我們編程建立數據結構模型時帶來一點不便。下面僅從直觀觀察描述該網格(實際編程實現時還需要根據具體情況而定)。蜂窩網格具有 6 臨域,例如在下面的圖中,就是左,右,上偏左,上偏右,下偏左,下偏右。當我們把蜂窩網格的數據也用數組存儲時,蜂窩網格的臨域和存儲結構有關。如下圖在行間交錯的情況下,6 臨域是在 8 臨域 的基礎上去掉了兩個元素(去掉的元素根據所在奇數行和偶數行有所不同),在編程時這些是需要注意的地方。
蜂窩網格的捕獲并不是那么直觀的,本文將講解如何在蜂窩網格定位,換句話說,也就是給定一個屏幕坐標,需要判斷哪個網格被該坐標選中。首先我們來看蜂窩網格定位的原理,由下圖所示:
在上面的蜂窩網格上,我用藍色線條繪制了一張矩形網格(暫時稱為網格A)。并用藍色圓點在圖上標記了每個蜂窩網格的中心點。我們根據給定的坐標(x,y)可以首先定位到網格A中的某個矩形網格,然后觀察“網格A”和“蜂窩網格”的關系可以發現,每個網格A的矩形網格的邊緣上都分布了三個蜂窩網格的中心點。這樣我們就可以在這三個點中找出與(x,y)點距離最近的點,也就完成了蜂窩網格定位。
需要注意的是,蜂窩網格由于存在一種錯位關系,因此蜂窩網格的中心點落到矩形網格A上時,是行間交替變化的。例如在我所畫的這張圖上,我在圖片右側繪制除了網格A的縱坐標為奇數和偶數時的蜂窩點分布情況。在捕獲蜂窩網格時,必須針對這一點特別處理。
網格A中的單個矩形網格寬度和高度在代碼中分別是 g_unitx 和 g_unity; 它們分別是 網格A 在兩個方向上的長度單位。 假設六邊形的邊長為 a ,則:
g_unitx = a * sqrt (3) ; g_unity = a * (3/2) ;
下面我們就給出捕獲蜂窩網格的重要代碼: void GetCell(int x, int y, int *lpCellX, int *lpCellY); (x,y)通常為鼠標的當前位置,調用后,通過 lpCellX 和 lpCellY 參數返回被捕獲的網格的中心點坐標。
#pragma once
#include "stdafx.h"
//蜂窩網格的定位方法 -- by hoodlum1980
//假設六邊形的邊長為a;
//兩個方向的矩形定位的基本單位
#define unitx(a) ((a)*1.7320508) //sqrt(3) * a
#define unity(a) ((a)*1.5) //1.5 * a
//兩個方向的矩形網格基本單位
double g_unitx;
double g_unity;
double g_MinDistance2; // (a*sqrt(3)/2)^2
//設置六邊形的邊長
void SetCellSize(int a)
{
if(a>0)
{
g_unitx = unitx(a);
g_unity = unity(a);
//二分之根號3 邊長的平方,如果距離比它還小,就必然捕獲
g_MinDistance2 = a*a*0.75;
}
}
//求取兩個點的距離平方
inline int distance2(int x1, int y1, int x2, int y2)
{
return ((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
}
//輸入鼠標按下的點坐標(x,y)
//返回被捕獲六邊形的中心坐標
void GetCell(int x, int y, int *lpCellX, int *lpCellY)
{
//位于矩形網格邊線上的三個CELL中心點
POINT points[3];
//當前距離
int dist;
int mindist= (int)(g_MinDistance2 * 100); //一個非常大的值
int i, index;//index:被捕獲的索引
//計算出鼠標點位于哪一個矩形網格中
int cx = (int)(x/g_unitx);
int cy = (int)(y/g_unity);
points[0].x = (int)(g_unitx * cx);
points[1].x = (int)(g_unitx * (cx+0.5));
points[2].x = (int)(g_unitx * (cx+1));
//根據cy是否是偶數,決定三個點的縱坐標
if(cy % 2 == 0)
{
//偶數時,三個點組成倒立三角
points[0].y = points[2].y = (int)(g_unity * cy);
points[1].y = (int)(g_unity * (cy+1));
}
else
{
//奇數時,三個點組成正立三角
points[0].y = points[2].y = (int)(g_unity * (cy+1));
points[1].y = (int)(g_unity * cy);
}
//現在找出鼠標距離哪一個點最近
for(i=0;i<3;i++)
{
//求出距離的平方
dist = distance2(x,y, points[i].x, points[i].y);
//如果已經肯定被捕獲
if(dist < g_MinDistance2)
{
index = i;
break;
}
//更新最小距離值和索引
if(dist < mindist)
{
mindist = dist;
index = i;
}
}
//現在index 就是被捕獲的結果
*lpCellX = points[index].x;
*lpCellY = points[index].y;
}
//給出蜂窩CELL的中心點和邊長a,填充Cell的六邊形的六個端點
void GetCellPoints(int cellx, int celly, int a, POINT *lpPoints)
{
if(lpPoints == NULL) return;
lpPoints[0].x = cellx;
lpPoints[0].y = celly - a;
lpPoints[1].x = cellx + (int)(g_unitx*0.5);
lpPoints[1].y = celly - a/2;
lpPoints[2].x = lpPoints[1].x;
lpPoints[2].y = celly + a/2;
lpPoints[3].x = cellx;
lpPoints[3].y = celly + a;
lpPoints[4].x = cellx - (int)(g_unitx*0.5);
lpPoints[4].y = celly + a/2;
lpPoints[5].x = lpPoints[4].x;
lpPoints[5].y = celly - a/2;
}
除了上面我實現的距離法以外,我們還可以根據角度法,求出被捕獲的CELL;原理如下圖所示:
如上圖所示,很顯然,同樣要區分 y 是偶數還是奇數。這里我們就其中一種情況討論,在上圖中,我們可以很容易獲取到三個 CELL 的交界點的坐標:
ox = g_unitx * x + a * sqrt(3)/2;
oy = g_unity * y + a/2;
然后我們求出鼠標點和交界點的偏離12點的角度值:(需要考慮y軸和笛卡爾坐標的方向相反,還要考慮點所在的象限,這里我們簡單起見,不去討論)
alpha = 90 - atan( (oy - y) / (x - ox)) ;
if (alpha < 0) alpha += 180 ;
然后根據該角度落在的區間,得出被捕獲的 CELL 的中心點坐標;
這種實現方法需要考慮的情況要比距離法更復雜,不易讀一些,因此這里我就不去嘗試寫出完成的代碼了。
下面我給出一個 Windows 應用程序作為上面的代碼的演示,該程序首先繪制一副蜂窩網格圖背景,然后當鼠標在窗口上移動時,窗口實時的反饋被鼠標捕獲的網格(用藍色顯示),程序運行效果如圖:
該范例的源代碼下載鏈接:
http://files.cnblogs.com/hoodlum1980/BeehiveCell.rar
總結
以上是生活随笔為你收集整理的蜂窝状网格的定位方法(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab最优控制实验报告_第十二篇
- 下一篇: 巨一自动化工业机器人_2021第11届深