Blame |
Last modification |
View Log
| RSS feed
#region --- License ---
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
* See license.txt for license info
*/
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
namespace OpenTK
{
class TexturePacker
<T
> where T
: IPackable
<T
>
{
Node root
;
#region --- Constructors ---
public TexturePacker
(int width,
int height
)
{
if (width
<= 0)
throw new ArgumentOutOfRangeException
("width", width,
"Must be greater than zero.");
if (height
<= 0)
throw new ArgumentOutOfRangeException
("height", height,
"Must be greater than zero.");
root
= new Node
();
root
.Rect = new Rectangle
(0,
0, width, width
);
}
#endregion
#region --- Public Methods ---
#region public Rectangle Add(T item)
// Packs the given item into the free space of the TexturePacker. Returns the Rectangle of the packed item.
public void Add
(T item,
out Rectangle rect
)
{
if (item
.Width > root
.Rect.Width || item
.Height > root
.Rect.Height)
throw new InvalidOperationException
("The item is too large for this TexturePacker");
Node node
;
//if (!items.ContainsKey(item))
{
node
= root
.Insert(item
);
// Tree is full and insertion failed:
if (node
== null)
throw new TexturePackerFullException
();
//items.Add(item, node);
rect
= node
.Rect;
}
//throw new ArgumentException("The item already exists in the TexturePacker.", "item");
}
#endregion
#region public void Clear()
/// <summary>
/// Discards all packed items.
/// </summary>
public void Clear
()
{
//items.Clear();
root
.Clear();
}
#endregion
#region public void ChangeSize(int new_width, int new_height)
/// <summary>
/// Changes the dimensions of the TexturePacker surface.
/// </summary>
/// <param name="new_width">The new width of the TexturePacker surface.</param>
/// <param name="new_height">The new height of the TexturePacker surface.</param>
/// <remarks>Changing the size of the TexturePacker surface will implicitly call TexturePacker.Clear().</remarks>
/// <seealso cref="Clear"/>
public void ChangeSize
(int new_width,
int new_height
)
{
throw new NotImplementedException
();
}
#endregion
#endregion
#region Node
class Node
{
public Node
()
{
}
Node left, right
;
Rectangle rect
;
int use_count
;
public Rectangle Rect
{ get
{ return rect
; } set
{ rect
= value
; } }
public Node Left
{ get
{ return left
; } set
{ left
= value
; } }
public Node Right
{ get
{ return right
; } set
{ right
= value
; } }
#region --- Constructor ---
public bool Leaf
{
get
{ return left
== null && right
== null; }
}
#endregion
#region public Node Insert(T item)
public Node Insert
(T item
)
{
if (!this.Leaf)
{
// Recurse towards left child, and if that fails, towards the right.
Node new_node
= left
.Insert(item
);
return new_node
?? right
.Insert(item
);
}
else
{
// We have recursed to a leaf.
// If it is not empty go back.
if (use_count
!= 0)
return null;
// If this leaf is too small go back.
if (rect
.Width < item
.Width || rect
.Height < item
.Height)
return null;
// If this leaf is the right size, insert here.
if (rect
.Width == item
.Width && rect
.Height == item
.Height)
{
use_count
= 1;
return this;
}
// This leaf is too large, split it up. We'll decide which way to split
// by checking the width and height difference between this rectangle and
// out item's bounding box. If the width difference is larger, we'll split
// horizontaly, else verticaly.
left
= new Node
();
right
= new Node
();
int dw
= this.rect.Width - item
.Width + 1;
int dh
= this.rect.Height - item
.Height + 1;
if (dw
> dh
)
{
left
.rect = new Rectangle
(rect
.Left, rect
.Top, item
.Width, rect
.Height);
right
.rect = new Rectangle
(rect
.Left + item
.Width, rect
.Top, rect
.Width - item
.Width, rect
.Height);
}
else
{
left
.rect = new Rectangle
(rect
.Left, rect
.Top, rect
.Width, item
.Height);
right
.rect = new Rectangle
(rect
.Left, rect
.Top + item
.Height, rect
.Width, rect
.Height - item
.Height);
}
return left
.Insert(item
);
}
}
#endregion
#region public void Clear()
public void Clear
()
{
if (left
!= null)
left
.Clear();
if (right
!= null)
right
.Clear();
left
= right
= null;
}
#endregion
}
#endregion
}
class TexturePackerFullException
: Exception
{
public TexturePackerFullException
() : base("There is not enough space to add this item. Consider calling the Clear() method.") { }
}
}