blob: fa8e68eb87f5eea6701abd207cd61b579ac9834a [file] [log] [blame] [raw]
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace NVNC.Utils.ScreenTree
{
public class QuadTree
{
public readonly Dictionary<Rectangle2, QuadNode> LocationToNode;
private Rectangle2 ScreenSize { get; set; }
private int[] ScreenPixels { get; set; }
public QuadNode Root { get; private set; }
//private object syncLock = new object();
public QuadTree(Rectangle2 screenSize, int[] pixels, int minTileHeight = 64, int minTileWidth = 64)
{
Stopwatch t = Stopwatch.StartNew();
QuadNode.MIN_HEIGHT = minTileHeight; //Any better way ?
QuadNode.MIN_WIDTH = minTileWidth;
ScreenSize = screenSize;
ScreenPixels = pixels;
LocationToNode = new Dictionary<Rectangle2, QuadNode>();
Root = new QuadNode(screenSize, pixels);
//Very time consuming method ! Needs optimization
//Root.Expand();
Stopwatch a = Stopwatch.StartNew();
HashSet<QuadNode> lowestLevel = BFSLowest();
a.Stop();
Trace.WriteLine("Found lowest in:" + a.ElapsedMilliseconds + "ms");
a = Stopwatch.StartNew();
int numberOfTasks = lowestLevel.Count * 4;
ManualResetEvent signal = new ManualResetEvent(false);
var arr = lowestLevel.ToArray();
for (int iter = 0; iter < arr.Length; iter++)
{
QuadNode currNode = arr[iter];
for (int i = 0; i < 4; i++)
{
int li = i;
//ThreadPool.QueueUserWorkItem(func =>
//{
Dictionary<int, long> occurances = new Dictionary<int, long>();
long h = 1;
long maxO = -1;
long maxV = -1;
for (long j = 0; j < currNode.childrenData[li].Length; j++)
{
int px = currNode.childrenData[li][j];
h = (h * ((px + j) % QuadNode.Q)) % QuadNode.Q;
int val = px;
if (!occurances.ContainsKey(val))
occurances.Add(val, 0);
occurances[val]++;
if (occurances[val] > maxO)
{
maxO = occurances[val];
maxV = val;
}
}
currNode.childrenHashes[li] = h;
long diff = occurances.Count;
//Calculates the percentage of different pixels in the rectangle
//If it is less than 10 in 1024, the tile is considered to be filled with a solid color
//The solid color used for filling is the color which occured the most times
float percDiff = (float)diff / currNode.childrenData[li].Length;
if (percDiff < 0.01)
{
currNode.childrenRect[li].SetSolidColor((int)maxV);
currNode.childrenHashes[li] = ((long)Math.Pow(maxV * maxO * diff, 3)) % QuadNode.Q;
//idk if the previous hash would be better or this one
}
currNode.DataHash = (currNode.DataHash + currNode.childrenHashes[li]) % QuadNode.Q;
if (Interlocked.Decrement(ref numberOfTasks) == 0)
{
signal.Set();
}
//});
}
}
signal.WaitOne();
a.Stop();
Trace.WriteLine("Processed nodes in:" + a.ElapsedMilliseconds + "ms");
Root.CalculateHash();
//IEnumerable<QuadNode> nodes = GetChildren(Root);
//foreach (QuadNode ch in nodes)
//{
//LocationToNode.Add(ch.Bounds, ch);
//}
t.Stop();
Trace.WriteLine("QuadTree construction: " + t.ElapsedMilliseconds + "ms");
}
private HashSet<QuadNode> BFSLowest()
{
HashSet<QuadNode> ret = new HashSet<QuadNode>();
Queue<QuadNode> q = new Queue<QuadNode>();
q.Enqueue(Root);
while (q.Count > 0)
{
QuadNode c = q.Dequeue();
if (c.CanExpand())
{
for (int i = 0; i < 4; i++)
{
int li = i;
Direction d = (Direction)li;
c[d] = new QuadNode(c.childrenRect[li], c.childrenData[li]);
q.Enqueue(c[d]);
}
}
else
{
ret.Add(c);
}
}
return ret;
}
private IEnumerable<QuadNode> GetChildren(QuadNode root)
{
HashSet<QuadNode> ret = new HashSet<QuadNode>();
GetChildren(root, ret);
return ret;
}
private void GetChildren(QuadNode root, ICollection<QuadNode> ret)
{
if (root != null)
{
ret.Add(root);
for (int i = 0; i < 4; i++)
{
QuadNode ch = root[(Direction)i];
if (ch != null)
{
GetChildren(ch, ret);
}
}
}
}
public QuadNode this[Rectangle2 location]
{
get { return LocationToNode[location]; }
}
public override string ToString()
{
return Root.DataHash.ToString();
}
}
public enum Direction
{
NW = 0,
NE = 1,
SW = 2,
SE = 3
}
}