184 lines
4.9 KiB
C
184 lines
4.9 KiB
C
/* NetHack 3.5 gr_rect.c $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */
|
|
/* NetHack 3.5 gr_rect.c $Date: 2009/05/06 10:56:40 $ $Revision: 1.3 $ */
|
|
/* 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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#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; cursor<dr->used; cursor++) {
|
|
if (gc_inside(&dr->rects[cursor],area)) {
|
|
/* Wholly contained already. */
|
|
return(TRUE);
|
|
}
|
|
}
|
|
for (cursor=0; cursor<dr->used; 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; c<dr->used && !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; cursor<dr->used; 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; c<dr->used && !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; merge1<dr->used; merge1++) {
|
|
for (merge2=0; merge2<dr->used; 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; c<dr->used && !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_w<test->g_x+test->g_w)
|
|
frame->g_w=test->g_x+test->g_w-frame->g_x;
|
|
if(frame->g_y+frame->g_h<test->g_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);
|
|
}
|