Files
OpenRA/OpenRA.Editor/BrushTool.cs
2013-03-27 09:45:17 +01:00

124 lines
3.3 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2011 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.Windows.Forms;
using OpenRA.FileFormats;
using SGraphics = System.Drawing.Graphics;
namespace OpenRA.Editor
{
class BrushTool : ITool
{
BrushTemplate Brush;
public BrushTool(BrushTemplate brush) { this.Brush = brush; }
public void Apply(Surface surface)
{
// change the bits in the map
var template = surface.TileSet.Templates[Brush.N];
var tile = template.Data;
var pos = surface.GetBrushLocation();
if (surface.GetModifiers() == Keys.Shift)
{
FloodFillWithBrush(surface, pos);
return;
}
for (var u = 0; u < template.Size.X; u++)
for (var v = 0; v < template.Size.Y; v++)
{
if (surface.Map.IsInMap(new CVec(u, v) + pos))
{
var z = u + v * template.Size.X;
if (tile.TileBitmapBytes[z] != null)
surface.Map.MapTiles.Value[u + pos.X, v + pos.Y] =
new TileReference<ushort, byte>
{
type = Brush.N,
index = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4) * 4) : (byte)z,
};
var ch = new int2((pos.X + u) / Surface.ChunkSize, (pos.Y + v) / Surface.ChunkSize);
if (surface.Chunks.ContainsKey(ch))
{
surface.Chunks[ch].Dispose();
surface.Chunks.Remove(ch);
}
}
}
}
public void Preview(Surface surface, SGraphics g)
{
g.DrawImage(Brush.Bitmap,
surface.TileSet.TileSize * surface.GetBrushLocation().X * surface.Zoom + surface.GetOffset().X,
surface.TileSet.TileSize * surface.GetBrushLocation().Y * surface.Zoom + surface.GetOffset().Y,
Brush.Bitmap.Width * surface.Zoom,
Brush.Bitmap.Height * surface.Zoom);
}
void FloodFillWithBrush(Surface s, CPos pos)
{
var queue = new Queue<CPos>();
var replace = s.Map.MapTiles.Value[pos.X, pos.Y];
var touched = new bool[s.Map.MapSize.X, s.Map.MapSize.Y];
Action<int, int> maybeEnqueue = (x, y) =>
{
if (s.Map.IsInMap(x, y) && !touched[x, y])
{
queue.Enqueue(new CPos(x, y));
touched[x, y] = true;
}
};
queue.Enqueue(pos);
while (queue.Count > 0)
{
var p = queue.Dequeue();
if (!s.Map.MapTiles.Value[p.X, p.Y].Equals(replace))
continue;
var a = FindEdge(s, p, new CVec(-1, 0), replace);
var b = FindEdge(s, p, new CVec(1, 0), replace);
for (var x = a.X; x <= b.X; x++)
{
s.Map.MapTiles.Value[x, p.Y] = new TileReference<ushort, byte> { type = Brush.N, index = (byte)0 };
if (s.Map.MapTiles.Value[x, p.Y - 1].Equals(replace))
maybeEnqueue(x, p.Y - 1);
if (s.Map.MapTiles.Value[x, p.Y + 1].Equals(replace))
maybeEnqueue(x, p.Y + 1);
}
}
/* TODO: optimize */
foreach (var ch in s.Chunks.Values) ch.Dispose();
s.Chunks.Clear();
}
CPos FindEdge(Surface s, CPos p, CVec d, TileReference<ushort, byte> replace)
{
for (; ; )
{
var q = p + d;
if (!s.Map.IsInMap(q)) return p;
if (!s.Map.MapTiles.Value[q.X, q.Y].Equals(replace)) return p;
p = q;
}
}
}
}