Files
OpenRA/OpenRA.Game/Traits/World/ScreenMap.cs
RoosterDragon 1116f578a3 Make FrozenActorsInBox a streaming enumerable.
Following the same layout as ActorsInBox, this streams the enumerable internally making it cheaper to call if only part of it is required, and also avoids building an intermediate list.
2014-12-07 21:36:56 +00:00

192 lines
5.8 KiB
C#
Executable File

#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Traits
{
public class ScreenMapInfo : ITraitInfo
{
[Desc("Size of partition bins (world pixels)")]
public readonly int BinSize = 250;
public object Create(ActorInitializer init) { return new ScreenMap(init.world, this); }
}
public class ScreenMap : IWorldLoaded
{
ScreenMapInfo info;
WorldRenderer worldRenderer;
Cache<Player, Dictionary<FrozenActor, Rectangle>[]> frozen;
Dictionary<Actor, Rectangle>[] actors;
int rows, cols;
public ScreenMap(World world, ScreenMapInfo info)
{
this.info = info;
var ts = Game.modData.Manifest.TileSize;
cols = world.Map.MapSize.X * ts.Width / info.BinSize + 1;
rows = world.Map.MapSize.Y * ts.Height / info.BinSize + 1;
frozen = new Cache<Player, Dictionary<FrozenActor, Rectangle>[]>(InitializeFrozenActors);
actors = new Dictionary<Actor, Rectangle>[rows * cols];
for (var j = 0; j < rows; j++)
for (var i = 0; i < cols; i++)
actors[j * cols + i] = new Dictionary<Actor, Rectangle>();
}
public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
Dictionary<FrozenActor, Rectangle>[] InitializeFrozenActors(Player p)
{
var f = new Dictionary<FrozenActor, Rectangle>[rows * cols];
for (var j = 0; j < rows; j++)
for (var i = 0; i < cols; i++)
f[j * cols + i] = new Dictionary<FrozenActor, Rectangle>();
return f;
}
public void Add(Player viewer, FrozenActor fa)
{
var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition);
var bounds = fa.Bounds;
bounds.Offset(pos.X, pos.Y);
var top = Math.Max(0, bounds.Top / info.BinSize);
var left = Math.Max(0, bounds.Left / info.BinSize);
var bottom = Math.Min(rows - 1, bounds.Bottom / info.BinSize);
var right = Math.Min(cols - 1, bounds.Right / info.BinSize);
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
frozen[viewer][j*cols + i].Add(fa, bounds);
}
public void Remove(Player viewer, FrozenActor fa)
{
foreach (var bin in frozen[viewer])
bin.Remove(fa);
}
public void Add(Actor a)
{
var pos = worldRenderer.ScreenPxPosition(a.CenterPosition);
var bounds = a.Bounds;
bounds.Offset(pos.X, pos.Y);
var top = Math.Max(0, bounds.Top / info.BinSize);
var left = Math.Max(0, bounds.Left / info.BinSize);
var bottom = Math.Min(rows - 1, bounds.Bottom / info.BinSize);
var right = Math.Min(cols - 1, bounds.Right / info.BinSize);
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
actors[j * cols + i].Add(a, bounds);
}
public void Remove(Actor a)
{
foreach (var bin in actors)
bin.Remove(a);
}
public void Update(Actor a)
{
Remove(a);
Add(a);
}
public static readonly IEnumerable<FrozenActor> NoFrozenActors = new FrozenActor[0].AsEnumerable();
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, int2 worldPx)
{
if (viewer == null)
return NoFrozenActors;
var i = (worldPx.X / info.BinSize).Clamp(0, cols - 1);
var j = (worldPx.Y / info.BinSize).Clamp(0, rows - 1);
return frozen[viewer][j*cols + i]
.Where(kv => kv.Key.IsValid && kv.Value.Contains(worldPx))
.Select(kv => kv.Key);
}
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, MouseInput mi)
{
return FrozenActorsAt(viewer, worldRenderer.Viewport.ViewToWorldPx(mi.Location));
}
public IEnumerable<Actor> ActorsAt(int2 worldPx)
{
var i = (worldPx.X / info.BinSize).Clamp(0, cols - 1);
var j = (worldPx.Y / info.BinSize).Clamp(0, rows - 1);
return actors[j * cols + i]
.Where(kv => kv.Key.IsInWorld && kv.Value.Contains(worldPx))
.Select(kv => kv.Key);
}
public IEnumerable<Actor> ActorsAt(MouseInput mi)
{
return ActorsAt(worldRenderer.Viewport.ViewToWorldPx(mi.Location));
}
public IEnumerable<Actor> ActorsInBox(int2 a, int2 b)
{
return ActorsInBox(Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)));
}
public IEnumerable<Actor> ActorsInBox(Rectangle r)
{
var left = (r.Left / info.BinSize).Clamp(0, cols - 1);
var right = (r.Right / info.BinSize).Clamp(0, cols - 1);
var top = (r.Top / info.BinSize).Clamp(0, rows - 1);
var bottom = (r.Bottom / info.BinSize).Clamp(0, rows - 1);
var actorsChecked = new HashSet<Actor>();
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
foreach (var kvp in actors[j * cols + i])
{
var actor = kvp.Key;
if (actor.IsInWorld && kvp.Value.IntersectsWith(r) && actorsChecked.Add(actor))
yield return actor;
}
}
public IEnumerable<FrozenActor> FrozenActorsInBox(Player p, int2 a, int2 b)
{
return FrozenActorsInBox(p, Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)));
}
public IEnumerable<FrozenActor> FrozenActorsInBox(Player p, Rectangle r)
{
var left = (r.Left / info.BinSize).Clamp(0, cols - 1);
var right = (r.Right / info.BinSize).Clamp(0, cols - 1);
var top = (r.Top / info.BinSize).Clamp(0, rows - 1);
var bottom = (r.Bottom / info.BinSize).Clamp(0, rows - 1);
var bins = frozen[p];
var actorsChecked = new HashSet<FrozenActor>();
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
foreach (var kvp in bins[j * cols + i])
{
var actor = kvp.Key;
if (actor.IsValid && kvp.Value.IntersectsWith(r) && actorsChecked.Add(actor))
yield return actor;
}
}
}
}