// 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;countpts_xmax) pts_xmax = Pts[count].x; if(Pts[count].ypts_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(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(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(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