R – simple “point in rect” algorithm for a wraparound map


I'm trying to build a rectangular grid that can wrap around at the edges. Anyone who plays video games will probably be familiar with the concept: go far enough in one direction on the world map and you'll end up back where you started. This causes some difficulty in setting up the viewport, though, since the edges can scroll into negative coordinate territory.

It's easy enough to take a negative coordinate and determine its real value:

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

where AddPoints and ModPoints simply apply the + and mod operators, respectively, to each coordinate of the two inputs to produce an output value.

The trouble is reversing this operation. Given a point in which both coordinates are positive and a TRect in which the Top and Left values could be positive or negative, (and the Bottom or Right could be beyond the edges of the map,) and with MAP_SIZE declared at global scope, is there any way to determine whether the point falls within the territory covered by the viewing rectangle without having to run the same calculation up to four different times?

Best Solution

With this you can test if your point is within the rectangle.

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

But if I understand your description properly, you want something like this:

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

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

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