Blender V2.61 - r43446

boxpack2d.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * Contributor(s): Campbell Barton
00019  *
00020  * ***** END GPL LICENSE BLOCK *****
00021  */
00022 
00028 #include <stdlib.h> /* for qsort */
00029 
00030 #include "MEM_guardedalloc.h"
00031 #include "BLI_boxpack2d.h"
00032  
00033 /* BoxPacker for backing 2D rectangles into a square
00034  * 
00035  * The defined Below are for internal use only */
00036 
00037 typedef struct boxVert {
00038     float x;
00039     float y;
00040     short free;
00041 
00042     struct boxPack *trb; /* top right box */
00043     struct boxPack *blb; /* bottom left box */
00044     struct boxPack *brb; /* bottom right box */
00045     struct boxPack *tlb; /* top left box */
00046 
00047     /* Store last intersecting boxes here
00048      * speedup intersection testing */
00049     struct boxPack *isect_cache[4];
00050 
00051     int index;
00052 } boxVert;
00053 
00054 /* free vert flags */
00055 #define eps 0.0000001f
00056 #define BLF 1
00057 #define TRF 2
00058 #define TLF 4
00059 #define BRF 8
00060 #define CORNERFLAGS (BLF|TRF|TLF|BRF)
00061 
00062 #define BL 0
00063 #define TR 1
00064 #define TL 2
00065 #define BR 3
00066 
00067 #define BOXLEFT(b)      b->v[BL]->x
00068 #define BOXRIGHT(b)     b->v[TR]->x
00069 #define BOXBOTTOM(b)    b->v[BL]->y
00070 #define BOXTOP(b)       b->v[TR]->y
00071 #define BOXAREA(b)      (b->w * b->h)
00072 
00073 #define UPDATE_V34X(b)  b->v[TL]->x = b->v[BL]->x;\
00074                         b->v[BR]->x = b->v[TR]->x
00075 #define UPDATE_V34Y(b)  b->v[TL]->y = b->v[TR]->y;\
00076                         b->v[BR]->y = b->v[BL]->y
00077 #define UPDATE_V34(b) UPDATE_V34X(b); UPDATE_V34Y(b)  
00078 
00079 #define SET_BOXLEFT(b, f)   b->v[TR]->x = f + b->w;\
00080                             b->v[BL]->x = f;\
00081                             UPDATE_V34X(b)
00082 #define SET_BOXRIGHT(b, f)  b->v[BL]->x = f - b->w;\
00083                             b->v[TR]->x = f;\
00084                             UPDATE_V34X(b)
00085 #define SET_BOXBOTTOM(b, f) b->v[TR]->y = f + b->h;\
00086                             b->v[BL]->y = f;\
00087                             UPDATE_V34Y(b)
00088 #define SET_BOXTOP(b, f)    b->v[BL]->y = f - b->h;\
00089                             b->v[TR]->y = f;\
00090                             UPDATE_V34Y(b)
00091 #define BOXINTERSECT(b1, b2)\
00092     (!( BOXLEFT(b1)+eps>=BOXRIGHT(b2) ||\
00093         BOXBOTTOM(b1)+eps>=BOXTOP(b2) ||\
00094         BOXRIGHT(b1)-eps<=BOXLEFT(b2) ||\
00095         BOXTOP(b1)-eps<=BOXBOTTOM(b2) ))
00096 
00097 #define MIN2(x,y)               ( (x)<(y) ? (x) : (y) )
00098 #define MAX2(x,y)               ( (x)>(y) ? (x) : (y) )
00099 
00100 /* #define BOXDEBUG(b)\
00101  *      printf("\tBox Debug i %i, w:%.3f h:%.3f x:%.3f y:%.3f\n",\
00102  *      b->index, b->w, b->h, b->x, b->y) */
00103 
00104 /* qsort function - sort largest to smallest */
00105 static int box_areasort(const void *p1, const void *p2)
00106 {
00107     const boxPack *b1= p1, *b2= p2;
00108     const float a1= BOXAREA(b1);
00109     const float a2= BOXAREA(b2);
00110 
00111     if      ( a1 < a2 ) return  1;
00112     else if ( a1 > a2 ) return -1;
00113     return 0;
00114 }
00115 
00116 /* qsort vertex sorting function
00117  * sorts from lower left to top right It uses the current box's width and height 
00118  * as offsets when sorting, this has the result of not placing boxes outside
00119  * the bounds of the existing backed area where possible
00120  * */
00121 static float box_width;
00122 static float box_height;
00123 static boxVert *vertarray;
00124 
00125 static int vertex_sort(const void *p1, const void *p2)
00126 {
00127     boxVert *v1, *v2;
00128     float a1, a2;
00129     
00130     v1 = vertarray + ((int *) p1)[0];
00131     v2 = vertarray + ((int *) p2)[0];
00132     
00133     a1 = MAX2(v1->x+box_width, v1->y+box_height);
00134     a2 = MAX2(v2->x+box_width, v2->y+box_height);
00135     
00136     /* sort largest to smallest */
00137     if      ( a1 > a2 ) return  1;
00138     else if ( a1 < a2 ) return -1;
00139     return 0;
00140 }
00141 /* Main boxpacking function accessed from other functions
00142  * This sets boxes x,y to positive values, sorting from 0,0 outwards.
00143  * There is no limit to the space boxes may take, only that they will be packed
00144  * tightly into the lower left hand corner (0,0)
00145  * 
00146  * boxarray - a pre allocated array of boxes.
00147  *      only the 'box->x' and 'box->y' are set, 'box->w' and 'box->h' are used,
00148  *      'box->index' is not used at all, the only reason its there
00149  *          is that the box array is sorted by area and programs need to be able
00150  *          to have some way of writing the boxes back to the original data.
00151  *  len - the number of boxes in the array.
00152  *  tot_width and tot_height are set so you can normalize the data.
00153  *  */
00154 void boxPack2D(boxPack *boxarray, const int len, float *tot_width, float *tot_height)
00155 {
00156     boxVert *vert; /* the current vert */
00157     int box_index, verts_pack_len, i, j, k, isect;
00158     int quad_flags[4]= {BLF,TRF,TLF,BRF}; /* use for looping */
00159     boxPack *box, *box_test; /*current box and another for intersection tests*/
00160     int *vertex_pack_indices; /*an array of indices used for sorting verts*/
00161     
00162     if (!len) {
00163         *tot_width =  0.0f;
00164         *tot_height = 0.0f;
00165         return;
00166     }
00167     
00168     /* Sort boxes, biggest first */
00169     qsort(boxarray, len, sizeof(boxPack), box_areasort);
00170     
00171     /* add verts to the boxes, these are only used internally  */
00172     vert = vertarray = MEM_mallocN( len*4*sizeof(boxVert), "boxPack Verts");
00173     vertex_pack_indices = MEM_mallocN( len*3*sizeof(int), "boxPack Indices");
00174     
00175     for (box=boxarray, box_index=0, i=0; box_index < len; box_index++, box++) {
00176 
00177         vert->blb = vert->brb = vert->tlb =
00178                 vert->isect_cache[0] = vert->isect_cache[1] =
00179                 vert->isect_cache[2] = vert->isect_cache[3] = NULL;
00180         vert->free = CORNERFLAGS &~ TRF;
00181         vert->trb = box;
00182         vert->index = i; i++;
00183         box->v[BL] = vert; vert++;
00184         
00185         vert->trb= vert->brb = vert->tlb =
00186                 vert->isect_cache[0] = vert->isect_cache[1] =
00187                 vert->isect_cache[2] = vert->isect_cache[3] = NULL;
00188         vert->free = CORNERFLAGS &~ BLF;
00189         vert->blb = box;
00190         vert->index = i; i++;
00191         box->v[TR] = vert; vert++;
00192         
00193         vert->trb = vert->blb = vert->tlb =
00194                 vert->isect_cache[0] = vert->isect_cache[1] =
00195                 vert->isect_cache[2] = vert->isect_cache[3] = NULL;
00196         vert->free = CORNERFLAGS &~ BRF;
00197         vert->brb = box;
00198         vert->index = i; i++;
00199         box->v[TL] = vert; vert++;
00200         
00201         vert->trb = vert->blb = vert->brb =
00202                 vert->isect_cache[0] = vert->isect_cache[1] =
00203                 vert->isect_cache[2] = vert->isect_cache[3] = NULL;
00204         vert->free = CORNERFLAGS &~ TLF;
00205         vert->tlb = box; 
00206         vert->index = i; i++;
00207         box->v[BR] = vert; vert++;
00208     }
00209     vert = NULL;
00210     
00211     
00212     /* Pack the First box!
00213      * then enter the main box-packing loop */
00214     
00215     box = boxarray; /* get the first box  */
00216     /* First time, no boxes packed */
00217     box->v[BL]->free = 0; /* Can't use any if these */
00218     box->v[BR]->free &= ~(BLF|BRF);
00219     box->v[TL]->free &= ~(BLF|TLF);
00220     
00221     *tot_width = box->w;
00222     *tot_height = box->h; 
00223     
00224     /* This sets all the vertex locations */
00225     SET_BOXLEFT(box, 0.0f);
00226     SET_BOXBOTTOM(box, 0.0f);
00227     box->x = box->y = 0.0f;
00228     
00229     for (i=0; i<3; i++)
00230         vertex_pack_indices[i] = box->v[i+1]->index; 
00231     verts_pack_len = 3;
00232     box++; /* next box, needed for the loop below */
00233     /* ...done packing the first box */
00234 
00235     /* Main boxpacking loop */
00236     for (box_index=1; box_index < len; box_index++, box++) {
00237         
00238         /* These static floatds are used for sorting */
00239         box_width = box->w;
00240         box_height = box->h;
00241         
00242         qsort(vertex_pack_indices, verts_pack_len, sizeof(int), vertex_sort);
00243         
00244         /* Pack the box in with the others */
00245         /* sort the verts */
00246         isect = 1;
00247         
00248         for (i=0; i<verts_pack_len && isect; i++) {
00249             vert = vertarray + vertex_pack_indices[i];
00250             /* printf("\ttesting vert %i %i %i %f %f\n", i,
00251              *      vert->free, verts_pack_len, vert->x, vert->y); */
00252             
00253             /* This vert has a free quadrant
00254              * Test if we can place the box here
00255              * vert->free & quad_flags[j] - Checks 
00256              * */
00257                         
00258             for (j=0; (j<4) && isect; j++) {
00259                 if (vert->free & quad_flags[j]) {
00260                     switch (j) {
00261                     case BL:
00262                         SET_BOXRIGHT(box, vert->x);
00263                         SET_BOXTOP(box, vert->y);
00264                         break;
00265                     case TR:
00266                         SET_BOXLEFT(box, vert->x);
00267                         SET_BOXBOTTOM(box, vert->y);
00268                         break;
00269                     case TL:
00270                         SET_BOXRIGHT(box, vert->x);
00271                         SET_BOXBOTTOM(box, vert->y);
00272                         break;
00273                     case BR:
00274                         SET_BOXLEFT(box, vert->x);
00275                         SET_BOXTOP(box, vert->y);
00276                         break;
00277                     }
00278                     
00279                     /* Now we need to check that the box intersects
00280                       * with any other boxes
00281                       * Assume no intersection... */
00282                     isect = 0;
00283                     
00284                     if (/* Constrain boxes to positive X/Y values */
00285                         BOXLEFT(box)<0.0f || BOXBOTTOM(box) < 0.0f ||
00286                         /* check for last intersected */
00287                         (   vert->isect_cache[j] &&
00288                             BOXINTERSECT(box, vert->isect_cache[j]) )
00289                        ) {
00290                         /* Here we check that the last intersected
00291                          * box will intersect with this one using
00292                          * isect_cache that can store a pointer to a
00293                          * box for each quadrant
00294                          * big speedup */
00295                         isect = 1;
00296                     } else {
00297                         /* do a full search for colliding box
00298                          * this is really slow, some spacialy divided
00299                          * data-structure would be better */
00300                         for (box_test=boxarray; box_test != box; box_test++) {
00301                             if BOXINTERSECT(box, box_test) {
00302                                 /* Store the last intersecting here as cache
00303                                  * for faster checking next time around */
00304                                 vert->isect_cache[j] = box_test;
00305                                 isect = 1;
00306                                 break;
00307                             }
00308                         }
00309                     }
00310                     
00311                     if (!isect) {
00312                         
00313                         /* maintain the total width and height */
00314                         (*tot_width) = MAX2(BOXRIGHT(box), (*tot_width));
00315                         (*tot_height) = MAX2(BOXTOP(box), (*tot_height));
00316                         
00317                         /* Place the box */
00318                         vert->free &= ~quad_flags[j];
00319                         
00320                         switch (j) {
00321                         case TR:
00322                             box->v[BL]= vert;
00323                             vert->trb = box;
00324                              break;
00325                         case TL:
00326                             box->v[BR]= vert;
00327                             vert->tlb = box;
00328                              break;
00329                         case BR:
00330                             box->v[TL]= vert;
00331                             vert->brb = box;
00332                             break;
00333                         case BL:
00334                             box->v[TR]= vert;
00335                             vert->blb = box;
00336                              break;
00337                         }
00338                         
00339                         /* Mask free flags for verts that are
00340                          * on the bottom or side so we don't get
00341                          * boxes outside the given rectangle ares
00342                          * 
00343                          * We can do an else/if here because only the first 
00344                          * box can be at the very bottom left corner */
00345                         if (BOXLEFT(box) <= 0) {
00346                             box->v[TL]->free &= ~(TLF|BLF);
00347                             box->v[BL]->free &= ~(TLF|BLF);             
00348                         } else if (BOXBOTTOM(box) <= 0) {
00349                             box->v[BL]->free &= ~(BRF|BLF);
00350                             box->v[BR]->free &= ~(BRF|BLF);
00351                         }
00352                         
00353                         /* The following block of code does a logical
00354                          * check with 2 adjacent boxes, its possible to
00355                          * flag verts on one or both of the boxes 
00356                          * as being used by checking the width or
00357                          * height of both boxes */
00358                         if (vert->tlb && vert->trb &&
00359                                     (box == vert->tlb || box == vert->trb)) {
00360                             if (vert->tlb->h > vert->trb->h) {
00361                                 vert->trb->v[TL]->free &= ~(TLF|BLF);
00362                             } else if (vert->tlb->h < vert->trb->h) {
00363                                 vert->tlb->v[TR]->free &= ~(TRF|BRF);
00364                             } else { /*same*/
00365                                 vert->tlb->v[TR]->free &= ~BLF;
00366                                 vert->trb->v[TL]->free &= ~BRF;
00367                             }
00368                         } else if (vert->blb && vert->brb &&
00369                                     (box == vert->blb || box == vert->brb)) {
00370                             if (vert->blb->h > vert->brb->h) {
00371                                 vert->brb->v[BL]->free &= ~(TLF|BLF);
00372                             } else if (vert->blb->h < vert->brb->h) {
00373                                 vert->blb->v[BR]->free &= ~(TRF|BRF);
00374                             } else { /*same*/
00375                                 vert->blb->v[BR]->free &= ~TRF;
00376                                 vert->brb->v[BL]->free &= ~TLF;
00377                             }
00378                         }
00379                         /* Horizontal */
00380                         if (vert->tlb && vert->blb &&
00381                                     (box == vert->tlb || box == vert->blb) ) {
00382                             if (vert->tlb->w > vert->blb->w) {
00383                                 vert->blb->v[TL]->free &= ~(TLF|TRF);
00384                             } else if (vert->tlb->w < vert->blb->w) {
00385                                 vert->tlb->v[BL]->free &= ~(BLF|BRF);
00386                             } else { /*same*/
00387                                 vert->blb->v[TL]->free &= ~TRF;
00388                                 vert->tlb->v[BL]->free &= ~BRF;
00389                             }
00390                         } else if ( vert->trb && vert->brb &&
00391                                     (box == vert->trb || box == vert->brb) ) {
00392                             if (vert->trb->w > vert->brb->w) {
00393                                 vert->brb->v[TR]->free &= ~(TRF|TRF);
00394                             } else if (vert->trb->w < vert->brb->w) {
00395                                 vert->trb->v[BR]->free &= ~(BLF|BRF);
00396                             } else { /*same*/
00397                                 vert->brb->v[TR]->free &= ~TLF;
00398                                 vert->trb->v[BR]->free &= ~BLF;
00399                             }
00400                         }
00401                         /* End logical check */
00402                         
00403                         
00404                         for (k=0; k<4; k++) {
00405                             if (box->v[k] != vert) {
00406                                 vertex_pack_indices[verts_pack_len] =
00407                                             box->v[k]->index; 
00408                                 verts_pack_len++;
00409                             }
00410                         }
00411                         /* The Box verts are only used internally
00412                          * Update the box x and y since thats what external
00413                          * functions will see */
00414                         box->x = BOXLEFT(box);
00415                         box->y = BOXBOTTOM(box);
00416                     }
00417                 }   
00418             }
00419         }
00420     }
00421 
00422     /* free all the verts, not really needed because they shouldn't be
00423      * touched anymore but accessing the pointers would crash blender */
00424     for (box_index=0; box_index < len; box_index++) {
00425         box = boxarray+box_index; 
00426         box->v[0] = box->v[1] = box->v[2] = box->v[3] = NULL; 
00427     }
00428     MEM_freeN(vertex_pack_indices);
00429     MEM_freeN(vertarray);
00430 }
00431