/* SCCS Id: @(#)gr_rect.c 3.5 2001/12/10 */ /* Copyright (c) Christian Bressler, 2001 */ /* NetHack may be freely redistributed. See license for details. */ /* This is an almost exact copy of qt_clust.cpp */ /* gr_rect.c */ #include #include #include #include "gr_rect.h" dirty_rect *new_dirty_rect(int size){ dirty_rect *new=NULL; if(size>0){ new=(dirty_rect *)calloc(1L,sizeof(dirty_rect)); if(new){ new->rects=(GRECT *)calloc((long)size,sizeof(GRECT)); if(new->rects==NULL){ free(new); return(NULL); } new->max=size; } } return(new); } void delete_dirty_rect(dirty_rect *this){ if(this==NULL) return; if(this->rects) free(this->rects); /* In case the Pointer is reused wrongly */ this->rects=NULL; this->max=0; this->used=0; free(this); } static int gc_inside(GRECT *frame,GRECT *test); static int gc_touch(GRECT *frame,GRECT *test); static void gc_combine(GRECT *frame,GRECT *test); static long gc_area(GRECT *area); int add_dirty_rect(dirty_rect *dr,GRECT *area){ int cursor; long lowestcost=9999999L; int cheapest=-1; int cheapestmerge1=-1; int cheapestmerge2=-1; int merge1; int merge2; for (cursor=0; cursorused; cursor++) { if (gc_inside(&dr->rects[cursor],area)) { /* Wholly contained already. */ return(TRUE); } } for (cursor=0; cursorused; cursor++) { if (gc_touch(&dr->rects[cursor],area)) { GRECT larger=dr->rects[cursor]; long cost; gc_combine(&larger,area); cost=gc_area(&larger)-gc_area(&dr->rects[cursor]); if (cost < lowestcost) { int bad=FALSE,c; for (c=0; cused && !bad; c++) { bad=gc_touch(&dr->rects[c],&larger) && c!=cursor; } if (!bad) { cheapest=cursor; lowestcost=cost; } } } } if (cheapest>=0) { gc_combine(&dr->rects[cheapest],area); return(TRUE); } if (dr->used < dr->max) { dr->rects[dr->used++]=*area; return(TRUE); } // Do cheapest of: // add to closest cluster // do cheapest cluster merge, add to new cluster lowestcost=9999999L; cheapest=-1; for (cursor=0; cursorused; cursor++) { GRECT larger=dr->rects[cursor]; long cost; gc_combine(&larger,area); cost=gc_area(&larger)-gc_area(&dr->rects[cursor]); if (cost < lowestcost) { int bad=FALSE, c; for (c=0; cused && !bad; c++) { bad=gc_touch(&dr->rects[c],&larger) && c!=cursor; } if (!bad) { cheapest=cursor; lowestcost=cost; } } } // XXX could make an heuristic guess as to whether we // XXX need to bother looking for a cheap merge. for (merge1=0; merge1used; merge1++) { for (merge2=0; merge2used; merge2++) { if (merge1!=merge2) { GRECT larger=dr->rects[merge1]; long cost; gc_combine(&larger,&dr->rects[merge2]); cost=gc_area(&larger)-gc_area(&dr->rects[merge1])-gc_area(&dr->rects[merge2]); if (cost < lowestcost) { int bad=FALSE, c; for (c=0; cused && !bad; c++) { bad=gc_touch(&dr->rects[c],&larger) && c!=cursor; } if (!bad) { cheapestmerge1=merge1; cheapestmerge2=merge2; lowestcost=cost; } } } } } if (cheapestmerge1>=0) { gc_combine(&dr->rects[cheapestmerge1],&dr->rects[cheapestmerge2]); dr->rects[cheapestmerge2]=dr->rects[dr->used-1]; dr->rects[dr->used-1]=*area; } else { gc_combine(&dr->rects[cheapest],area); } // NB: clusters do not intersect (or intersection will // overwrite). This is a result of the above algorithm, // given the assumption that (x,y) are ordered topleft // to bottomright. return(TRUE); } int get_dirty_rect(dirty_rect* dr,GRECT *area){ if(dr==NULL || area==NULL || dr->rects==NULL || dr->used<=0 || dr->max<=0) return(FALSE); *area=dr->rects[--dr->used]; return(TRUE); } int clear_dirty_rect(dirty_rect *dr){ if(dr) dr->used=0; return(TRUE); } int resize_dirty_rect(dirty_rect *dr,int new_size){ return(FALSE); } static int gc_inside(GRECT *frame,GRECT *test){ if(frame && test && frame->g_x<=test->g_x && frame->g_y<=test->g_y && frame->g_x+frame->g_w>=test->g_x+test->g_w && frame->g_y+frame->g_h>=test->g_y+test->g_h ) return(TRUE); return(FALSE); } static int gc_touch(GRECT *frame,GRECT *test){ GRECT tmp={test->g_x-1,test->g_y-1,test->g_w+2,test->g_h+2}; return(rc_intersect(frame,&tmp)); } static void gc_combine(GRECT *frame,GRECT *test){ if(!frame || !test) return; if(frame->g_x>test->g_x){ frame->g_w+=frame->g_x-test->g_x; frame->g_x=test->g_x; } if(frame->g_y>test->g_y){ frame->g_h+=frame->g_y-test->g_y; frame->g_y=test->g_y; } if(frame->g_x+frame->g_wg_x+test->g_w) frame->g_w=test->g_x+test->g_w-frame->g_x; if(frame->g_y+frame->g_hg_y+test->g_h) frame->g_h=test->g_y+test->g_h-frame->g_y; } static long gc_area(GRECT *area){ return((long)area->g_h*(long)area->g_w); }