#region Copyright & License Information /* * Copyright 2007-2021 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System.Collections; using System.Collections.Generic; namespace OpenRA { // Represents a (on-screen) rectangular collection of tiles. // TopLeft and BottomRight are inclusive public class ProjectedCellRegion : IEnumerable { // Corners of the region public readonly PPos TopLeft; public readonly PPos BottomRight; // Corners of the bounding map region that contains all the cells that // may be projected within this region. readonly MPos mapTopLeft; readonly MPos mapBottomRight; public ProjectedCellRegion(Map map, PPos topLeft, PPos bottomRight) { TopLeft = topLeft; BottomRight = bottomRight; // The projection from MPos -> PPos cannot produce a larger V coordinate // so the top edge of the MPos region is the same as the PPos region. // (in fact the cells are identical if height == 0) mapTopLeft = (MPos)topLeft; // The bottom edge is trickier: cells at MPos.V > bottomRight.V may have // been projected into this region if they have height > 0. // Each height step is equivalent to 512 WDist units, which is one MPos // step for isometric cells, but only half a MPos step for classic cells. Doh! var maxHeight = map.Grid.MaximumTerrainHeight; var heightOffset = map.Grid.Type == MapGridType.RectangularIsometric ? maxHeight : maxHeight / 2; // Use the map Height data array to clamp the bottom coordinate so it doesn't overflow the map mapBottomRight = map.Height.Clamp(new MPos(bottomRight.U, bottomRight.V + heightOffset)); } public bool Contains(PPos puv) { return puv.U >= TopLeft.U && puv.U <= BottomRight.U && puv.V >= TopLeft.V && puv.V <= BottomRight.V; } /// /// The region in map coordinates that contains all the cells that /// may be projected inside this region. For increased performance, /// this does not validate whether individual map cells are actually /// projected inside the region. /// public MapCoordsRegion CandidateMapCoords => new MapCoordsRegion(mapTopLeft, mapBottomRight); public ProjectedCellRegionEnumerator GetEnumerator() { return new ProjectedCellRegionEnumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public struct ProjectedCellRegionEnumerator : IEnumerator { readonly ProjectedCellRegion r; // Current position, in projected map coordinates int u, v; PPos current; public ProjectedCellRegionEnumerator(ProjectedCellRegion region) : this() { r = region; Reset(); current = new PPos(u, v); } public bool MoveNext() { u += 1; // Check for column overflow if (u > r.BottomRight.U) { v += 1; u = r.TopLeft.U; // Check for row overflow if (v > r.BottomRight.V) return false; } current = new PPos(u, v); return true; } public void Reset() { // Enumerator starts *before* the first element in the sequence. u = r.TopLeft.U - 1; v = r.TopLeft.V; } public PPos Current => current; object IEnumerator.Current => Current; public void Dispose() { } } } }