Remove old rect placement algorithm.
This commit is contained in:
parent
ba6afafc86
commit
b6eb5f721a
|
@ -1,38 +0,0 @@
|
|||
A class that fits subrectangles into a power-of-2 rectangle
|
||||
|
||||
(C) Copyright 2000-2002 by Javier Arevalo
|
||||
This code is free to use and modify for all purposes
|
||||
|
||||
You have a bunch of rectangular pieces. You need to arrange them in a
|
||||
rectangular surface so that they don't overlap, keeping the total area of the
|
||||
rectangle as small as possible. This is fairly common when arranging characters
|
||||
in a bitmapped font, lightmaps for a 3D engine, and I guess other situations as
|
||||
well.
|
||||
|
||||
The idea of this algorithm is that, as we add rectangles, we can pre-select
|
||||
"interesting" places where we can try to add the next rectangles. For optimal
|
||||
results, the rectangles should be added in order. I initially tried using area
|
||||
as a sorting criteria, but it didn't work well with very tall or very flat
|
||||
rectangles. I then tried using the longest dimension as a selector, and it
|
||||
worked much better. So much for intuition...
|
||||
|
||||
These "interesting" places are just to the right and just below the currently
|
||||
added rectangle. The first rectangle, obviously, goes at the top left, the next
|
||||
one would go either to the right or below this one, and so on. It is a weird way
|
||||
to do it, but it seems to work very nicely.
|
||||
|
||||
The way we search here is fairly brute-force, the fact being that for most off-
|
||||
line purposes the performance seems more than adequate. I have generated a
|
||||
japanese font with around 8500 characters and all the time was spent generating
|
||||
the bitmaps.
|
||||
|
||||
Also, for all we care, we could grow the parent rectangle in a different way
|
||||
than power of two. It just happens that power of 2 is very convenient for
|
||||
graphics hardware textures.
|
||||
|
||||
I'd be interested in hearing of other approaches to this problem. Make sure
|
||||
to post them on http://www.flipcode.com
|
||||
|
||||
See also
|
||||
http://www.flipcode.com/archives/Rectangle_Placement.shtml
|
||||
http://kossovsky.net/index.php/2009/07/cshar-rectangle-packing
|
|
@ -1,258 +0,0 @@
|
|||
// ----------------------------------------------------------------------------------------
|
||||
// Name : rect_placement.cpp
|
||||
// Description : A class that fits subrectangles into a power-of-2 rectangle
|
||||
// (C) Copyright 2000-2002 by Javier Arevalo
|
||||
// This code is free to use and modify for all purposes
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* You have a bunch of rectangular pieces. You need to arrange them in a
|
||||
* rectangular surface so that they don't overlap, keeping the total area of the
|
||||
* rectangle as small as possible. This is fairly common when arranging characters
|
||||
* in a bitmapped font, lightmaps for a 3D engine, and I guess other situations as
|
||||
* well.
|
||||
*
|
||||
* The idea of this algorithm is that, as we add rectangles, we can pre-select
|
||||
* "interesting" places where we can try to add the next rectangles. For optimal
|
||||
* results, the rectangles should be added in order. I initially tried using area
|
||||
* as a sorting criteria, but it didn't work well with very tall or very flat
|
||||
* rectangles. I then tried using the longest dimension as a selector, and it
|
||||
* worked much better. So much for intuition...
|
||||
*
|
||||
* These "interesting" places are just to the right and just below the currently
|
||||
* added rectangle. The first rectangle, obviously, goes at the top left, the next
|
||||
* one would go either to the right or below this one, and so on. It is a weird way
|
||||
* to do it, but it seems to work very nicely.
|
||||
*
|
||||
* The way we search here is fairly brute-force, the fact being that for most off-
|
||||
* line purposes the performance seems more than adequate. I have generated a
|
||||
* japanese font with around 8500 characters and all the time was spent generating
|
||||
* the bitmaps.
|
||||
*
|
||||
* Also, for all we care, we could grow the parent rectangle.
|
||||
*
|
||||
* I'd be interested in hearing of other approaches to this problem. Make sure
|
||||
* to post them on http://www.flipcode.com
|
||||
*/
|
||||
|
||||
#include "rect_placement.h"
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Name :
|
||||
// Description :
|
||||
// --------------------------------------------------------------------------------
|
||||
void CRectPlacement::Init( int w, int h )
|
||||
{
|
||||
End();
|
||||
m_size = TRect( 0, 0, w, h );
|
||||
m_vPositions.push_back( TPos( 0, 0 ) );
|
||||
m_area = 0;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Name :
|
||||
// Description :
|
||||
// --------------------------------------------------------------------------------
|
||||
void CRectPlacement::End()
|
||||
{
|
||||
m_vPositions.clear();
|
||||
m_vRects.clear();
|
||||
m_size.w = 0;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Name : IsFree
|
||||
// Description : Check if the given rectangle is partially or totally used
|
||||
// --------------------------------------------------------------------------------
|
||||
bool CRectPlacement::IsFree( const TRect& r ) const
|
||||
{
|
||||
if( !m_size.Contains( r ) )
|
||||
return false;
|
||||
|
||||
for( CRectArray::const_iterator it = m_vRects.begin();
|
||||
it != m_vRects.end(); ++it )
|
||||
{
|
||||
if( it->Intersects( r ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Name : AddPosition
|
||||
// Description : Add new anchor point
|
||||
// --------------------------------------------------------------------------------
|
||||
void CRectPlacement::AddPosition( const TPos& p )
|
||||
{
|
||||
// Try to insert anchor as close as possible to the top left corner
|
||||
// So it will be tried first
|
||||
bool bFound = false;
|
||||
CPosArray::iterator it;
|
||||
|
||||
for( it = m_vPositions.begin();
|
||||
!bFound && it != m_vPositions.end();
|
||||
++it )
|
||||
{
|
||||
if( p.x + p.y < it->x + it->y )
|
||||
bFound = true;
|
||||
}
|
||||
|
||||
if( bFound )
|
||||
m_vPositions.insert( it, p );
|
||||
else
|
||||
m_vPositions.push_back( p );
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Name : AddRect
|
||||
// Description : Add the given rect and updates anchor points
|
||||
// --------------------------------------------------------------------------------
|
||||
void CRectPlacement::AddRect( const TRect& r )
|
||||
{
|
||||
m_vRects.push_back( r );
|
||||
m_area += r.w * r.h;
|
||||
|
||||
// Add two new anchor points
|
||||
AddPosition( TPos( r.x, r.y + r.h ) );
|
||||
AddPosition( TPos( r.x + r.w, r.y ) );
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Name : AddAtEmptySpot
|
||||
// Description : Add the given rectangle
|
||||
// --------------------------------------------------------------------------------
|
||||
bool CRectPlacement::AddAtEmptySpot( TRect& r )
|
||||
{
|
||||
// Find a valid spot among available anchors.
|
||||
bool bFound = false;
|
||||
CPosArray::iterator it;
|
||||
|
||||
for( it = m_vPositions.begin();
|
||||
!bFound && it != m_vPositions.end();
|
||||
++it )
|
||||
{
|
||||
TRect Rect( it->x, it->y, r.w, r.h );
|
||||
|
||||
if( IsFree( Rect ) )
|
||||
{
|
||||
r = Rect;
|
||||
bFound = true;
|
||||
break; // Don't let the loop increase the iterator.
|
||||
}
|
||||
}
|
||||
|
||||
if( bFound )
|
||||
{
|
||||
int x, y;
|
||||
|
||||
// Remove the used anchor point
|
||||
m_vPositions.erase( it );
|
||||
|
||||
// Sometimes, anchors end up displaced from the optimal position
|
||||
// due to irregular sizes of the subrects.
|
||||
// So, try to adjut it up & left as much as possible.
|
||||
for( x = 1; x <= r.x; x++ )
|
||||
{
|
||||
if( !IsFree( TRect( r.x - x, r.y, r.w, r.h ) ) )
|
||||
break;
|
||||
}
|
||||
|
||||
for( y = 1; y <= r.y; y++ )
|
||||
{
|
||||
if( !IsFree( TRect( r.x, r.y - y, r.w, r.h ) ) )
|
||||
break;
|
||||
}
|
||||
|
||||
if( y > x )
|
||||
r.y -= y - 1;
|
||||
else
|
||||
r.x -= x - 1;
|
||||
|
||||
AddRect( r );
|
||||
}
|
||||
|
||||
return bFound;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Name : AddAtEmptySpotAutoGrow
|
||||
// Description : Add a rectangle of the given size, growing our area if needed
|
||||
// Area grows only until the max given.
|
||||
// Returns the placement of the rect in the rect's x,y coords
|
||||
// --------------------------------------------------------------------------------
|
||||
bool CRectPlacement::AddAtEmptySpotAutoGrow( TRect* pRect, int maxW, int maxH )
|
||||
{
|
||||
double growing_factor = 1.2; // Must be > 1.0, and event > 1.1 for fast optimization
|
||||
|
||||
#define GROW(x) static_cast<int>( (x * growing_factor) + 1 )
|
||||
|
||||
if( pRect->w <= 0 )
|
||||
return true;
|
||||
|
||||
int orgW = m_size.w;
|
||||
int orgH = m_size.h;
|
||||
|
||||
// Try to add it in the existing space
|
||||
while( !AddAtEmptySpot( *pRect ) )
|
||||
{
|
||||
int pw = m_size.w;
|
||||
int ph = m_size.h;
|
||||
|
||||
// Sanity check - if area is complete.
|
||||
if( pw >= maxW && ph >= maxH )
|
||||
{
|
||||
m_size.w = orgW;
|
||||
m_size.h = orgH;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try growing the smallest dim
|
||||
if( pw < maxW && ( pw < ph || ( (pw == ph) && (pRect->w >= pRect->h) ) ) )
|
||||
m_size.w = GROW( pw );
|
||||
else
|
||||
m_size.h = GROW( ph );
|
||||
|
||||
if( AddAtEmptySpot( *pRect ) )
|
||||
break;
|
||||
|
||||
// Try growing the other dim instead
|
||||
if( pw != m_size.w )
|
||||
{
|
||||
m_size.w = pw;
|
||||
|
||||
if( ph < maxW )
|
||||
m_size.h = GROW( ph );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_size.h = ph;
|
||||
|
||||
if( pw < maxW )
|
||||
m_size.w = GROW( pw );
|
||||
}
|
||||
|
||||
if( pw != m_size.w || ph != m_size.h )
|
||||
if( AddAtEmptySpot( *pRect ) )
|
||||
break;
|
||||
|
||||
|
||||
|
||||
// Grow both if possible, and reloop.
|
||||
m_size.w = pw;
|
||||
m_size.h = ph;
|
||||
|
||||
if( pw < maxW )
|
||||
m_size.w = GROW( pw );
|
||||
|
||||
if( ph < maxH )
|
||||
m_size.h = GROW( ph );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
// --------------------------------------------------------------------------------
|
||||
// Name : rect_placement.h
|
||||
// Description : A class that allocates subrectangles into power-of-2 rectangles
|
||||
// (C) Copyright 2000-2002 by Javier Arevalo
|
||||
// This code is free to use and modify for all purposes
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @file rect_placement.h
|
||||
*/
|
||||
|
||||
#ifndef _RECT_PLACEMENT_H_
|
||||
#define _RECT_PLACEMENT_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
class CRectPlacement
|
||||
{
|
||||
public:
|
||||
|
||||
// Helper classes
|
||||
struct TPos
|
||||
{
|
||||
int x, y;
|
||||
|
||||
TPos() : x( 0 ), y( 0 ) { }
|
||||
TPos( int _x, int _y ) : x( _x ), y( _y ) { }
|
||||
|
||||
bool operator ==( const TPos& p ) const { return x == p.x && y == p.y; }
|
||||
};
|
||||
|
||||
struct TRect : public TPos
|
||||
{
|
||||
int w, h;
|
||||
|
||||
TRect() : w( 0 ), h( 0 ) { }
|
||||
TRect( int _x, int _y, int _w, int _h ) : TPos( _x, _y ), w( _w > 0 ? _w : 0 ), h(
|
||||
_h > 0 ? _h : 0 ) { }
|
||||
|
||||
bool Contains( const TPos& p ) const
|
||||
{
|
||||
return p.x >= x && p.y >= y && p.x < (x + w) && p.y < (y + h);
|
||||
}
|
||||
bool Contains( const TRect& r ) const
|
||||
{
|
||||
return r.x >= x && r.y >= y &&
|
||||
(r.x + r.w) <= (x + w) && (r.y + r.h) <= (y + h);
|
||||
}
|
||||
bool Intersects( const TRect& r ) const
|
||||
{
|
||||
return w > 0 && h > 0 && r.w > 0 && r.h > 0
|
||||
&& ( (r.x + r.w) > x && r.x < (x + w) && (r.y + r.h) > y && r.y < (y + h) );
|
||||
}
|
||||
|
||||
// static bool Greater(const TRect &a, const TRect &b)
|
||||
// { return a.w*a.h > b.w*b.h; }
|
||||
// Greater rect area. Not as good as the next heuristic:
|
||||
// Greater size in at least one dim.
|
||||
static bool Greater( const TRect& a, const TRect& b )
|
||||
{
|
||||
return (a.w > b.w && a.w > b.h) || (a.h > b.w && a.h > b.h);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------
|
||||
|
||||
typedef std::vector<TPos> CPosArray;
|
||||
typedef std::vector<TRect> CRectArray;
|
||||
|
||||
// ---------------------
|
||||
|
||||
CRectPlacement() { Init(); }
|
||||
~CRectPlacement() { End(); }
|
||||
|
||||
void Init( int w = 1, int h = 1 );
|
||||
void End();
|
||||
|
||||
bool IsOk() const { return m_size.w > 0; }
|
||||
|
||||
int GetW() const { return m_size.w; }
|
||||
int GetH() const { return m_size.h; }
|
||||
double GetArea() const { return m_area; }
|
||||
double GetTotalArea() const { return (double)m_size.w * m_size.h; }
|
||||
|
||||
bool AddAtEmptySpotAutoGrow( TRect* pRect, int maxW, int maxH );
|
||||
|
||||
private:
|
||||
TRect m_size;
|
||||
CRectArray m_vRects;
|
||||
CPosArray m_vPositions;
|
||||
double m_area;
|
||||
|
||||
// ---------------------
|
||||
|
||||
bool IsFree( const TRect& r ) const;
|
||||
void AddPosition( const TPos& p );
|
||||
void AddRect( const TRect& r );
|
||||
bool AddAtEmptySpot( TRect& r );
|
||||
};
|
||||
|
||||
#endif // _RECT_PLACEMENT_H_
|
Loading…
Reference in New Issue