Codechange: Add InverseRemapCoords2 function for remapping viewport coordinates to underlying tile coordinates (Patch by adf88, #6583)
This commit is contained in:
		
				
					committed by
					
						
						Niels Martin Hansen
					
				
			
			
				
	
			
			
			
						parent
						
							43852baace
						
					
				
				
					commit
					f0290d5de7
				
			@@ -89,6 +89,57 @@ extern const byte _slope_to_sprite_offset[32] = {
 | 
			
		||||
 */
 | 
			
		||||
static SnowLine *_snow_line = NULL;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Map 2D viewport or smallmap coordinate to 3D world or tile coordinate.
 | 
			
		||||
 * Function takes into account height of tiles and foundations.
 | 
			
		||||
 *
 | 
			
		||||
 * @param x X viewport 2D coordinate.
 | 
			
		||||
 * @param y Y viewport 2D coordinate.
 | 
			
		||||
 * @param clamp_to_map Clamp the coordinate outside of the map to the closest, non-void tile within the map.
 | 
			
		||||
 * @return 3D world coordinate of point visible at the given screen coordinate (3D perspective).
 | 
			
		||||
 *
 | 
			
		||||
 * @note Inverse of #RemapCoords2 function. Smaller values may get rounded.
 | 
			
		||||
 * @see InverseRemapCoords
 | 
			
		||||
 */
 | 
			
		||||
Point InverseRemapCoords2(int x, int y, bool clamp_to_map)
 | 
			
		||||
{
 | 
			
		||||
	/* Initial x/y world coordinate is like if the landscape
 | 
			
		||||
	 * was completely flat on height 0. */
 | 
			
		||||
	Point pt = InverseRemapCoords(x, y);
 | 
			
		||||
 | 
			
		||||
	const uint min_coord = _settings_game.construction.freeform_edges ? TILE_SIZE : 0;
 | 
			
		||||
	const uint max_x = MapMaxX() * TILE_SIZE - 1;
 | 
			
		||||
	const uint max_y = MapMaxY() * TILE_SIZE - 1;
 | 
			
		||||
 | 
			
		||||
	if (clamp_to_map) {
 | 
			
		||||
		/* Bring the coordinates near to a valid range. At the top we allow a number
 | 
			
		||||
		 * of extra tiles. This is mostly due to the tiles on the north side of
 | 
			
		||||
		 * the map possibly being drawn higher due to the extra height levels. */
 | 
			
		||||
		int extra_tiles = CeilDiv(_settings_game.construction.max_heightlevel * TILE_HEIGHT, TILE_PIXELS);
 | 
			
		||||
		pt.x = Clamp(pt.x, -extra_tiles * TILE_SIZE, max_x);
 | 
			
		||||
		pt.y = Clamp(pt.y, -extra_tiles * TILE_SIZE, max_y);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Now find the Z-world coordinate by fix point iteration.
 | 
			
		||||
	 * This is a bit tricky because the tile height is non-continuous at foundations.
 | 
			
		||||
	 * The clicked point should be approached from the back, otherwise there are regions that are not clickable.
 | 
			
		||||
	 * (FOUNDATION_HALFTILE_LOWER on SLOPE_STEEP_S hides north halftile completely)
 | 
			
		||||
	 * So give it a z-malus of 4 in the first iterations. */
 | 
			
		||||
	int z = 0;
 | 
			
		||||
	for (int i = 0; i < 5; i++) z = GetSlopePixelZ(Clamp(pt.x + max(z, 4) - 4, min_coord, max_x), Clamp(pt.y + max(z, 4) - 4, min_coord, max_y)) / 2;
 | 
			
		||||
	for (int m = 3; m > 0; m--) z = GetSlopePixelZ(Clamp(pt.x + max(z, m) - m, min_coord, max_x), Clamp(pt.y + max(z, m) - m, min_coord, max_y)) / 2;
 | 
			
		||||
	for (int i = 0; i < 5; i++) z = GetSlopePixelZ(Clamp(pt.x + z,             min_coord, max_x), Clamp(pt.y + z,             min_coord, max_y)) / 2;
 | 
			
		||||
 | 
			
		||||
	pt.x += z;
 | 
			
		||||
	pt.y += z;
 | 
			
		||||
	if (clamp_to_map) {
 | 
			
		||||
		pt.x = Clamp(pt.x, min_coord, max_x);
 | 
			
		||||
		pt.y = Clamp(pt.y, min_coord, max_y);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Applies a foundation to a slope.
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -108,6 +108,7 @@ static inline Point RemapCoords2(int x, int y)
 | 
			
		||||
 * @param y Y coordinate of the 2D coordinate.
 | 
			
		||||
 * @return X and Y components of equivalent world or tile coordinate.
 | 
			
		||||
 * @note Inverse of #RemapCoords function. Smaller values may get rounded.
 | 
			
		||||
 * @see InverseRemapCoords2
 | 
			
		||||
 */
 | 
			
		||||
static inline Point InverseRemapCoords(int x, int y)
 | 
			
		||||
{
 | 
			
		||||
@@ -115,6 +116,8 @@ static inline Point InverseRemapCoords(int x, int y)
 | 
			
		||||
	return pt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point InverseRemapCoords2(int x, int y, bool clamp_to_map = false);
 | 
			
		||||
 | 
			
		||||
uint ApplyFoundationToSlope(Foundation f, Slope *s);
 | 
			
		||||
/**
 | 
			
		||||
 * Applies a foundation to a slope.
 | 
			
		||||
 
 | 
			
		||||
@@ -923,8 +923,8 @@ void SmallMapWindow::DrawMapIndicators() const
 | 
			
		||||
	/* Find main viewport. */
 | 
			
		||||
	const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
 | 
			
		||||
 | 
			
		||||
	Point upper_left_smallmap_coord  = TranslateXYToTileCoord(vp, vp->left, vp->top, false);
 | 
			
		||||
	Point lower_right_smallmap_coord = TranslateXYToTileCoord(vp, vp->left + vp->width - 1, vp->top + vp->height - 1, false);
 | 
			
		||||
	Point upper_left_smallmap_coord  = InverseRemapCoords2(vp->virtual_left, vp->virtual_top);
 | 
			
		||||
	Point lower_right_smallmap_coord = InverseRemapCoords2(vp->virtual_left + vp->virtual_width - 1, vp->virtual_top + vp->virtual_height - 1);
 | 
			
		||||
 | 
			
		||||
	Point upper_left = this->RemapTile(upper_left_smallmap_coord.x / (int)TILE_SIZE, upper_left_smallmap_coord.y / (int)TILE_SIZE);
 | 
			
		||||
	upper_left.x -= this->subscroll;
 | 
			
		||||
@@ -1645,7 +1645,7 @@ void SmallMapWindow::SetNewScroll(int sx, int sy, int sub)
 | 
			
		||||
void SmallMapWindow::SmallMapCenterOnCurrentPos()
 | 
			
		||||
{
 | 
			
		||||
	const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
 | 
			
		||||
	Point viewport_center = TranslateXYToTileCoord(vp, vp->left + vp->width / 2, vp->top + vp->height / 2);
 | 
			
		||||
	Point viewport_center = InverseRemapCoords2(vp->virtual_left + vp->virtual_width / 2, vp->virtual_top + vp->virtual_height / 2);
 | 
			
		||||
 | 
			
		||||
	int sub;
 | 
			
		||||
	const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SM_MAP);
 | 
			
		||||
 
 | 
			
		||||
@@ -399,65 +399,27 @@ ViewPort *IsPtInWindowViewport(const Window *w, int x, int y)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Translate screen coordinate in a viewport to a tile coordinate
 | 
			
		||||
 * Translate screen coordinate in a viewport to underlying tile coordinate.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns exact point of the map that is visible in the given place
 | 
			
		||||
 * of the viewport (3D perspective), height of tiles and foundations matter.
 | 
			
		||||
 *
 | 
			
		||||
 * @param vp  Viewport that contains the (\a x, \a y) screen coordinate
 | 
			
		||||
 * @param x   Screen x coordinate
 | 
			
		||||
 * @param y   Screen y coordinate
 | 
			
		||||
 * @param clamp_to_map Clamp the coordinate outside of the map to the closest tile within the map.
 | 
			
		||||
 * @return Tile coordinate
 | 
			
		||||
 * @param x   Screen x coordinate, distance in pixels from the left edge of viewport frame
 | 
			
		||||
 * @param y   Screen y coordinate, distance in pixels from the top edge of viewport frame
 | 
			
		||||
 * @param clamp_to_map Clamp the coordinate outside of the map to the closest, non-void tile within the map
 | 
			
		||||
 * @return Tile coordinate or (-1, -1) if given x or y is not within viewport frame
 | 
			
		||||
 */
 | 
			
		||||
Point TranslateXYToTileCoord(const ViewPort *vp, int x, int y, bool clamp_to_map)
 | 
			
		||||
{
 | 
			
		||||
	Point pt;
 | 
			
		||||
	int a, b;
 | 
			
		||||
	int z;
 | 
			
		||||
 | 
			
		||||
	if ( (uint)(x -= vp->left) >= (uint)vp->width ||
 | 
			
		||||
				(uint)(y -= vp->top) >= (uint)vp->height) {
 | 
			
		||||
				Point pt = {-1, -1};
 | 
			
		||||
				return pt;
 | 
			
		||||
	if (!IsInsideBS(x, vp->left, vp->width) || !IsInsideBS(y, vp->top, vp->height)) {
 | 
			
		||||
		Point pt = { -1, -1 };
 | 
			
		||||
		return pt;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	x = (ScaleByZoom(x, vp->zoom) + vp->virtual_left) >> (2 + ZOOM_LVL_SHIFT);
 | 
			
		||||
	y = (ScaleByZoom(y, vp->zoom) + vp->virtual_top) >> (1 + ZOOM_LVL_SHIFT);
 | 
			
		||||
 | 
			
		||||
	a = y - x;
 | 
			
		||||
	b = y + x;
 | 
			
		||||
 | 
			
		||||
	if (clamp_to_map) {
 | 
			
		||||
		/* Bring the coordinates near to a valid range. This is mostly due to the
 | 
			
		||||
		 * tiles on the north side of the map possibly being drawn too high due to
 | 
			
		||||
		 * the extra height levels. So at the top we allow a number of extra tiles.
 | 
			
		||||
		 * This number is based on the tile height and pixels. */
 | 
			
		||||
		int extra_tiles = CeilDiv(_settings_game.construction.max_heightlevel * TILE_HEIGHT, TILE_PIXELS);
 | 
			
		||||
		a = Clamp(a, -extra_tiles * TILE_SIZE, MapMaxX() * TILE_SIZE - 1);
 | 
			
		||||
		b = Clamp(b, -extra_tiles * TILE_SIZE, MapMaxY() * TILE_SIZE - 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* (a, b) is the X/Y-world coordinate that belongs to (x,y) if the landscape would be completely flat on height 0.
 | 
			
		||||
	 * Now find the Z-world coordinate by fix point iteration.
 | 
			
		||||
	 * This is a bit tricky because the tile height is non-continuous at foundations.
 | 
			
		||||
	 * The clicked point should be approached from the back, otherwise there are regions that are not clickable.
 | 
			
		||||
	 * (FOUNDATION_HALFTILE_LOWER on SLOPE_STEEP_S hides north halftile completely)
 | 
			
		||||
	 * So give it a z-malus of 4 in the first iterations.
 | 
			
		||||
	 */
 | 
			
		||||
	z = 0;
 | 
			
		||||
 | 
			
		||||
	int min_coord = _settings_game.construction.freeform_edges ? TILE_SIZE : 0;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < 5; i++) z = GetSlopePixelZ(Clamp(a + max(z, 4) - 4, min_coord, MapMaxX() * TILE_SIZE - 1), Clamp(b + max(z, 4) - 4, min_coord, MapMaxY() * TILE_SIZE - 1)) / 2;
 | 
			
		||||
	for (int malus = 3; malus > 0; malus--) z = GetSlopePixelZ(Clamp(a + max(z, malus) - malus, min_coord, MapMaxX() * TILE_SIZE - 1), Clamp(b + max(z, malus) - malus, min_coord, MapMaxY() * TILE_SIZE - 1)) / 2;
 | 
			
		||||
	for (int i = 0; i < 5; i++) z = GetSlopePixelZ(Clamp(a + z, min_coord, MapMaxX() * TILE_SIZE - 1), Clamp(b + z, min_coord, MapMaxY() * TILE_SIZE - 1)) / 2;
 | 
			
		||||
 | 
			
		||||
	if (clamp_to_map) {
 | 
			
		||||
		pt.x = Clamp(a + z, min_coord, MapMaxX() * TILE_SIZE - 1);
 | 
			
		||||
		pt.y = Clamp(b + z, min_coord, MapMaxY() * TILE_SIZE - 1);
 | 
			
		||||
	} else {
 | 
			
		||||
		pt.x = a + z;
 | 
			
		||||
		pt.y = b + z;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pt;
 | 
			
		||||
	return InverseRemapCoords2(
 | 
			
		||||
			ScaleByZoom(x - vp->left, vp->zoom) + vp->virtual_left,
 | 
			
		||||
			ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top, clamp_to_map);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* When used for zooming, check area below current coordinates (x,y)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user