374 lines
10 KiB
Plaintext
374 lines
10 KiB
Plaintext
// Copyright (C) 2010, Guy Barrand. All rights reserved.
|
|
// See the file tools.license for terms.
|
|
|
|
#ifndef tools_zb_polygon
|
|
#define tools_zb_polygon
|
|
|
|
#include "edge_table"
|
|
#include "../mnmx"
|
|
|
|
namespace tools {
|
|
namespace zb {
|
|
|
|
class polygon {
|
|
|
|
static const int NUMPTSTOBUFFER = 200;
|
|
|
|
typedef struct _POINTBLOCK {
|
|
point pts[NUMPTSTOBUFFER];
|
|
struct _POINTBLOCK* next;
|
|
} POINTBLOCK;
|
|
|
|
int m_pETEn;
|
|
EdgeTableEntry* m_pETEs;
|
|
int m_numAllocPtBlocks;
|
|
POINTBLOCK m_FirstPtBlock;
|
|
public:
|
|
polygon():m_pETEn(0),m_pETEs(NULL),m_numAllocPtBlocks(0){}
|
|
virtual ~polygon(){clear();}
|
|
protected:
|
|
polygon(const polygon&){}
|
|
polygon& operator=(const polygon&){return *this;}
|
|
public:
|
|
void clear(){
|
|
POINTBLOCK* curPtBlock;
|
|
cmem_free(m_pETEs);
|
|
m_pETEn = 0;
|
|
for(curPtBlock = m_FirstPtBlock.next; --m_numAllocPtBlocks >= 0;){
|
|
POINTBLOCK* tmpPtBlock;
|
|
tmpPtBlock = curPtBlock->next;
|
|
cmem_free(curPtBlock);
|
|
curPtBlock = tmpPtBlock;
|
|
}
|
|
m_numAllocPtBlocks = 0;
|
|
}
|
|
|
|
typedef void (*scan_func)(void*,int,int,int);
|
|
|
|
void scan(int Count, /* number of pts */
|
|
const point* Pts, /* the pts */
|
|
int rule, /* winding rule */
|
|
scan_func a_proc,void* a_tag,
|
|
unsigned int a_width,unsigned int a_height){
|
|
// polytoregion
|
|
// Scan converts a polygon by returning a run-length
|
|
// encoding of the resultant bitmap -- the run-length
|
|
// encoding is in the form of an array of rectangles.
|
|
|
|
EdgeTableEntry* pAET; /* Active Edge Table */
|
|
int y; /* current scanline */
|
|
int iPts = 0; /* number of pts in buffer */
|
|
EdgeTableEntry* pWETE; /* Winding Edge Table Entry*/
|
|
ScanLineList* pSLL; /* current scanLineList */
|
|
|
|
EdgeTableEntry* pPrevAET; /* ptr to previous AET */
|
|
EdgeTable ET; /* header node for ET */
|
|
EdgeTableEntry AET; /* header node for AET */
|
|
ScanLineListBlock SLLBlock; /* header for scanlinelist */
|
|
int fixWAET = 0;
|
|
POINTBLOCK* curPtBlock;
|
|
int numFullPtBlocks = 0;
|
|
|
|
if(a_proc==NULL) return;
|
|
if(Count==0) return;
|
|
|
|
if(Count==3) {
|
|
point pts[3];
|
|
pts[0] = Pts[0];
|
|
pts[1] = Pts[1];
|
|
pts[2] = Pts[2];
|
|
|
|
point vp_down[3];
|
|
vp_down[0] = point(0,0,0);
|
|
vp_down[1] = point(a_width,0,0);
|
|
vp_down[2] = point(a_width,a_height,0);
|
|
if(triangles_overlap(pts,vp_down)) {
|
|
} else {
|
|
point vp_up[3];
|
|
vp_up[0] = point(0,0,0);
|
|
vp_up[1] = point(a_width,a_height,0);
|
|
vp_up[2] = point(0,a_height,0);
|
|
if(!triangles_overlap(pts,vp_up)) return;
|
|
}
|
|
}
|
|
|
|
int pts_xmin = Pts[0].x;
|
|
int pts_xmax = pts_xmin;
|
|
int pts_ymin = Pts[0].y;
|
|
int pts_ymax = pts_ymin;
|
|
{for(int count=1;count<Count;count++) {
|
|
if(Pts[count].x<pts_xmin) pts_xmin = Pts[count].x;
|
|
if(Pts[count].x>pts_xmax) pts_xmax = Pts[count].x;
|
|
if(Pts[count].y<pts_ymin) pts_ymin = Pts[count].y;
|
|
if(Pts[count].y>pts_ymax) pts_ymax = Pts[count].y;
|
|
}}
|
|
|
|
/* special case a rectangle */
|
|
point* pts = (point*)Pts;
|
|
if (((Count == 4) ||
|
|
((Count == 5) && (pts[4].x == pts[0].x) && (pts[4].y == pts[0].y))) &&
|
|
(((pts[0].y == pts[1].y) &&
|
|
(pts[1].x == pts[2].x) &&
|
|
(pts[2].y == pts[3].y) &&
|
|
(pts[3].x == pts[0].x)) ||
|
|
((pts[0].x == pts[1].x) &&
|
|
(pts[1].y == pts[2].y) &&
|
|
(pts[2].x == pts[3].x) &&
|
|
(pts[3].y == pts[0].y))))
|
|
{
|
|
int xmin,xmax,ymin,ymax;
|
|
xmin = (int)min_of(pts[0].x, pts[2].x);
|
|
ymin = (int)min_of(pts[0].y, pts[2].y);
|
|
xmax = (int)max_of(pts[0].x, pts[2].x);
|
|
ymax = (int)max_of(pts[0].y, pts[2].y);
|
|
if ((xmin != xmax) && (ymin != ymax))
|
|
{
|
|
for(y=ymin;y<=ymax;y++) a_proc(a_tag,xmin ,xmax ,y);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(Count>m_pETEn)
|
|
{
|
|
cmem_free(m_pETEs);
|
|
m_pETEn = Count;
|
|
m_pETEs = cmem_alloc<EdgeTableEntry>(m_pETEn);
|
|
if(m_pETEs==NULL)
|
|
{
|
|
m_pETEn = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
ET.scanlines.next = (ScanLineList*)NULL;
|
|
ET.ymax = pts_ymin;
|
|
ET.ymin = pts_ymax;
|
|
|
|
AET.next = (EdgeTableEntry*)NULL;
|
|
AET.back = (EdgeTableEntry*)NULL;
|
|
AET.nextWETE = (EdgeTableEntry*)NULL;
|
|
AET.bres.minor_axis = pts_xmin;
|
|
|
|
SLLBlock.next = (ScanLineListBlock*)NULL;
|
|
|
|
CreateETandAET (Count,(point*)Pts, &ET, &AET, m_pETEs, &SLLBlock,pts_xmin,pts_ymin,pts_ymax);
|
|
|
|
pSLL = ET.scanlines.next;
|
|
|
|
curPtBlock = &m_FirstPtBlock;
|
|
pts = m_FirstPtBlock.pts;
|
|
|
|
|
|
if (rule==0)
|
|
{
|
|
/*
|
|
* for each scanline
|
|
*/
|
|
for (y = ET.ymin; y < ET.ymax; y++) {
|
|
/*
|
|
* Add a new edge to the active edge table when we
|
|
* get to the next edge.
|
|
*/
|
|
if (pSLL != NULL && y == pSLL->scanline)
|
|
{
|
|
LoadAET(&AET, pSLL->edgelist);
|
|
pSLL = pSLL->next;
|
|
}
|
|
pPrevAET = &AET;
|
|
pAET = AET.next;
|
|
|
|
/*
|
|
* for each active edge
|
|
*/
|
|
while (pAET) {
|
|
pts->x = pAET->bres.minor_axis;
|
|
pts->y = y;
|
|
pts++;
|
|
iPts++;
|
|
|
|
/*
|
|
* send out the buffer
|
|
*/
|
|
if (iPts == NUMPTSTOBUFFER)
|
|
{
|
|
if(numFullPtBlocks < m_numAllocPtBlocks)
|
|
{
|
|
curPtBlock = curPtBlock->next;
|
|
}
|
|
else
|
|
{
|
|
POINTBLOCK* tmpPtBlock = cmem_alloc<POINTBLOCK>(1);
|
|
if(tmpPtBlock==NULL)
|
|
{
|
|
FreeStorage(SLLBlock.next);
|
|
return;
|
|
}
|
|
tmpPtBlock->next = NULL; /*Barrand*/
|
|
curPtBlock->next = tmpPtBlock;
|
|
curPtBlock = tmpPtBlock;
|
|
m_numAllocPtBlocks++;
|
|
}
|
|
numFullPtBlocks++;
|
|
pts = curPtBlock->pts;
|
|
iPts = 0;
|
|
}
|
|
|
|
EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
|
|
}
|
|
(void) InsertAndSort(&AET);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* for each scanline
|
|
*/
|
|
for (y = ET.ymin; y < ET.ymax; y++) {
|
|
/*
|
|
* Add a new edge to the active edge table when we
|
|
* get to the next edge.
|
|
*/
|
|
if (pSLL != NULL && y == pSLL->scanline)
|
|
{
|
|
LoadAET(&AET, pSLL->edgelist);
|
|
ComputeWAET(&AET);
|
|
pSLL = pSLL->next;
|
|
}
|
|
pPrevAET = &AET;
|
|
pAET = AET.next;
|
|
pWETE = pAET;
|
|
|
|
/*
|
|
* for each active edge
|
|
*/
|
|
while (pAET) {
|
|
/*
|
|
* add to the buffer only those edges that
|
|
* are in the Winding active edge table.
|
|
*/
|
|
if (pWETE == pAET) {
|
|
pts->x = pAET->bres.minor_axis;
|
|
pts->y = y;
|
|
pts++;
|
|
iPts++;
|
|
|
|
/*
|
|
* send out the buffer
|
|
*/
|
|
if (iPts == NUMPTSTOBUFFER)
|
|
{
|
|
if(numFullPtBlocks < m_numAllocPtBlocks)
|
|
{
|
|
curPtBlock = curPtBlock->next;
|
|
}
|
|
else
|
|
{
|
|
POINTBLOCK* tmpPtBlock = cmem_alloc<POINTBLOCK>(1);
|
|
if(tmpPtBlock==NULL)
|
|
{
|
|
FreeStorage(SLLBlock.next);
|
|
return;
|
|
}
|
|
tmpPtBlock->next = NULL; /*Barrand*/
|
|
curPtBlock->next = tmpPtBlock;
|
|
curPtBlock = tmpPtBlock;
|
|
m_numAllocPtBlocks++;
|
|
}
|
|
numFullPtBlocks++;
|
|
pts = curPtBlock->pts;
|
|
iPts = 0;
|
|
}
|
|
pWETE = pWETE->nextWETE;
|
|
}
|
|
EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
|
|
}
|
|
|
|
/*
|
|
* recompute the winding active edge table if
|
|
* we just resorted or have exited an edge.
|
|
*/
|
|
if ( (InsertAndSort(&AET)!=0) || (fixWAET!=0) )
|
|
{
|
|
ComputeWAET(&AET);
|
|
fixWAET = 0;
|
|
}
|
|
}
|
|
}
|
|
FreeStorage (SLLBlock.next);
|
|
|
|
ScanPoints (numFullPtBlocks, iPts, &m_FirstPtBlock,a_proc,a_tag);
|
|
|
|
}
|
|
protected:
|
|
void ScanPoints (int numFullPtBlocks,
|
|
int iCurPtBlock,
|
|
POINTBLOCK* FirstPtBlock,
|
|
scan_func a_proc,void* a_tag) {
|
|
point* pts;
|
|
POINTBLOCK* CurPtBlock;
|
|
int i;
|
|
CurPtBlock = FirstPtBlock;
|
|
for ( ; numFullPtBlocks >= 0; numFullPtBlocks--)
|
|
{
|
|
/* the loop uses 2 points per iteration */
|
|
i = numFullPtBlocks!=0 ? NUMPTSTOBUFFER >> 1 : iCurPtBlock >> 1 ;
|
|
for (pts = CurPtBlock->pts; i--; pts += 2)
|
|
{
|
|
a_proc (a_tag,(int)(pts->x),(int)pts[1].x,(int)pts->y);
|
|
}
|
|
CurPtBlock = CurPtBlock->next;
|
|
}
|
|
}
|
|
|
|
// from: https://rosettacode.org/wiki/Determine_if_two_triangles_overlap#C++
|
|
static double det_2D(const point& a_p1,const point& a_p2,const point& a_p3) {
|
|
return a_p1.x*(a_p2.y-a_p3.y)+a_p2.x*(a_p3.y-a_p1.y)+a_p3.x*(a_p1.y-a_p2.y);
|
|
}
|
|
|
|
static void check_winding(point& a_p1,point& a_p2,point& a_p3) {
|
|
double detTri = det_2D(a_p1, a_p2, a_p3);
|
|
if(detTri < 0.0) { //swap p2 and p3:
|
|
point a = a_p3;
|
|
a_p3 = a_p2;
|
|
a_p2 = a;
|
|
}
|
|
}
|
|
|
|
static bool check_boundary_overlap(point& a_p1,point& a_p2,point& a_p3) {return det_2D(a_p1, a_p2, a_p3) < 0.0;}
|
|
|
|
static bool triangles_overlap(point* a_t1,point* a_t2) {
|
|
//Trangles must be expressed anti-clockwise
|
|
check_winding(a_t1[0], a_t1[1], a_t1[2]);
|
|
check_winding(a_t2[0], a_t2[1], a_t2[2]);
|
|
|
|
//For edge E of trangle 1,
|
|
for(int i=0; i<3; i++) {
|
|
int j=(i+1)%3;
|
|
|
|
//Check all points of trangle 2 lay on the external side of the edge E. If
|
|
//they do, the triangles do not overlap.
|
|
if (check_boundary_overlap(a_t1[i], a_t1[j], a_t2[0]) &&
|
|
check_boundary_overlap(a_t1[i], a_t1[j], a_t2[1]) &&
|
|
check_boundary_overlap(a_t1[i], a_t1[j], a_t2[2])) return false;
|
|
}
|
|
|
|
//For edge E of trangle 2,
|
|
for(int i=0; i<3; i++) {
|
|
int j=(i+1)%3;
|
|
|
|
//Check all points of trangle 1 lay on the external side of the edge E. If
|
|
//they do, the triangles do not overlap.
|
|
if (check_boundary_overlap(a_t2[i], a_t2[j], a_t1[0]) &&
|
|
check_boundary_overlap(a_t2[i], a_t2[j], a_t1[1]) &&
|
|
check_boundary_overlap(a_t2[i], a_t2[j], a_t1[2])) return false;
|
|
}
|
|
|
|
return true; //the triangles overlap.
|
|
}
|
|
|
|
};
|
|
|
|
}}
|
|
|
|
#endif
|