BOOKS i'm reading
 |
/*
* Module ID: voronoi.cpp
* Titre : Construction d'un diagrame de Voronoi.
* Utilite : Utilise l'algorithme de Steven Fortune des laboratoires
* Bell AT&T comme decrit dans 'Computational Geometry in C ' de
* J. O'Rourke. (P. 179)
*
* Limite : Cet algorithme n'est pas le plus appropriƩ lorsque l'on
* desire ajouter ou supprimer des sites de l'ensemble de sites
* de depart.
* De plus, avec certaines conditions (inconnues) le diagramme
* resultant de cet algorithme est errone.
*
* Auteur : Olivier Langlois (olivier@olivierlanglois.net)
* Date : 07 Mars 1998
*/
#ifdef __BORLANDC__
// Indique au compilateur que les instances de template utilisees dans ce
// module seront definies dans un autre module.
#pragma option -Jgx
#endif
#ifdef _MSC_VER
#define __WIN32__ _WIN32
#endif
#ifdef __GNUG__
#include <std/typeinfo.h>
#endif
#include "collect/idlist.h"
#include "geo/point.h"
#include "geo/segment.h"
#include "geo/circle.h"
#include "../tools/collect/pq.cpp"
#include "collect/colvar.h"
#include "collect/dict.h"
#include "geo/dcel.h"
#include "collect/vector.h"
#include "geo/hedge.h"
#include "geo/voronoi.h"
#ifdef __GNUG__
#include "umath.h"
#else
#include <math.h>
#endif
#include "debug.h"
#ifdef __WIN32__
#include "diagwin.h"
#else
#include "diagdos.h"
#endif
Boolean yStarIsSmallest( circle &star, IDList &sl)
{
IDListIterator it(sl);
point *p;
while( ( p = (point *)++it) &&
star.inside(*p) == BOOL_FALSE)
if( p->ycoord() > star.center().ycoord()+star.radius() ||
(p->ycoord() == star.center().ycoord()+star.radius() &&
p->xcoord() > star.center().xcoord()) ) return BOOL_TRUE;
return BOOL_FALSE;
}
/******************************************************************************
*
* Nom : Voronoi
*
* Utilite : Contruire un diagrame de Voronoi.
*
* Reference : voronoi.h
*
****************************************************************************/
DCEL *Voronoi( IDList &sl )
{
face *newsite, *bot, *top, *bottomsite, *temp;
vertex *v, *p;
point *tmpPoint;
circle newintstar;
halfEdge *lbnd, *rbnd, *llbnd, *rrbnd, *bisector;
edge *e;
line l;
char direction;
float xmax = 0;
// Prepare un 'plane sweep' en triant les sites
sl.sort(sortSites);
IDListIterator it(sl);
while( tmpPoint = (point *)++it )
{
D( cout << *tmpPoint << endl; )
if( tmpPoint->xcoord() < point::xmin ) point::xmin = tmpPoint->xcoord();
if( tmpPoint->xcoord() > xmax ) xmax = tmpPoint->xcoord();
}
if( xmax - point::xmin )
point::deltax = (xmax - point::xmin);
DCEL *edgeList = new DCEL;
halfEdge::init( sl.entries() );
PQ<halfEdge> pQueue;
// Assigne un Sentinel a la PQ.
lbnd = new halfEdge;
lbnd->setYStar( new vertex(point( -HUGE_VAL, -HUGE_VAL )),
point( -HUGE_VAL, -HUGE_VAL ));
pQueue.setSentinel( lbnd );
tmpPoint = (point *)sl.get();
if(tmpPoint)
{
bottomsite = new face(*tmpPoint);
delete tmpPoint;
}
else bottomsite = NULL;
tmpPoint = (point *)sl.get();
if(tmpPoint)
{
newsite = new face(*tmpPoint);
delete tmpPoint;
}
else newsite = NULL;
try{
while(1)
{
if(pQueue.isEmpty() == BOOL_FALSE)
{
newintstar = pQueue.first()->getYStar();
}
if( newsite != NULL
&& (pQueue.isEmpty() == BOOL_TRUE
/*
* Chaque vertex du diagramme est le centre d'un cercle dont
* ses 3 sites sont cocirculaires.
* newintstar sert a determiner si le nouveau site est a
* l'interieur du rayon du cercle des 3 derniers sites traites
*/
|| newintstar.inside(newsite->getLocation()) == BOOL_TRUE
|| yStarIsSmallest( newintstar, sl) == BOOL_FALSE ))
{
/* Le nouveau site est le plus petit */
D( if( newsite ) cout << " newsite :" << newsite->getLocation() << endl; )
lbnd = halfEdge::leftbnd(newsite->getLocation());
/*
if( sl.first() &&
lbnd->ve &&
lbnd->rightSite()->getLocation().distance(*(point *)sl.first()) <
lbnd->rightSite()->getLocation().distance(newsite->getLocation()) &&
lbnd->next() != halfEdge::rightEnd &&
lbnd->next()->atRight(newsite->getLocation()) == BOOL_TRUE )
lbnd = lbnd->next();
*/
rbnd = lbnd->next();
// D( if( lbnd == halfEdge::leftEnd || lbnd == halfEdge::rightEnd ) DiagOutWin("Voronoi").DisplayMsg("Mauvais lbnd"); )
if( !lbnd->ve ) bot = bottomsite;
else bot = lbnd->rightSite();
l = p_bisector(bot->getLocation(), newsite->getLocation());
D( cout << "Bissectrice :" << l << endl; )
D( if( lbnd->ve ) cout << "lbnd :" << lbnd->ve->getSegment() << ((lbnd->direction)?"right":"left") << lbnd->ve->getF1()->getLocation() << lbnd->ve->getF2()->getLocation() << endl; )
D( if( rbnd->ve ) cout << "rbnd :" << rbnd->ve->getSegment() << ((rbnd->direction)?"right":"left") << rbnd->ve->getF1()->getLocation() << rbnd->ve->getF2()->getLocation() << endl; )
e = new edge( edgeList, bot, newsite, l );
bisector = new halfEdge( e, 0 );
bisector->insert( lbnd );
if ((p = lbnd->intersection(bisector)) != NULL)
{
pQueue.remove(lbnd);
lbnd->removeYStar();
lbnd->setYStar(p,newsite->getLocation());
pQueue.insert(lbnd);
}
lbnd = bisector;
bisector = new halfEdge( e, 1 );
bisector->insert(lbnd);
if ((p = bisector->intersection(rbnd)) != NULL)
{
bisector->setYStar(p,newsite->getLocation());
pQueue.insert(bisector);
}
tmpPoint = (point *)sl.get();
if(tmpPoint)
{
newsite = new face(*tmpPoint);
delete tmpPoint;
}
else
{
newsite = NULL;
}
}
else if ( pQueue.isEmpty() == BOOL_FALSE )
/* l'intersection est la plus petite */
{
lbnd = pQueue.removeFirst();
llbnd = lbnd->previous();
rbnd = lbnd->next();
rrbnd = rbnd->next();
bot = lbnd->leftSite();
top = rbnd->rightSite();
v = lbnd->Vertex;
D( cout << "Vertex :" << *(point *)v << endl; )
D( if( lbnd->ve ) cout << "lbnd :" << lbnd->ve->getSegment() << ((lbnd->direction)?"right":"left") << lbnd->ve->getF1()->getLocation() << lbnd->ve->getF2()->getLocation() << endl; )
D( if( rbnd->ve ) cout << "rbnd :" << rbnd->ve->getSegment() << ((rbnd->direction)?"right":"left") << rbnd->ve->getF1()->getLocation() << rbnd->ve->getF2()->getLocation() << endl; )
D( if( llbnd->ve) cout << "llbnd:" << lbnd->ve->getSegment() << ((llbnd->direction)?"right":"left") << llbnd->ve->getF1()->getLocation() << llbnd->ve->getF2()->getLocation() << endl; )
D( if( rrbnd->ve) cout << "rrbnd:" << rbnd->ve->getSegment() << ((rrbnd->direction)?"right":"left") << rrbnd->ve->getF1()->getLocation() << rrbnd->ve->getF2()->getLocation() << endl; )
D( if( pQueue.first() ) cout << "Next intersection: " << pQueue.first()->ve->getSegment() << endl; )
D( if(line(lbnd->ve->getSegment()).contains(*v) == BOOL_FALSE) cout << "Mauvais vertex" << endl; )
if( lbnd->direction == 0 )
// 1
lbnd->setV1(v);
else
lbnd->setV2(v);
D( if(line(rbnd->ve->getSegment()).contains(*v) == BOOL_FALSE) cout << "Mauvais vertex" << endl; )
if( rbnd->direction == 0 )
// 2
rbnd->setV1(v);
else
rbnd->setV2(v);
D( cout << "Site 1:" << bot->getLocation() << " Site 2:" << top->getLocation() << endl; )
if( bot->getLocation().ycoord() > top->getLocation().ycoord() )
{ temp = bot; bot = top; top = temp; direction = 1; }
else direction = 0;
l = p_bisector(bot->getLocation(), top->getLocation());
e = new edge( edgeList, bot, top, l );
bisector = new halfEdge(e, direction);
D( cout << "Bissectrice :" << l << endl; )
/*
* Chaque vertex du diagramme de Voronoi possede exactement 3
* aretes. ( Computational Geometry, [PREPARATA,SHAMOS] P.206 )
*/
// 3
if( direction == 1 )
bisector->setV1(v);
else
bisector->setV2(v);
D( if(l.contains(*v) == BOOL_FALSE) cout << "Mauvais vertex" << endl; )
D( if(lbnd == halfEdge::leftEnd) cout << "lbnd == halfEdge::leftEnd" << endl; )
D( if(rbnd == halfEdge::rightEnd) cout << "rbnd == halfEdge::rightEnd" << endl; )
// Insere le edge dans le DCEL
delete lbnd;
pQueue.remove(rbnd);
delete rbnd;
bisector->insert(llbnd);
if ((p = llbnd->intersection(bisector)) != NULL)
{
pQueue.remove(llbnd);
llbnd->removeYStar();
llbnd->setYStar(p,bot->getLocation());
pQueue.insert(llbnd);
}
if ((p = bisector->intersection(rrbnd)) != NULL)
{
bisector->setYStar(p,bot->getLocation());
pQueue.insert(bisector);
}
}
else break;
}
} catch( GeoEx &ex )
{
#ifdef __WIN32__
ex.Explain( DiagOutWin("Erreur") );
#else
DiagOutDOS out(cerr);
ex.Explain( out );
#endif
}
// Les halfEdges restant sont les edges non connectees du diagramme.
/*
for(lbnd=ELright(ELleftend); lbnd != ELrightend; lbnd=ELright(lbnd))
{
e = lbnd -> ELedge;
out_ep(e);
}
*/
pQueue.clear();
delete pQueue.getSentinel();
halfEdge::terminate();
// Sanity Check
/*
* Un diagramme de Voronoi de N sites possede au plus 2N - 5 vertices
* et 3N - 6 aretes.
* ( Computational Geometry, [PREPARATA,SHAMOS] P.211 )
*/
D( size_t N = edgeList->Faces().entries(); )
D( cout << "Sites :" << N << " Vertices :" << edgeList->Vertices().entries() << " Aretes :" << edgeList->Edges().entries() << endl; )
// Ne tient pas compte des vertices non attaches au diagrame
// Cela peut parfois donner une fausse alarme.
D( if( edgeList->Vertices().entries() > 2*N - 5 ) cout << "Voronoi: N = " << N << " numVertices = " << edgeList->Vertices().entries() << endl; )
D( if( edgeList->Edges().entries() > 3*N - 6 ) cout << "Voronoi: N = " << N << " numEdges = " << edgeList->Edges().entries() << endl; )
return edgeList;
}
#ifdef __GNUG__
template class PQ<halfEdge>;
#endif
|