package com.bioxx.jmapgen.com.nodename.delaunay; import java.awt.Rectangle; import java.util.Comparator; import java.util.Vector; import com.bioxx.jmapgen.Point; import com.bioxx.jmapgen.com.nodename.geom.LineSegment; /** * The line segment connecting the two Sites is part of the Delaunay triangulation; * the line segment connecting the two Vertices is part of the Voronoi diagram * @author ashaw * */ public class Edge implements Comparator<Edge> { private static Vector<Edge> _pool = new Vector<Edge>(); public static final Edge DELETED = new Edge(); public int _edgeIndex; public Edge() { _edgeIndex = _nedges++; init(); } private void init() { _sites = new Site[2]; } /** * This is the only way to create a new Edge * @param site0 * @param site1 * @return * @throws Exception * */ public static Edge createBisectingEdge(Site site0, Site site1) { double dx, dy, absdx, absdy; double a, b, c; dx = site1.getX() - site0.getX(); dy = site1.getY() - site0.getY(); absdx = dx > 0 ? dx : -dx; absdy = dy > 0 ? dy : -dy; c = site0.getX() * dx + site0.getY() * dy + (dx * dx + dy * dy) * 0.5; if (absdx > absdy) { a = 1.0; b = dy/dx; c /= dx; } else { b = 1.0; a = dx/dy; c /= dy; } Edge edge = Edge.create(); edge.setLeftSite(site0); edge.setRightSite(site1); site0.addEdge(edge); site1.addEdge(edge); edge._leftVertex = null; edge._rightVertex = null; edge.a = a; edge.b = b; edge.c = c; //trace("createBisectingEdge: a ", edge.a, "b", edge.b, "c", edge.c); return edge; } private static Edge create() { Edge edge; if (_pool.size() > 0) { edge = _pool.lastElement(); _pool.remove(_pool.size()-1); edge.init(); } else { edge = new Edge(); } return edge; } public LineSegment delaunayLine() { // draw a line connecting the input Sites for which the edge is a bisector: Site ls = getLeftSite(); Site rs = getRightSite(); if(ls != null && rs != null) return new LineSegment(ls.getCoord(), rs.getCoord()); return null; } public LineSegment voronoiEdge() { if (!getVisible()) return new LineSegment(null, null); return new LineSegment((Point)_clippedVertices[LR.LEFT.value], (Point)_clippedVertices[LR.RIGHT.value]); } public static int _nedges = 0; // the equation of the edge: ax + by = c public double a, b, c; // the two Voronoi vertices that the edge connects // (if one of them is null, the edge extends to infinity) Vertex _leftVertex; public Vertex getLeftVertex() { return _leftVertex; } Vertex _rightVertex; public Vertex getRightVertex() { return _rightVertex; } Vertex vertex(LR leftRight) { return (leftRight == LR.LEFT) ? _leftVertex : _rightVertex; } void setVertex(LR leftRight, Vertex v) { if (leftRight == LR.LEFT) { _leftVertex = v; } else { _rightVertex = v; } } boolean isPartOfConvexHull() { return (_leftVertex == null || _rightVertex == null); } public double sitesDistance() { return Point.distance(this.getLeftSite().getCoord(), this.getRightSite().getCoord()); } public static int compareSitesDistances_MAX(Edge edge0, Edge edge1) { double length0 = edge0.sitesDistance(); double length1 = edge1.sitesDistance(); if (length0 < length1) { return 1; } if (length0 > length1) { return -1; } return 0; } public static int compareSitesDistances(Edge edge0, Edge edge1) { return - compareSitesDistances_MAX(edge0, edge1); } // Once clipVertices() is called, this Dictionary will hold two Points // representing the clipped coordinates of the left and right ends... private Point[] _clippedVertices; public Point[] getClippedEnds() { return _clippedVertices; } // unless the entire Edge is outside the bounds. // In that case visible will be false: boolean getVisible() { return _clippedVertices != null; } // the two input Sites for which this Edge is a bisector: private Site[] _sites; void setLeftSite(Site s) { _sites[LR.LEFT.value] = s; } Site getLeftSite() { return _sites[LR.LEFT.value]; } void setRightSite(Site s) { _sites[LR.RIGHT.value] = s; } Site getRightSite() { return _sites[LR.RIGHT.value]; } Site site(LR leftRight) { return _sites[leftRight.value]; } public String toString() { return "Edge " + _edgeIndex + "; sites " + _sites[LR.LEFT.value] + ", " + _sites[LR.RIGHT.value] + "; endVertices " + (_leftVertex != null ? _leftVertex.getVertexIndex() : "null") + ", " + (_rightVertex != null ? _rightVertex.getVertexIndex() : "null") + "::"; } /** * Set _clippedVertices to contain the two ends of the portion of the Voronoi edge that is visible * within the bounds. If no part of the Edge falls within the bounds, leave _clippedVertices null. * @param bounds * */ void clipVertices(Rectangle bounds) { double xmin = bounds.x; double ymin = bounds.y; double xmax = bounds.getMaxX(); double ymax = bounds.getMaxY(); Vertex vertex0, vertex1; double x0, x1, y0, y1; if (a == 1.0 && b >= 0.0) { vertex0 = _rightVertex; vertex1 = _leftVertex; } else { vertex0 = _leftVertex; vertex1 = _rightVertex; } if (a == 1.0) { y0 = ymin; if (vertex0 != null && vertex0.getY() > ymin) { y0 = vertex0.getY(); } if (y0 > ymax) { return; } x0 = c - b * y0; y1 = ymax; if (vertex1 != null && vertex1.getY() < ymax) { y1 = vertex1.getY(); } if (y1 < ymin) { return; } x1 = c - b * y1; if ((x0 > xmax && x1 > xmax) || (x0 < xmin && x1 < xmin)) { return; } if (x0 > xmax) { x0 = xmax; y0 = (c - x0)/b; } else if (x0 < xmin) { x0 = xmin; y0 = (c - x0)/b; } if (x1 > xmax) { x1 = xmax; y1 = (c - x1)/b; } else if (x1 < xmin) { x1 = xmin; y1 = (c - x1)/b; } } else { x0 = xmin; if (vertex0 != null && vertex0.getX() > xmin) { x0 = vertex0.getX(); } if (x0 > xmax) { return; } y0 = c - a * x0; x1 = xmax; if (vertex1 != null && vertex1.getX() < xmax) { x1 = vertex1.getX(); } if (x1 < xmin) { return; } y1 = c - a * x1; if ((y0 > ymax && y1 > ymax) || (y0 < ymin && y1 < ymin)) { return; } if (y0 > ymax) { y0 = ymax; x0 = (c - y0)/a; } else if (y0 < ymin) { y0 = ymin; x0 = (c - y0)/a; } if (y1 > ymax) { y1 = ymax; x1 = (c - y1)/a; } else if (y1 < ymin) { y1 = ymin; x1 = (c - y1)/a; } } _clippedVertices = new Point[2]; if (vertex0 == _leftVertex) { _clippedVertices[LR.LEFT.value] = new Point(x0, y0); _clippedVertices[LR.RIGHT.value] = new Point(x1, y1); } else { _clippedVertices[LR.RIGHT.value] = new Point(x0, y0); _clippedVertices[LR.LEFT.value] = new Point(x1, y1); } } @Override public int compare(Edge arg0, Edge arg1) { return compareSitesDistances(arg0, arg1); } }