RectPlacement.cpp
上传用户:jnfxsk
上传日期:2022-06-16
资源大小:3675k
文件大小:8k
源码类别:

游戏引擎

开发平台:

Visual C++

  1. // ----------------------------------------------------------------------------------------
  2. // Name        : RectPlacement.cpp
  3. // Description : A class that fits subrectangles into a power-of-2 rectangle
  4. //               (C) Copyright 2000-2002 by Javier Arevalo
  5. //               This code is free to use and modify for all purposes
  6. // ----------------------------------------------------------------------------------------
  7. // modified to support sprite margins and VC8
  8. // (C) Copyright 2008 by Haaf
  9. // --------------------------------------------------------------------------------
  10. /*
  11.   You have a bunch of rectangular pieces. You need to arrange them in a 
  12.   rectangular surface so that they don't overlap, keeping the total area of the 
  13.   rectangle as small as possible. This is fairly common when arranging characters 
  14.   in a bitmapped font, lightmaps for a 3D engine, and I guess other situations as 
  15.   well.
  16.   The idea of this algorithm is that, as we add rectangles, we can pre-select 
  17.   "interesting" places where we can try to add the next rectangles. For optimal 
  18.   results, the rectangles should be added in order. I initially tried using area 
  19.   as a sorting criteria, but it didn't work well with very tall or very flat 
  20.   rectangles. I then tried using the longest dimension as a selector, and it 
  21.   worked much better. So much for intuition...
  22.   These "interesting" places are just to the right and just below the currently 
  23.   added rectangle. The first rectangle, obviously, goes at the top left, the next 
  24.   one would go either to the right or below this one, and so on. It is a weird way 
  25.   to do it, but it seems to work very nicely.
  26.   The way we search here is fairly brute-force, the fact being that for most off-
  27.   line purposes the performance seems more than adequate. I have generated a 
  28.   japanese font with around 8500 characters and all the time was spent generating 
  29.   the bitmaps.
  30.   Also, for all we care, we could grow the parent rectangle in a different way 
  31.   than power of two. It just happens that power of 2 is very convenient for 
  32.   graphics hardware textures.
  33.   I'd be interested in hearing of other approaches to this problem. Make sure
  34.   to post them on http://www.flipcode.com
  35. */
  36. #include "RectPlacement.h"
  37. int CRectPlacement::m_margin = 1;
  38. // --------------------------------------------------------------------------------
  39. // Name        : 
  40. // Description : 
  41. // --------------------------------------------------------------------------------
  42. void CRectPlacement::Init    (int w, int h)
  43. {
  44.   End();
  45.   m_size = TRect(0, 0, w, h);
  46.   m_vPositions.push_back(TPos(m_margin, m_margin));
  47.   m_area = 0;
  48. }
  49. // --------------------------------------------------------------------------------
  50. // Name        : 
  51. // Description : 
  52. // --------------------------------------------------------------------------------
  53. void CRectPlacement::End     ()
  54. {
  55.   m_vPositions.clear();
  56.   m_vRects.clear();
  57.   m_size.w = 0;
  58. }
  59. // --------------------------------------------------------------------------------
  60. // Name        : IsFree
  61. // Description : Check if the given rectangle is partially or totally used
  62. // --------------------------------------------------------------------------------
  63. bool CRectPlacement::IsFree (const TRect &r) const
  64. {
  65.   if (!m_size.Contains(r))
  66.     return false;
  67.   for (CRectArray::const_iterator it = m_vRects.begin();
  68.        it != m_vRects.end();
  69.        ++it)
  70.     if (it->Intersects(r))
  71.       return false;
  72.   return true;
  73. }
  74. // --------------------------------------------------------------------------------
  75. // Name        : AddPosition
  76. // Description : Add new anchor point
  77. // --------------------------------------------------------------------------------
  78. void CRectPlacement::AddPosition    (const TPos &p)
  79. {
  80.   // Try to insert anchor as close as possible to the top left corner
  81.   // So it will be tried first
  82.   bool bFound = false;
  83.   CPosArray::iterator it;
  84.   for (it = m_vPositions.begin();
  85.        !bFound && it != m_vPositions.end();
  86.        ++it)
  87.   {
  88.     if (p.x+p.y < it->x+it->y)
  89.       bFound = true;
  90.   }
  91.   if (bFound)
  92.     m_vPositions.insert(it, p);
  93.   else
  94.     m_vPositions.push_back(p);
  95. }
  96. // --------------------------------------------------------------------------------
  97. // Name        : AddRect
  98. // Description : Add the given rect and updates anchor points
  99. // --------------------------------------------------------------------------------
  100. void CRectPlacement::AddRect  (const TRect &r)
  101. {
  102.   m_vRects.push_back(r);
  103.   m_area += r.w*r.h;
  104.   // Add two new anchor points
  105.   AddPosition(TPos(r.x, r.y+r.h+m_margin));
  106.   AddPosition(TPos(r.x+r.w+m_margin, r.y));
  107. }
  108. // --------------------------------------------------------------------------------
  109. // Name        : AddAtEmptySpot
  110. // Description : Add the given rectangle
  111. // --------------------------------------------------------------------------------
  112. bool CRectPlacement::AddAtEmptySpot   (TRect &r)
  113. {
  114.   // Find a valid spot among available anchors.
  115.   bool bFound = false;
  116.   CPosArray::iterator it;
  117.   for (it = m_vPositions.begin();
  118.        !bFound && it != m_vPositions.end();
  119.        ++it)
  120.   {
  121.     TRect Rect(it->x, it->y, r.w, r.h);
  122.     if (IsFree(Rect))
  123.     {
  124.       r = Rect;
  125.       bFound = true;
  126.       break; // Don't let the loop increase the iterator.
  127.     }
  128.   }
  129.   if (bFound)
  130.   {
  131.     // Remove the used anchor point
  132.     m_vPositions.erase(it);
  133.     // Sometimes, anchors end up displaced from the optimal position
  134.     // due to irregular sizes of the subrects.
  135.     // So, try to adjut it up & left as much as possible.
  136. int x,y;
  137.     for (x = 1; x <= r.x; x++)
  138.       if (!IsFree(TRect(r.x - x, r.y, r.w, r.h)))
  139.         break;
  140.     for (y = 1; y <= r.y; y++)
  141.       if (!IsFree(TRect(r.x, r.y - y, r.w, r.h)))
  142.         break;
  143.     if (y > x)
  144.       r.y -= y-1;
  145.     else
  146.       r.x -= x-1;
  147.     AddRect(r);
  148.   }
  149.   return bFound;
  150. }
  151. // --------------------------------------------------------------------------------
  152. // Name        : AddAtEmptySpotAutoGrow
  153. // Description : Add a rectangle of the given size, growing our area if needed
  154. //               Area grows only until the max given.
  155. //               Returns the placement of the rect in the rect's x,y coords
  156. // --------------------------------------------------------------------------------
  157. bool CRectPlacement::AddAtEmptySpotAutoGrow   (TRect *pRect, int maxW, int maxH)
  158. {
  159.   if (pRect->w <= 0)
  160.     return true;
  161.   int orgW = m_size.w;
  162.   int orgH = m_size.h;
  163.   // Try to add it in the existing space
  164.   while (!AddAtEmptySpot(*pRect))
  165.   {
  166.     int pw = m_size.w;
  167.     int ph = m_size.h;
  168.     // Sanity check - if area is complete.
  169.     if (pw >= maxW && ph >= maxH)
  170.     {
  171.       m_size.w = orgW;
  172.       m_size.h = orgH;
  173.       return false;
  174.     }
  175.     // Try growing the smallest dim
  176.     if (pw < maxW && (pw < ph || ((pw == ph) && (pRect->w >= pRect->h))))
  177.       m_size.w = pw*2;
  178.     else
  179.       m_size.h = ph*2;
  180.     if (AddAtEmptySpot(*pRect))
  181.       break;
  182.     // Try growing the other dim instead
  183.     if (pw != m_size.w)
  184.     {
  185.       m_size.w = pw;
  186.       if (ph < maxW)
  187.         m_size.h = ph*2;
  188.     }
  189.     else
  190.     {
  191.       m_size.h = ph;
  192.       if (pw < maxW)
  193.         m_size.w = pw*2;
  194.     }
  195.     if (pw != m_size.w || ph != m_size.h)
  196.       if (AddAtEmptySpot(*pRect))
  197.         break;
  198.     // Grow both if possible, and reloop.
  199.     m_size.w = pw;
  200.     m_size.h = ph;
  201.     if (pw < maxW)
  202.       m_size.w = pw*2;
  203.     if (ph < maxH)
  204.       m_size.h = ph*2;
  205.   }
  206.   return true;
  207. }
  208.