对于环绕地图,是否存在简单的“直观点”算法?

时间:2009-07-12 01:24:41

标签: algorithm delphi math geometry coordinates

我正在尝试构建一个可以在边缘环绕的矩形网格。任何玩电子游戏的人都可能熟悉这个概念:在世界地图上向一个方向走得足够远,你最终会回到你开始的地方。但是,这会导致设置视口时出现一些困难,因为边缘可以滚动到负坐标区域。

采用负坐标并确定其实际值很容易:

function GetRealCoords(value: TPoint): TPoint;
begin
   result := ModPoints(AddPoints(value, MAP_SIZE), MAP_SIZE);
end;

其中AddPoints和ModPoints分别将+mod运算符分别应用于两个输入的每个坐标以生成输出值。

问题在于扭转此操作。给定一个点,其中两个坐标都是正的,TRect,其中Top和Left值可以是正的或负的(并且Bottom或Right可能超出了map的边缘),并且在全局范围内声明了MAP_SIZE,有没有办法确定该点是否属于观察矩形覆盖的区域,而不必在四个不同时间运行相同的计算?

3 个答案:

答案 0 :(得分:4)

我相信。

我能想到的最糟糕的情况(grid = [0,1)x [0,1))是这样的: Top = -0.25,Left = -0.25,Bottom = 0.25,Right = 0.25

这看起来像(包裹时):

 ______
|_|  |_|
|      |
|_    _|
|_|__|_|

现在,您必须测试四个角以查看该点是否位于其中。 但是,我相信通过在空间[1,2] x [1,2]中执行测试,你可以避免 问题,因为它又变成了一个矩形。

 ______
|      |
|      |
|     _|_
|____|   |
     |___|

通过计算矩形的宽度和高度来简化问题。

Width=Mod(Right-Left+MAP_SIZE,MAP_SIZE)
Height=Mod(Bottom-Top+MAP_SIZE,MAP_SIZE)

现在,计算左上角的包裹位置

LeftNew=Mod(Left+MAP_SIZE,MAP_SIZE)
TopNew=Mod(Top+MAP_SIZE,MAP_SIZE)

计算新的底部和右边:

RightNew=LeftNew+Width
BottomNew=TopNew+Height

现在,对于您要测试的每个点,添加MAP_SIZE,并测试它是否在新的rect中!

TestNew=AddPoints(Test,MAP_SIZE)

If (TestNew.X>=LeftNew && TestNew.X<=RightNew && TestNew.Y>=TopNew && TestNew.T<=BottomNew)
{
  We have a point inside!
}

我没有对此进行详尽的测试,但我认为这是正确的。

答案 1 :(得分:3)

通过这个,您可以测试您的点是否在矩形内。

function PointInRect(aPoint:TPoint;aRect:TRect):boolean;
begin
  Result:=(aPoint.X >= aRect.Left  ) and 
          (aPoint.X <  aRect.Right ) and 
          (aPoint.Y >= aRect.Top   ) and 
          (aPoint.Y <  aRect.Bottom);
end;

但如果我理解你的描述,你需要这样的东西:

function NormalisePoint(aPoint:TPoint;aRect:TRect):TPoint;
var Width,Height:integer;
begin
  Width  := aRect.Right-aRect.Left;
  Height := aRect.Bottom-aRect.Top;

  if (Width=0) then
    aPoint.X := aRect.Left
  else
  begin
    while (aPoint.X< aRect.Left  ) do inc(aPoint.X,Width );
    while (aPoint.X>=aRect.Right ) do dec(aPoint.X,Width );
  end;

  if (Height=0) then
    aPoint.Y := aRect.Top
  else
  begin
    while (aPoint.Y< aRect.Top   ) do inc(aPoint.Y,Height);
    while (aPoint.Y>=aRect.Bottom) do dec(aPoint.Y,Height);
  end;
  Result := aPoint;
end;

答案 2 :(得分:0)

在二维之前进行一维考虑。你想弄清楚一个数字是否在一个可能环绕的范围内,例如。时钟在7到2之间是3。完成后,您可以只对X和Y坐标执行测试。

我对更简单问题的解决方案:

//assumes start and end are both in [0, divisor). (Because .net and most other languages do modulus WRONG.)
double ClockDistance(double start, double end, double clockSize) {
    return (end - start + clockSize) % clockSize;
}
//assumes inclusive bounds
bool ClockBetween(int n, double start, double end, double clockSize) {
    return ClockDistance(start, n, clockSize) 
           <= ClockDistance(start, end, clockSize);
}

其中概括为:

//assumes rects oriented so bottom < top, not the other way around like in UI
bool RectContains(double x, double y, double left, double bottom, double right, double top, double worldWidth, double wordlHeight) {
    return ClockBetween(x, left, right, worldWidth) 
           && ClockBetween(y, bottom, top, worldHeight);
}