| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Delaunay.cpp |
| // $Id: Delaunay.cpp,v 1.10 2011/06/17 13:35:48 mbansal Exp $ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <memory.h> |
| #include "Delaunay.h" |
| |
| #define QQ 9 // Optimal value as determined by testing |
| #define DM 38 // 2^(1+DM/2) element sort capability. DM=38 for >10^6 elements |
| #define NYL -1 |
| #define valid(l) ccw(orig(basel), dest(l), dest(basel)) |
| |
| |
| CDelaunay::CDelaunay() |
| { |
| } |
| |
| CDelaunay::~CDelaunay() |
| { |
| } |
| |
| // Allocate storage, construct triangulation, compute voronoi corners |
| int CDelaunay::triangulate(SEdgeVector **edges, int n_sites, int width, int height) |
| { |
| EdgePointer cep; |
| |
| deleteAllEdges(); |
| buildTriangulation(n_sites); |
| cep = consolidateEdges(); |
| *edges = ev; |
| |
| // Note: construction_list will change ev |
| return constructList(cep, width, height); |
| } |
| |
| // builds delaunay triangulation |
| void CDelaunay::buildTriangulation(int size) |
| { |
| int i, rows; |
| EdgePointer lefte, righte; |
| |
| rows = (int)( 0.5 + sqrt( (double) size / log( (double) size ))); |
| |
| // Sort the pointers by x-coordinate of site |
| for ( i=0 ; i < size ; i++ ) { |
| sp[i] = (SitePointer) i; |
| } |
| |
| spsortx( sp, 0, size-1 ); |
| build( 0, size-1, &lefte, &righte, rows ); |
| oneBndryEdge = lefte; |
| } |
| |
| // Recursive Delaunay Triangulation Procedure |
| // Contains modifications for axis-switching division. |
| void CDelaunay::build(int lo, int hi, EdgePointer *le, EdgePointer *re, int rows) |
| { |
| EdgePointer a, b, c, ldo, rdi, ldi, rdo, maxx, minx; |
| int split, lowrows; |
| int low, high; |
| SitePointer s1, s2, s3; |
| low = lo; |
| high = hi; |
| |
| if ( low < (high-2) ) { |
| // more than three elements; do recursion |
| minx = sp[low]; |
| maxx = sp[high]; |
| if (rows == 1) { // time to switch axis of division |
| spsorty( sp, low, high); |
| rows = 65536; |
| } |
| lowrows = rows/2; |
| split = low - 1 + (int) |
| (0.5 + ((double)(high-low+1) * ((double)lowrows / (double)rows))); |
| build( low, split, &ldo, &ldi, lowrows ); |
| build( split+1, high, &rdi, &rdo, (rows-lowrows) ); |
| doMerge(&ldo, ldi, rdi, &rdo); |
| while (orig(ldo) != minx) { |
| ldo = rprev(ldo); |
| } |
| while (orig(rdo) != maxx) { |
| rdo = (SitePointer) lprev(rdo); |
| } |
| *le = ldo; |
| *re = rdo; |
| } |
| else if (low >= (high - 1)) { // two or one points |
| a = makeEdge(sp[low], sp[high]); |
| *le = a; |
| *re = (EdgePointer) sym(a); |
| } else { // three points |
| // 3 cases: triangles of 2 orientations, and 3 points on a line |
| a = makeEdge((s1 = sp[low]), (s2 = sp[low+1])); |
| b = makeEdge(s2, (s3 = sp[high])); |
| splice((EdgePointer) sym(a), b); |
| if (ccw(s1, s3, s2)) { |
| c = connectLeft(b, a); |
| *le = (EdgePointer) sym(c); |
| *re = c; |
| } else { |
| *le = a; |
| *re = (EdgePointer) sym(b); |
| if (ccw(s1, s2, s3)) { |
| // not colinear |
| c = connectLeft(b, a); |
| } |
| } |
| } |
| } |
| |
| // Quad-edge manipulation primitives |
| EdgePointer CDelaunay::makeEdge(SitePointer origin, SitePointer destination) |
| { |
| EdgePointer temp, ans; |
| temp = allocEdge(); |
| ans = temp; |
| |
| onext(temp) = ans; |
| orig(temp) = origin; |
| onext(++temp) = (EdgePointer) (ans + 3); |
| onext(++temp) = (EdgePointer) (ans + 2); |
| orig(temp) = destination; |
| onext(++temp) = (EdgePointer) (ans + 1); |
| |
| return(ans); |
| } |
| |
| void CDelaunay::splice(EdgePointer a, EdgePointer b) |
| { |
| EdgePointer alpha, beta, temp; |
| alpha = (EdgePointer) rot(onext(a)); |
| beta = (EdgePointer) rot(onext(b)); |
| temp = onext(alpha); |
| onext(alpha) = onext(beta); |
| onext(beta) = temp; |
| temp = onext(a); |
| onext(a) = onext(b); |
| onext(b) = temp; |
| } |
| |
| EdgePointer CDelaunay::connectLeft(EdgePointer a, EdgePointer b) |
| { |
| EdgePointer ans; |
| ans = makeEdge(dest(a), orig(b)); |
| splice(ans, (EdgePointer) lnext(a)); |
| splice((EdgePointer) sym(ans), b); |
| return(ans); |
| } |
| |
| EdgePointer CDelaunay::connectRight(EdgePointer a, EdgePointer b) |
| { |
| EdgePointer ans; |
| ans = makeEdge(dest(a), orig(b)); |
| splice(ans, (EdgePointer) sym(a)); |
| splice((EdgePointer) sym(ans), (EdgePointer) oprev(b)); |
| return(ans); |
| } |
| |
| // disconnects e from the rest of the structure and destroys it |
| void CDelaunay::deleteEdge(EdgePointer e) |
| { |
| splice(e, (EdgePointer) oprev(e)); |
| splice((EdgePointer) sym(e), (EdgePointer) oprev(sym(e))); |
| freeEdge(e); |
| } |
| |
| // |
| // Overall storage allocation |
| // |
| |
| // Quad-edge storage allocation |
| CSite *CDelaunay::allocMemory(int n) |
| { |
| unsigned int size; |
| |
| size = ((sizeof(CSite) + sizeof(SitePointer)) * n + |
| (sizeof(SitePointer) + sizeof(EdgePointer)) * 12 |
| ) * n; |
| if (!(sa = (CSite*) malloc(size))) { |
| return NULL; |
| } |
| sp = (SitePointer *) (sa + n); |
| ev = (SEdgeVector *) (org = sp + n); |
| next = (EdgePointer *) (org + 12 * n); |
| ei = (struct EDGE_INFO *) (next + 12 * n); |
| return sa; |
| } |
| |
| void CDelaunay::freeMemory() |
| { |
| if (sa) { |
| free(sa); |
| sa = (CSite*)NULL; |
| } |
| } |
| |
| // |
| // Edge storage management |
| // |
| |
| void CDelaunay::deleteAllEdges() |
| { |
| nextEdge = 0; |
| availEdge = NYL; |
| } |
| |
| EdgePointer CDelaunay::allocEdge() |
| { |
| EdgePointer ans; |
| |
| if (availEdge == NYL) { |
| ans = nextEdge, nextEdge += 4; |
| } else { |
| ans = availEdge, availEdge = onext(availEdge); |
| } |
| return(ans); |
| } |
| |
| void CDelaunay::freeEdge(EdgePointer e) |
| { |
| e ^= e & 3; |
| onext(e) = availEdge; |
| availEdge = e; |
| } |
| |
| EdgePointer CDelaunay::consolidateEdges() |
| { |
| EdgePointer e; |
| int i,j; |
| |
| while (availEdge != NYL) { |
| nextEdge -= 4; e = availEdge; availEdge = onext(availEdge); |
| |
| if (e==nextEdge) { |
| continue; // the one deleted was the last one anyway |
| } |
| if ((oneBndryEdge&~3) == nextEdge) { |
| oneBndryEdge = (EdgePointer) (e | (oneBndryEdge&3)); |
| } |
| for (i=0,j=3; i<4; i++,j=rot(j)) { |
| onext(e+i) = onext(nextEdge+i); |
| onext(rot(onext(e+i))) = (EdgePointer) (e+j); |
| } |
| } |
| return nextEdge; |
| } |
| |
| // |
| // Sorting Routines |
| // |
| |
| int CDelaunay::xcmpsp(int i, int j) |
| { |
| double d = sa[(i>=0)?sp[i]:sp1].X() - sa[(j>=0)?sp[j]:sp1].X(); |
| if ( d > 0. ) { |
| return 1; |
| } |
| if ( d < 0. ) { |
| return -1; |
| } |
| d = sa[(i>=0)?sp[i]:sp1].Y() - sa[(j>=0)?sp[j]:sp1].Y(); |
| if ( d > 0. ) { |
| return 1; |
| } |
| if ( d < 0. ) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| int CDelaunay::ycmpsp(int i, int j) |
| { |
| double d = sa[(i>=0)?sp[i]:sp1].Y() - sa[(j>=0)?sp[j]:sp1].Y(); |
| if ( d > 0. ) { |
| return 1; |
| } |
| if ( d < 0. ) { |
| return -1; |
| } |
| d = sa[(i>=0)?sp[i]:sp1].X() - sa[(j>=0)?sp[j]:sp1].X(); |
| if ( d > 0. ) { |
| return 1; |
| } |
| if ( d < 0. ) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| int CDelaunay::cmpev(int i, int j) |
| { |
| return (ev[i].first - ev[j].first); |
| } |
| |
| void CDelaunay::swapsp(int i, int j) |
| { |
| int t; |
| t = (i>=0) ? sp[i] : sp1; |
| |
| if (i>=0) { |
| sp[i] = (j>=0)?sp[j]:sp1; |
| } else { |
| sp1 = (j>=0)?sp[j]:sp1; |
| } |
| |
| if (j>=0) { |
| sp[j] = (SitePointer) t; |
| } else { |
| sp1 = (SitePointer) t; |
| } |
| } |
| |
| void CDelaunay::swapev(int i, int j) |
| { |
| SEdgeVector temp; |
| |
| temp = ev[i]; |
| ev[i] = ev[j]; |
| ev[j] = temp; |
| } |
| |
| void CDelaunay::copysp(int i, int j) |
| { |
| if (j>=0) { |
| sp[j] = (i>=0)?sp[i]:sp1; |
| } else { |
| sp1 = (i>=0)?sp[i]:sp1; |
| } |
| } |
| |
| void CDelaunay::copyev(int i, int j) |
| { |
| ev[j] = ev[i]; |
| } |
| |
| void CDelaunay::spsortx(SitePointer *sp_in, int low, int high) |
| { |
| sp = sp_in; |
| rcssort(low,high,-1,&CDelaunay::xcmpsp,&CDelaunay::swapsp,&CDelaunay::copysp); |
| } |
| |
| void CDelaunay::spsorty(SitePointer *sp_in, int low, int high ) |
| { |
| sp = sp_in; |
| rcssort(low,high,-1,&CDelaunay::ycmpsp,&CDelaunay::swapsp,&CDelaunay::copysp); |
| } |
| |
| void CDelaunay::rcssort(int lowelt, int highelt, int temp, |
| int (CDelaunay::*comparison)(int,int), |
| void (CDelaunay::*swap)(int,int), |
| void (CDelaunay::*copy)(int,int)) |
| { |
| int m,sij,si,sj,sL,sk; |
| int stack[DM]; |
| |
| if (highelt-lowelt<=1) { |
| return; |
| } |
| if (highelt-lowelt>QQ) { |
| m = 0; |
| si = lowelt; sj = highelt; |
| for (;;) { // partition [si,sj] about median-of-3. |
| sij = (sj+si) >> 1; |
| |
| // Now to sort elements si,sij,sj into order & set temp=their median |
| if ( (this->*comparison)( si,sij ) > 0 ) { |
| (this->*swap)( si,sij ); |
| } |
| if ( (this->*comparison)( sij,sj ) > 0 ) { |
| (this->*swap)( sj,sij ); |
| if ( (this->*comparison)( si,sij ) > 0 ) { |
| (this->*swap)( si,sij ); |
| } |
| } |
| (this->*copy)( sij,temp ); |
| |
| // Now to partition into elements <=temp, >=temp, and ==temp. |
| sk = si; sL = sj; |
| do { |
| do { |
| sL--; |
| } while( (this->*comparison)( sL,temp ) > 0 ); |
| do { |
| sk++; |
| } while( (this->*comparison)( temp,sk ) > 0 ); |
| if ( sk < sL ) { |
| (this->*swap)( sL,sk ); |
| } |
| } while(sk <= sL); |
| |
| // Now to recurse on shorter partition, store longer partition on stack |
| if ( sL-si > sj-sk ) { |
| if ( sL-si < QQ ) { |
| if( m==0 ) { |
| break; // empty stack && both partitions < QQ so break |
| } else { |
| sj = stack[--m]; |
| si = stack[--m]; |
| } |
| } |
| else { |
| if ( sj-sk < QQ ) { |
| sj = sL; |
| } else { |
| stack[m++] = si; |
| stack[m++] = sL; |
| si = sk; |
| } |
| } |
| } |
| else { |
| if ( sj-sk < QQ ) { |
| if ( m==0 ) { |
| break; // empty stack && both partitions < QQ so break |
| } else { |
| sj = stack[--m]; |
| si = stack[--m]; |
| } |
| } |
| else { |
| if ( sL-si < QQ ) { |
| si = sk; |
| } else { |
| stack[m++] = sk; |
| stack[m++] = sj; |
| sj = sL; |
| } |
| } |
| } |
| } |
| } |
| |
| // Now for 0 or Data bounded "straight insertion" sort of [0,nels-1]; if it is |
| // known that el[-1] = -INF, then can omit the "sk>=0" test and save time. |
| for (si=lowelt; si<highelt; si++) { |
| if ( (this->*comparison)( si,si+1 ) > 0 ) { |
| (this->*copy)( si+1,temp ); |
| sj = sk = si; |
| sj++; |
| do { |
| (this->*copy)( sk,sj ); |
| sj = sk; |
| sk--; |
| } while ( (this->*comparison)( sk,temp ) > 0 && sk>=lowelt ); |
| (this->*copy)( temp,sj ); |
| } |
| } |
| } |
| |
| // |
| // Geometric primitives |
| // |
| |
| // incircle, as in the Guibas-Stolfi paper. |
| int CDelaunay::incircle(SitePointer a, SitePointer b, SitePointer c, SitePointer d) |
| { |
| double adx, ady, bdx, bdy, cdx, cdy, dx, dy, nad, nbd, ncd; |
| dx = sa[d].X(); |
| dy = sa[d].Y(); |
| adx = sa[a].X() - dx; |
| ady = sa[a].Y() - dy; |
| bdx = sa[b].X() - dx; |
| bdy = sa[b].Y() - dy; |
| cdx = sa[c].X() - dx; |
| cdy = sa[c].Y() - dy; |
| nad = adx*adx+ady*ady; |
| nbd = bdx*bdx+bdy*bdy; |
| ncd = cdx*cdx+cdy*cdy; |
| return( (0.0 < (nad * (bdx * cdy - bdy * cdx) |
| + nbd * (cdx * ady - cdy * adx) |
| + ncd * (adx * bdy - ady * bdx))) ? TRUE : FALSE ); |
| } |
| |
| // TRUE iff A, B, C form a counterclockwise oriented triangle |
| int CDelaunay::ccw(SitePointer a, SitePointer b, SitePointer c) |
| { |
| int result; |
| |
| double ax = sa[a].X(); |
| double bx = sa[b].X(); |
| double cx = sa[c].X(); |
| double ay = sa[a].Y(); |
| double by = sa[b].Y(); |
| double cy = sa[c].Y(); |
| |
| double val = (ax - cx)*(by - cy) - (bx - cx)*(ay - cy); |
| if ( val > 0.0) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // |
| // The Merge Procedure. |
| // |
| |
| void CDelaunay::doMerge(EdgePointer *ldo, EdgePointer ldi, EdgePointer rdi, EdgePointer *rdo) |
| { |
| int rvalid, lvalid; |
| EdgePointer basel,lcand,rcand,t; |
| |
| for (;;) { |
| while (ccw(orig(ldi), dest(ldi), orig(rdi))) { |
| ldi = (EdgePointer) lnext(ldi); |
| } |
| if (ccw(dest(rdi), orig(rdi), orig(ldi))) { |
| rdi = (EdgePointer)rprev(rdi); |
| } else { |
| break; |
| } |
| } |
| |
| basel = connectLeft((EdgePointer) sym(rdi), ldi); |
| lcand = rprev(basel); |
| rcand = (EdgePointer) oprev(basel); |
| if (orig(basel) == orig(*rdo)) { |
| *rdo = basel; |
| } |
| if (dest(basel) == orig(*ldo)) { |
| *ldo = (EdgePointer) sym(basel); |
| } |
| |
| for (;;) { |
| #if 1 |
| if (valid(t=onext(lcand))) { |
| #else |
| t = (EdgePointer)onext(lcand); |
| if (valid(basel, t)) { |
| #endif |
| while (incircle(dest(lcand), dest(t), orig(lcand), orig(basel))) { |
| deleteEdge(lcand); |
| lcand = t; |
| t = onext(lcand); |
| } |
| } |
| #if 1 |
| if (valid(t=(EdgePointer)oprev(rcand))) { |
| #else |
| t = (EdgePointer)oprev(rcand); |
| if (valid(basel, t)) { |
| #endif |
| while (incircle(dest(t), dest(rcand), orig(rcand), dest(basel))) { |
| deleteEdge(rcand); |
| rcand = t; |
| t = (EdgePointer)oprev(rcand); |
| } |
| } |
| |
| #if 1 |
| lvalid = valid(lcand); |
| rvalid = valid(rcand); |
| #else |
| lvalid = valid(basel, lcand); |
| rvalid = valid(basel, rcand); |
| #endif |
| if ((! lvalid) && (! rvalid)) { |
| return; |
| } |
| |
| if (!lvalid || |
| (rvalid && incircle(dest(lcand), orig(lcand), orig(rcand), dest(rcand)))) { |
| basel = connectLeft(rcand, (EdgePointer) sym(basel)); |
| rcand = (EdgePointer) lnext(sym(basel)); |
| } else { |
| basel = (EdgePointer) sym(connectRight(lcand, basel)); |
| lcand = rprev(basel); |
| } |
| } |
| } |
| |
| int CDelaunay::constructList(EdgePointer last, int width, int height) |
| { |
| int c, i; |
| EdgePointer curr, src, nex; |
| SEdgeVector *currv, *prevv; |
| |
| c = (int) ((curr = (EdgePointer) ((last & ~3))) >> 1); |
| |
| for (last -= 4; last >= 0; last -= 4) { |
| src = orig(last); |
| nex = dest(last); |
| orig(--curr) = src; |
| orig(--curr) = nex; |
| orig(--curr) = nex; |
| orig(--curr) = src; |
| } |
| rcssort(0, c - 1, -1, &CDelaunay::cmpev, &CDelaunay::swapev, &CDelaunay::copyev); |
| |
| // Throw out any edges that are too far apart |
| currv = prevv = ev; |
| for (i = c; i--; currv++) { |
| if ((int) fabs(sa[currv->first].getVCenter().x - sa[currv->second].getVCenter().x) <= width && |
| (int) fabs(sa[currv->first].getVCenter().y - sa[currv->second].getVCenter().y) <= height) { |
| *(prevv++) = *currv; |
| } else { |
| c--; |
| } |
| } |
| return c; |
| } |
| |
| // Fill in site neighbor information |
| void CDelaunay::linkNeighbors(SEdgeVector *edge, int nedge, int nsite) |
| { |
| int i; |
| |
| for (i = 0; i < nsite; i++) { |
| sa[i].setNeighbor(edge); |
| sa[i].setNumNeighbors(0); |
| for (; edge->first == i && nedge; edge++, nedge--) { |
| sa[i].incrNumNeighbors(); |
| } |
| } |
| } |