xenia-canary/third_party/crunch/crnlib/crn_arealist.cpp

699 lines
19 KiB
C++

// File: crn_arealist.cpp - 2D shape algebra (currently unused)
// See Copyright Notice and license at the end of inc/crnlib.h
// Ported from the PowerView DOS image viewer, a product I wrote back in 1993. Not currently used in the open source release of crnlib.
#include "crn_core.h"
#include "crn_arealist.h"
#define RECT_DEBUG
namespace crnlib
{
static void area_fatal_error(const char* pFunc, const char* pMsg, ...)
{
pFunc;
va_list args;
va_start(args, pMsg);
char buf[512];
#ifdef _MSC_VER
_vsnprintf_s(buf, sizeof(buf), pMsg, args);
#else
vsnprintf(buf, sizeof(buf), pMsg, args);
#endif
va_end(args);
CRNLIB_FAIL(buf);
}
static Area * delete_area(Area_List *Plist, Area *Parea)
{
Area *p, *q;
#ifdef RECT_DEBUG
if ((Parea == Plist->Phead) || (Parea == Plist->Ptail))
area_fatal_error("delete_area", "tried to remove head or tail");
#endif
p = Parea->Pprev;
q = Parea->Pnext;
p->Pnext = q;
q->Pprev = p;
Parea->Pnext = Plist->Pfree;
Parea->Pprev = NULL;
Plist->Pfree = Parea;
return (q);
}
static Area * alloc_area(Area_List *Plist)
{
Area *p = Plist->Pfree;
if (p == NULL)
{
if (Plist->next_free == Plist->total_areas)
area_fatal_error("alloc_area", "Out of areas!");
p = Plist->Phead + Plist->next_free;
Plist->next_free++;
}
else
Plist->Pfree = p->Pnext;
return (p);
}
static Area * insert_area_before(Area_List *Plist, Area *Parea,
int x1, int y1, int x2, int y2)
{
Area *p, *Pnew_area = alloc_area(Plist);
p = Parea->Pprev;
p->Pnext = Pnew_area;
Pnew_area->Pprev = p;
Pnew_area->Pnext = Parea;
Parea->Pprev = Pnew_area;
Pnew_area->x1 = x1;
Pnew_area->y1 = y1;
Pnew_area->x2 = x2;
Pnew_area->y2 = y2;
return (Pnew_area);
}
static Area * insert_area_after(Area_List *Plist, Area *Parea,
int x1, int y1, int x2, int y2)
{
Area *p, *Pnew_area = alloc_area(Plist);
p = Parea->Pnext;
p->Pprev = Pnew_area;
Pnew_area->Pnext = p;
Pnew_area->Pprev = Parea;
Parea->Pnext = Pnew_area;
Pnew_area->x1 = x1;
Pnew_area->y1 = y1;
Pnew_area->x2 = x2;
Pnew_area->y2 = y2;
return (Pnew_area);
}
void Area_List_deinit(Area_List* Pobj_base)
{
Area_List *Plist = (Area_List *)Pobj_base;
if (!Plist)
return;
if (Plist->Phead)
{
crnlib_free(Plist->Phead);
Plist->Phead = NULL;
}
crnlib_free(Plist);
}
Area_List * Area_List_init(int max_areas)
{
Area_List *Plist = (Area_List*)crnlib_calloc(1, sizeof(Area_List));
Plist->total_areas = max_areas + 2;
Plist->Phead = (Area *)crnlib_calloc(max_areas + 2, sizeof(Area));
Plist->Ptail = Plist->Phead + 1;
Plist->Phead->Pprev = NULL;
Plist->Phead->Pnext = Plist->Ptail;
Plist->Ptail->Pprev = Plist->Phead;
Plist->Ptail->Pnext = NULL;
Plist->Pfree = NULL;
Plist->next_free = 2;
return (Plist);
}
void Area_List_print(Area_List *Plist)
{
Area *Parea = Plist->Phead->Pnext;
while (Parea != Plist->Ptail)
{
printf("%04i %04i : %04i %04i\n", Parea->x1, Parea->y1, Parea->x2, Parea->y2);
Parea = Parea->Pnext;
}
}
Area_List * Area_List_dup_new(Area_List *Plist,
int x_ofs, int y_ofs)
{
int i;
Area_List *Pnew_list = (Area_List*)crnlib_calloc(1, sizeof(Area_List));
Pnew_list->total_areas = Plist->total_areas;
Pnew_list->Phead = (Area *)crnlib_malloc(sizeof(Area) * Plist->total_areas);
Pnew_list->Ptail = Pnew_list->Phead + 1;
Pnew_list->Pfree = (Plist->Pfree) ? ((Plist->Pfree - Plist->Phead) + Pnew_list->Phead) : NULL;
Pnew_list->next_free = Plist->next_free;
memcpy(Pnew_list->Phead, Plist->Phead, sizeof(Area) * Plist->total_areas);
for (i = 0; i < Plist->total_areas; i++)
{
Pnew_list->Phead[i].Pnext = (Plist->Phead[i].Pnext == NULL) ? NULL : (Plist->Phead[i].Pnext - Plist->Phead) + Pnew_list->Phead;
Pnew_list->Phead[i].Pprev = (Plist->Phead[i].Pprev == NULL) ? NULL : (Plist->Phead[i].Pprev - Plist->Phead) + Pnew_list->Phead;
Pnew_list->Phead[i].x1 += x_ofs;
Pnew_list->Phead[i].y1 += y_ofs;
Pnew_list->Phead[i].x2 += x_ofs;
Pnew_list->Phead[i].y2 += y_ofs;
}
return (Pnew_list);
}
uint Area_List_get_num(Area_List* Plist)
{
uint num = 0;
Area *Parea = Plist->Phead->Pnext;
while (Parea != Plist->Ptail)
{
num++;
Parea = Parea->Pnext;
}
return num;
}
void Area_List_dup(Area_List *Psrc_list, Area_List *Pdst_list,
int x_ofs, int y_ofs)
{
int i;
if (Psrc_list->total_areas != Pdst_list->total_areas)
area_fatal_error("Area_List_dup", "Src and Dst total_areas must be equal!");
Pdst_list->Pfree = (Psrc_list->Pfree) ? ((Psrc_list->Pfree - Psrc_list->Phead) + Pdst_list->Phead) : NULL;
Pdst_list->next_free = Psrc_list->next_free;
memcpy(Pdst_list->Phead, Psrc_list->Phead, sizeof(Area) * Psrc_list->total_areas);
if ((x_ofs) || (y_ofs))
{
for (i = 0; i < Psrc_list->total_areas; i++)
{
Pdst_list->Phead[i].Pnext = (Psrc_list->Phead[i].Pnext == NULL) ? NULL : (Psrc_list->Phead[i].Pnext - Psrc_list->Phead) + Pdst_list->Phead;
Pdst_list->Phead[i].Pprev = (Psrc_list->Phead[i].Pprev == NULL) ? NULL : (Psrc_list->Phead[i].Pprev - Psrc_list->Phead) + Pdst_list->Phead;
Pdst_list->Phead[i].x1 += x_ofs;
Pdst_list->Phead[i].y1 += y_ofs;
Pdst_list->Phead[i].x2 += x_ofs;
Pdst_list->Phead[i].y2 += y_ofs;
}
}
else
{
for (i = 0; i < Psrc_list->total_areas; i++)
{
Pdst_list->Phead[i].Pnext = (Psrc_list->Phead[i].Pnext == NULL) ? NULL : (Psrc_list->Phead[i].Pnext - Psrc_list->Phead) + Pdst_list->Phead;
Pdst_list->Phead[i].Pprev = (Psrc_list->Phead[i].Pprev == NULL) ? NULL : (Psrc_list->Phead[i].Pprev - Psrc_list->Phead) + Pdst_list->Phead;
}
}
}
void Area_List_copy(
Area_List *Psrc_list, Area_List *Pdst_list,
int x_ofs, int y_ofs)
{
Area *Parea = Psrc_list->Phead->Pnext;
Area_List_clear(Pdst_list);
if ((x_ofs) || (y_ofs))
{
Area *Pprev_area = Pdst_list->Phead;
while (Parea != Psrc_list->Ptail)
{
// Area *p, *Pnew_area;
Area *Pnew_area;
if (Pdst_list->next_free == Pdst_list->total_areas)
area_fatal_error("Area_List_copy", "Out of areas!");
Pnew_area = Pdst_list->Phead + Pdst_list->next_free;
Pdst_list->next_free++;
Pnew_area->Pprev = Pprev_area;
Pprev_area->Pnext = Pnew_area;
Pnew_area->x1 = Parea->x1 + x_ofs;
Pnew_area->y1 = Parea->y1 + y_ofs;
Pnew_area->x2 = Parea->x2 + x_ofs;
Pnew_area->y2 = Parea->y2 + y_ofs;
Pprev_area = Pnew_area;
Parea = Parea->Pnext;
}
Pprev_area->Pnext = Pdst_list->Ptail;
}
else
{
#if 0
while (Parea != Psrc_list->Ptail)
{
insert_area_after(Pdst_list, Pdst_list->Phead,
Parea->x1,
Parea->y1,
Parea->x2,
Parea->y2);
Parea = Parea->Pnext;
}
#endif
Area *Pprev_area = Pdst_list->Phead;
while (Parea != Psrc_list->Ptail)
{
// Area *p, *Pnew_area;
Area *Pnew_area;
if (Pdst_list->next_free == Pdst_list->total_areas)
area_fatal_error("Area_List_copy", "Out of areas!");
Pnew_area = Pdst_list->Phead + Pdst_list->next_free;
Pdst_list->next_free++;
Pnew_area->Pprev = Pprev_area;
Pprev_area->Pnext = Pnew_area;
Pnew_area->x1 = Parea->x1;
Pnew_area->y1 = Parea->y1;
Pnew_area->x2 = Parea->x2;
Pnew_area->y2 = Parea->y2;
Pprev_area = Pnew_area;
Parea = Parea->Pnext;
}
Pprev_area->Pnext = Pdst_list->Ptail;
}
}
void Area_List_clear(Area_List *Plist)
{
Plist->Phead->Pnext = Plist->Ptail;
Plist->Ptail->Pprev = Plist->Phead;
Plist->Pfree = NULL;
Plist->next_free = 2;
}
void Area_List_set(Area_List *Plist, int x1, int y1, int x2, int y2)
{
Plist->Pfree = NULL;
Plist->Phead[2].x1 = x1;
Plist->Phead[2].y1 = y1;
Plist->Phead[2].x2 = x2;
Plist->Phead[2].y2 = y2;
Plist->Phead[2].Pprev = Plist->Phead;
Plist->Phead->Pnext = Plist->Phead + 2;
Plist->Phead[2].Pnext = Plist->Ptail;
Plist->Ptail->Pprev = Plist->Phead + 2;
Plist->next_free = 3;
}
void Area_List_remove(Area_List *Plist,
int x1, int y1, int x2, int y2)
{
int l, h;
Area *Parea = Plist->Phead->Pnext;
#ifdef RECT_DEBUG
if ((x1 > x2) || (y1 > y2))
area_fatal_error("area_list_remove", "invalid coords: %i %i %i %i", x1, y1, x2, y2);
#endif
while (Parea != Plist->Ptail)
{
// Not touching
if ((x2 < Parea->x1) || (x1 > Parea->x2) ||
(y2 < Parea->y1) || (y1 > Parea->y2))
{
Parea = Parea->Pnext;
continue;
}
// Completely covers
if ((x1 <= Parea->x1) && (x2 >= Parea->x2) &&
(y1 <= Parea->y1) && (y2 >= Parea->y2))
{
if ((x1 == Parea->x1) && (x2 == Parea->x2) &&
(y1 == Parea->y1) && (y2 == Parea->y2))
{
delete_area(Plist, Parea);
return;
}
Parea = delete_area(Plist, Parea);
continue;
}
// top
if (y1 > Parea->y1)
{
insert_area_before(Plist, Parea,
Parea->x1, Parea->y1,
Parea->x2, y1 - 1);
}
// bottom
if (y2 < Parea->y2)
{
insert_area_before(Plist, Parea,
Parea->x1, y2 + 1,
Parea->x2, Parea->y2);
}
l = math::maximum(y1, Parea->y1);
h = math::minimum(y2, Parea->y2);
// left middle
if (x1 > Parea->x1)
{
insert_area_before(Plist, Parea,
Parea->x1, l,
x1 - 1, h);
}
// right middle
if (x2 < Parea->x2)
{
insert_area_before(Plist, Parea,
x2 + 1, l,
Parea->x2, h);
}
// early out - we know there's nothing else to remove, as areas can
// never overlap
if ((x1 >= Parea->x1) && (x2 <= Parea->x2) &&
(y1 >= Parea->y1) && (y2 <= Parea->y2))
{
delete_area(Plist, Parea);
return;
}
Parea = delete_area(Plist, Parea);
}
}
void Area_List_insert(Area_List *Plist,
int x1, int y1, int x2, int y2,
bool combine)
{
Area *Parea = Plist->Phead->Pnext;
#ifdef RECT_DEBUG
if ((x1 > x2) || (y1 > y2))
area_fatal_error("Area_List_insert", "invalid coords: %i %i %i %i", x1, y1, x2, y2);
#endif
while (Parea != Plist->Ptail)
{
// totally covers
if ((x1 <= Parea->x1) && (x2 >= Parea->x2) &&
(y1 <= Parea->y1) && (y2 >= Parea->y2))
{
Parea = delete_area(Plist, Parea);
continue;
}
// intersects
if ((x2 >= Parea->x1) && (x1 <= Parea->x2) &&
(y2 >= Parea->y1) && (y1 <= Parea->y2))
{
int ax1, ay1, ax2, ay2;
ax1 = Parea->x1;
ay1 = Parea->y1;
ax2 = Parea->x2;
ay2 = Parea->y2;
if (x1 < ax1)
Area_List_insert(Plist, x1, math::maximum(y1, ay1), ax1 - 1, math::minimum(y2, ay2), combine);
if (x2 > ax2)
Area_List_insert(Plist, ax2 + 1, math::maximum(y1, ay1), x2, math::minimum(y2, ay2), combine);
if (y1 < ay1)
Area_List_insert(Plist, x1, y1, x2, ay1 - 1, combine);
if (y2 > ay2)
Area_List_insert(Plist, x1, ay2 + 1, x2, y2, combine);
return;
}
if (combine)
{
if ((x1 == Parea->x1) && (x2 == Parea->x2))
{
if ((y2 == Parea->y1 - 1) || (y1 == Parea->y2 + 1))
{
delete_area(Plist, Parea);
Area_List_insert(Plist, x1, math::minimum(y1, Parea->y1), x2, math::maximum(y2, Parea->y2), CRNLIB_TRUE);
return;
}
}
else if ((y1 == Parea->y1) && (y2 == Parea->y2))
{
if ((x2 == Parea->x1 - 1) || (x1 == Parea->x2 + 1))
{
delete_area(Plist, Parea);
Area_List_insert(Plist, math::minimum(x1, Parea->x1), y1, math::maximum(x2, Parea->x2), y2, CRNLIB_TRUE);
return;
}
}
}
Parea = Parea->Pnext;
}
insert_area_before(Plist, Parea, x1, y1, x2, y2);
}
void Area_List_intersect_area(Area_List *Plist,
int x1, int y1, int x2, int y2)
{
Area *Parea = Plist->Phead->Pnext;
while (Parea != Plist->Ptail)
{
// doesn't cover
if ((x2 < Parea->x1) || (x1 > Parea->x2) ||
(y2 < Parea->y1) || (y1 > Parea->y2))
{
Parea = delete_area(Plist, Parea);
continue;
}
// totally covers
if ((x1 <= Parea->x1) && (x2 >= Parea->x2) &&
(y1 <= Parea->y1) && (y2 >= Parea->y2))
{
Parea = Parea->Pnext;
continue;
}
// Oct 21- should insert after, because deleted area will access the NEXT area!
// insert_area_after(Plist, Parea,
// math::maximum(x1, Parea->x1),
// math::maximum(y1, Parea->y1),
// math::minimum(x2, Parea->x2),
// math::minimum(y2, Parea->y2));
insert_area_before(Plist, Parea,
math::maximum(x1, Parea->x1),
math::maximum(y1, Parea->y1),
math::minimum(x2, Parea->x2),
math::minimum(y2, Parea->y2));
Parea = delete_area(Plist, Parea);
}
}
#if 0
void Area_List_intersect_Area_List(
Area_List *Pouter_list,
Area_List *Pinner_list,
Area_List *Pdst_list)
{
Area *Parea1 = Pouter_list->Phead->Pnext;
while (Parea1 != Pouter_list->Ptail)
{
Area *Parea2 = Pinner_list->Phead->Pnext;
int x1, y1, x2, y2;
x1 = Parea1->x1; x2 = Parea1->x2;
y1 = Parea1->y1; y2 = Parea1->y2;
while (Parea2 != Pinner_list->Ptail)
{
if ((x1 <= Parea2->x2) && (x2 >= Parea2->x1) &&
(y1 <= Parea2->y2) && (y2 >= Parea2->y1))
{
insert_area_after(Pdst_list, Pdst_list->Phead,
math::maximum(x1, Parea2->x1),
math::maximum(y1, Parea2->y1),
math::minimum(x2, Parea2->x2),
math::minimum(y2, Parea2->y2));
}
Parea2 = Parea2->Pnext;
}
Parea1 = Parea1->Pnext;
}
}
#endif
#if 1
void Area_List_intersect_Area_List(Area_List *Pouter_list,
Area_List *Pinner_list,
Area_List *Pdst_list)
{
Area *Parea1 = Pouter_list->Phead->Pnext;
while (Parea1 != Pouter_list->Ptail)
{
Area *Parea2 = Pinner_list->Phead->Pnext;
int x1, y1, x2, y2;
x1 = Parea1->x1; x2 = Parea1->x2;
y1 = Parea1->y1; y2 = Parea1->y2;
while (Parea2 != Pinner_list->Ptail)
{
if ((x1 <= Parea2->x2) && (x2 >= Parea2->x1) &&
(y1 <= Parea2->y2) && (y2 >= Parea2->y1))
{
int nx1, ny1, nx2, ny2;
nx1 = math::maximum(x1, Parea2->x1);
ny1 = math::maximum(y1, Parea2->y1);
nx2 = math::minimum(x2, Parea2->x2);
ny2 = math::minimum(y2, Parea2->y2);
if (Pdst_list->Phead->Pnext == Pdst_list->Ptail)
{
insert_area_after(Pdst_list, Pdst_list->Phead,
nx1, ny1, nx2, ny2);
}
else
{
Area_Ptr Ptemp = Pdst_list->Phead->Pnext;
if ((Ptemp->x1 == nx1) && (Ptemp->x2 == nx2))
{
if (Ptemp->y1 == (ny2+1))
{
Ptemp->y1 = ny1;
goto next;
}
else if (Ptemp->y2 == (ny1-1))
{
Ptemp->y2 = ny2;
goto next;
}
}
else if ((Ptemp->y1 == ny1) && (Ptemp->y2 == ny2))
{
if (Ptemp->x1 == (nx2+1))
{
Ptemp->x1 = nx1;
goto next;
}
else if (Ptemp->x2 == (nx1-1))
{
Ptemp->x2 = nx2;
goto next;
}
}
insert_area_after(Pdst_list, Pdst_list->Phead,
nx1, ny1, nx2, ny2);
}
}
next:
Parea2 = Parea2->Pnext;
}
Parea1 = Parea1->Pnext;
}
}
#endif
Area_List_Ptr Area_List_create_optimal(Area_List_Ptr Plist)
{
Area_Ptr Parea = Plist->Phead->Pnext, Parea_after;
int num = 2;
Area_List_Ptr Pnew_list;
while (Parea != Plist->Ptail)
{
num++;
Parea = Parea->Pnext;
}
Pnew_list = Area_List_init(num);
Parea = Plist->Phead->Pnext;
Parea_after = Pnew_list->Phead;
while (Parea != Plist->Ptail)
{
Parea_after = insert_area_after(Pnew_list, Parea_after,
Parea->x1, Parea->y1,
Parea->x2, Parea->y2);
Parea = Parea->Pnext;
}
return (Pnew_list);
}
} // namespace crnlib