/*
* The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T
* Bell Laboratories.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
package com.bioxx.jmapgen.com.nodename.delaunay;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import com.bioxx.jmapgen.Point;
public class Voronoi
{
private SiteList _sites;
private HashMap<Point, Site> _siteMap;
private Vector<Triangle> _triangles;
private Vector<Edge> _edges;
private Rectangle _plotBounds;
private static Site bottomMostSite = null;
public Voronoi(Vector<Point> points, Rectangle plotBounds)
{
_sites = new SiteList();
_siteMap = new HashMap<Point, Site>();
addSites(points);
_plotBounds = plotBounds;
_triangles = new Vector<Triangle>();
_edges = new Vector<Edge>();
fortunesAlgorithm();
}
private void addSites(Vector<Point> points)
{
int length = points.size();
for (int i = 0; i < length; ++i)
{
addSite(points.get(i), i);
}
}
private void addSite(Point p, int index)
{
Site site = Site.create(p, index);
_sites.push(site);
_siteMap.put(p, site);
}
public Vector<Edge> getEdges()
{
return _edges;
}
public Rectangle getPlotBounds()
{
return _plotBounds;
}
public Vector<Point> region(Point p)
{
Site site = _siteMap.get(p);
if (site == null)
{
return new Vector<Point>();
}
return site.region(_plotBounds);
}
// TODO: bug: if you call this before you call region(), something goes wrong :(
public Vector<Point> neighborSitesForSite(Point coord)
{
Vector<Point> points = new Vector<Point>();
Site site = _siteMap.get(coord);
if (site == null)
{
return points;
}
Vector<Site> sites = site.neighborSites();
for (int i = 0; i < sites.size(); ++i)
{
Site neighbor = sites.get(i);
points.add(neighbor.getCoord());
}
return points;
}
public Vector<Point> siteCoords()
{
return _sites.siteCoords();
}
private void fortunesAlgorithm()
{
Site newSite, bottomSite, topSite, tempSite;
Vertex v, vertex;
Point newintstar = new Point();
LR leftRight;
Halfedge lbnd, rbnd, llbnd, rrbnd, bisector;
Edge edge;
Rectangle dataBounds = _sites.getSitesBounds();
int sqrt_nsites = (int) (Math.sqrt(_sites.getLength() + 4));
HalfedgePriorityQueue heap = new HalfedgePriorityQueue(dataBounds.y, dataBounds.height, sqrt_nsites);
EdgeList edgeList = new EdgeList(dataBounds.x, dataBounds.width, sqrt_nsites);
Vector<Halfedge> halfEdges = new Vector<Halfedge>();
Vector<Vertex> vertices = new Vector<Vertex>();
bottomMostSite = _sites.next();
newSite = _sites.next();
for (;;)
{
if (heap.empty() == false)
{
newintstar = heap.min();
}
if (newSite != null
&& (heap.empty() || compareByYThenX(newSite, newintstar) < 0))
{
/* new site is smallest */
//trace("smallest: new site " + newSite);
// Step 8:
lbnd = edgeList.edgeListLeftNeighbor(newSite.getCoord()); // the Halfedge just to the left of newSite
//trace("lbnd: " + lbnd);
rbnd = lbnd.edgeListRightNeighbor; // the Halfedge just to the right
//trace("rbnd: " + rbnd);
bottomSite = rightRegion(lbnd); // this is the same as leftRegion(rbnd)
// this Site determines the region containing the new site
//trace("new Site is in region of existing site: " + bottomSite);
// Step 9:
edge = Edge.createBisectingEdge(bottomSite, newSite);
//trace("new edge: " + edge);
_edges.add(edge);
bisector = Halfedge.create(edge, LR.LEFT);
halfEdges.add(bisector);
// inserting two Halfedges into edgeList constitutes Step 10:
// insert bisector to the right of lbnd:
edgeList.insert(lbnd, bisector);
// first half of Step 11:
if ((vertex = Vertex.intersect(lbnd, bisector)) != null)
{
vertices.add(vertex);
heap.remove(lbnd);
lbnd.vertex = vertex;
lbnd.ystar = vertex.getY() + newSite.dist(vertex);
heap.insert(lbnd);
}
lbnd = bisector;
bisector = Halfedge.create(edge, LR.RIGHT);
halfEdges.add(bisector);
// second Halfedge for Step 10:
// insert bisector to the right of lbnd:
edgeList.insert(lbnd, bisector);
// second half of Step 11:
if (rbnd != null && (vertex = Vertex.intersect(bisector, rbnd)) != null)
{
vertices.add(vertex);
bisector.vertex = vertex;
bisector.ystar = vertex.getY() + newSite.dist(vertex);
heap.insert(bisector);
}
newSite = _sites.next();
}
else if (heap.empty() == false)
{
/* intersection is smallest */
lbnd = heap.extractMin();
llbnd = lbnd.edgeListLeftNeighbor;
rbnd = lbnd.edgeListRightNeighbor;
rrbnd = rbnd.edgeListRightNeighbor;
bottomSite = leftRegion(lbnd);
topSite = rightRegion(rbnd);
// these three sites define a Delaunay triangle
// (not actually using these for anything...)
//_triangles.push(new Triangle(bottomSite, topSite, rightRegion(lbnd)));
v = lbnd.vertex;
v.setIndex();
lbnd.edge.setVertex(lbnd.leftRight, v);
rbnd.edge.setVertex(rbnd.leftRight, v);
edgeList.remove(lbnd);
heap.remove(rbnd);
edgeList.remove(rbnd);
leftRight = LR.LEFT;
if (bottomSite.getY() > topSite.getY())
{
tempSite = bottomSite; bottomSite = topSite; topSite = tempSite; leftRight = LR.RIGHT;
}
edge = Edge.createBisectingEdge(bottomSite, topSite);
_edges.add(edge);
bisector = Halfedge.create(edge, leftRight);
halfEdges.add(bisector);
edgeList.insert(llbnd, bisector);
edge.setVertex(LR.other(leftRight), v);
if(edge._leftVertex == null || edge._rightVertex == null)
try {
throw new Exception();
} catch (Exception e)
{
}
if ((vertex = Vertex.intersect(llbnd, bisector)) != null)
{
vertices.add(vertex);
heap.remove(llbnd);
llbnd.vertex = vertex;
llbnd.ystar = vertex.getY() + bottomSite.dist(vertex);
heap.insert(llbnd);
}
if ((vertex = Vertex.intersect(bisector, rrbnd)) != null)
{
vertices.add(vertex);
bisector.vertex = vertex;
bisector.ystar = vertex.getY() + bottomSite.dist(vertex);
heap.insert(bisector);
}
}
else
{
break;
}
}
for(Iterator<Edge> iter = _edges.iterator(); iter.hasNext();)
{
edge = iter.next();
edge.clipVertices(_plotBounds);
if(!edge.getVisible())
{
iter.remove();
}
}
// we need the vertices to clip the edges
for(int i = 0; i < _edges.size(); i++)
{
edge = _edges.get(i);
edge.clipVertices(_plotBounds);
}
}
public static Site leftRegion(Halfedge he)
{
Edge edge = he.edge;
if (edge == null)
{
return bottomMostSite;
}
return edge.site(he.leftRight);
}
public static Site rightRegion(Halfedge he)
{
Edge edge = he.edge;
if (edge == null)
{
return bottomMostSite;
}
return edge.site(LR.other(he.leftRight));
}
public static int compareByYThenX(Site s1, Object s2)
{
if(s2 instanceof Site)
{
if (s1.getY() < ((Site)s2).getY()) return -1;
if (s1.getY() > ((Site)s2).getY()) return 1;
if (s1.getX() < ((Site)s2).getX()) return -1;
if (s1.getX() > ((Site)s2).getX()) return 1;
}
else if(s2 instanceof Point)
{
if (s1.getY() < ((Point)s2).getY()) return -1;
if (s1.getY() > ((Point)s2).getY()) return 1;
if (s1.getX() < ((Point)s2).getX()) return -1;
if (s1.getX() > ((Point)s2).getX()) return 1;
}
return 0;
}
}