Implement height-aware map.Clamp(MPos).

This commit is contained in:
Paul Chote
2015-07-26 19:22:29 +01:00
parent 2c00019715
commit 13e0527f7c

View File

@@ -933,12 +933,62 @@ namespace OpenRA
public MPos Clamp(MPos uv)
{
if (MaximumTerrainHeight == 0)
return (MPos)Clamp((PPos)uv);
// Already in bounds, so don't need to do anything.
if (Contains(uv))
if (ProjectedCellsCovering(uv).Any(containsTest))
return uv;
// TODO: Account for terrain height
return (MPos)Clamp((PPos)uv);
// Clamping map coordinates is trickier than it might first look!
// This needs to handle three nasty cases:
// * The requested cell is well outside the map region
// * The requested cell is near the top edge inside the map but outside the projected layer
// * The clamped projected cell lands on a cliff face with no associated map cell
//
// Handling these cases properly requires abuse of our knowledge of the projection transform.
//
// The U coordinate doesn't change significantly in the projection, so clamp this
// straight away and ensure the point is somewhere inside the map
uv = cellProjection.Clamp(new MPos(uv.U.Clamp(Bounds.Left, Bounds.Right), uv.V));
// Project this guessed cell and take the first available cell
// If it is projected outside the layer, then make another guess.
var allProjected = ProjectedCellsCovering(uv);
var projected = allProjected.Any() ? allProjected.First()
: new PPos(uv.U, uv.V.Clamp(Bounds.Top, Bounds.Bottom));
// Clamp the projected cell to the map area
projected = Clamp(projected);
// Project the cell back into map coordinates.
// This may fail if the projected cell covered a cliff or another feature
// where there is a large change in terrain height.
var unProjected = Unproject(projected);
if (!unProjected.Any())
{
// Adjust V until we find a cell that works
for (var x = 2; x <= 2 * MaximumTerrainHeight; x++)
{
var dv = ((x & 1) == 1 ? 1 : -1) * x / 2;
var test = new PPos(projected.U, projected.V + dv);
if (!Contains(test))
continue;
unProjected = Unproject(test);
if (unProjected.Any())
break;
}
// This shouldn't happen. But if it does, return the original value and hope the caller doesn't explode.
if (!unProjected.Any())
{
Log.Write("debug", "Failed to clamp map cell {0} to map bounds", uv);
return uv;
}
}
return projected.V == Bounds.Bottom ? unProjected.MaxBy(x => x.V) : unProjected.MinBy(x => x.V);
}
public PPos Clamp(PPos puv)