/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.revolsys.geometry.operation.buffer;
/**
* @version 1.7
*/
import java.util.List;
import com.revolsys.geometry.algorithm.CGAlgorithms;
import com.revolsys.geometry.algorithm.CGAlgorithmsDD;
import com.revolsys.geometry.geomgraph.DirectedEdge;
import com.revolsys.geometry.geomgraph.DirectedEdgeStar;
import com.revolsys.geometry.geomgraph.Edge;
import com.revolsys.geometry.geomgraph.Node;
import com.revolsys.geometry.geomgraph.Position;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.util.Assert;
/**
* A RightmostEdgeFinder find the DirectedEdge in a list which has the highest coordinate,
* and which is oriented L to R at that point. (I.e. the right side is on the RHS of the edge.)
*
* @version 1.7
*/
class RightmostEdgeFinder {
private Point minCoord = null;
private DirectedEdge minDe = null;
// private Point extremeCoord;
private int minIndex = -1;
private DirectedEdge orientedDe = null;
/**
* A RightmostEdgeFinder finds the DirectedEdge with the rightmost coordinate.
* The DirectedEdge returned is guaranteed to have the R of the world on its RHS.
*/
public RightmostEdgeFinder() {
}
private void checkForRightmostCoordinate(final DirectedEdge de) {
final Edge edge = de.getEdge();
for (int i = 0; i < edge.getVertexCount() - 1; i++) {
// only check vertices which are the start or end point of a
// non-horizontal segment
// <FIX> MD 19 Sep 03 - NO! we can test all vertices, since the rightmost
// must have a non-horiz segment adjacent to it
final int i1 = i;
final Point point = edge.getPoint(i1);
if (this.minCoord == null || point.getX() > this.minCoord.getX()) {
this.minDe = de;
this.minIndex = i;
this.minCoord = point;
}
// }
}
}
public void findEdge(final List<DirectedEdge> dirEdgeList) {
/**
* Check all forward DirectedEdges only. This is still general,
* because each edge has a forward DirectedEdge.
*/
for (final DirectedEdge de : dirEdgeList) {
if (de.isForward()) {
checkForRightmostCoordinate(de);
}
}
/**
* If the rightmost point is a node, we need to identify which of
* the incident edges is rightmost.
*/
Assert.isTrue(this.minIndex != 0 || this.minCoord.equals(this.minDe.getCoordinate()),
"inconsistency in rightmost processing");
if (this.minIndex == 0) {
findRightmostEdgeAtNode();
} else {
findRightmostEdgeAtVertex();
}
/**
* now check that the extreme side is the R side.
* If not, use the sym instead.
*/
this.orientedDe = this.minDe;
final int rightmostSide = getRightmostSide(this.minDe, this.minIndex);
if (rightmostSide == Position.LEFT) {
this.orientedDe = this.minDe.getSym();
}
}
private void findRightmostEdgeAtNode() {
final Node node = this.minDe.getNode();
final DirectedEdgeStar star = (DirectedEdgeStar)node.getEdges();
this.minDe = star.getRightmostEdge();
// the DirectedEdge returned by the previous call is not
// necessarily in the forward direction. Use the sym edge if it isn't.
if (!this.minDe.isForward()) {
this.minDe = this.minDe.getSym();
this.minIndex = this.minDe.getEdge().getVertexCount() - 1;
}
}
private void findRightmostEdgeAtVertex() {
/**
* The rightmost point is an interior vertex, so it has a segment on either side of it.
* If these segments are both above or below the rightmost point, we need to
* determine their relative orientation to decide which is rightmost.
*/
final Edge edge = this.minDe.getEdge();
Assert.isTrue(this.minIndex > 0 && this.minIndex < edge.getVertexCount(),
"rightmost point expected to be interior vertex of edge");
final Point pPrev = edge.getPoint(this.minIndex - 1);
final Point pNext = edge.getPoint(this.minIndex + 1);
final int orientation = CGAlgorithmsDD.orientationIndex(this.minCoord, pNext, pPrev);
boolean usePrev = false;
// both segments are below min point
if (pPrev.getY() < this.minCoord.getY() && pNext.getY() < this.minCoord.getY()
&& orientation == CGAlgorithms.COUNTERCLOCKWISE) {
usePrev = true;
} else if (pPrev.getY() > this.minCoord.getY() && pNext.getY() > this.minCoord.getY()
&& orientation == CGAlgorithms.CLOCKWISE) {
usePrev = true;
}
// if both segments are on the same side, do nothing - either is safe
// to select as a rightmost segment
if (usePrev) {
this.minIndex = this.minIndex - 1;
}
}
public Point getCoordinate() {
return this.minCoord;
}
public DirectedEdge getEdge() {
return this.orientedDe;
}
private int getRightmostSide(final DirectedEdge de, final int index) {
int side = getRightmostSideOfSegment(de, index);
if (side < 0) {
side = getRightmostSideOfSegment(de, index - 1);
}
if (side < 0) {
// reaching here can indicate that segment is horizontal
// Assert.shouldNeverReachHere("problem with finding rightmost side of
// segment at "
// + de.getCoordinate());
// testing only
this.minCoord = null;
checkForRightmostCoordinate(de);
}
return side;
}
private int getRightmostSideOfSegment(final DirectedEdge de, final int i) {
final Edge edge = de.getEdge();
if (i < 0 || i + 1 >= edge.getVertexCount()) {
return -1;
}
final Point p1 = edge.getPoint(i);
final Point p2 = edge.getPoint(i + 1);
if (p1.getY() == p2.getY()) {
return -1; // indicates edge is parallel to x-axis
}
int pos = Position.LEFT;
if (p1.getY() < p2.getY()) {
pos = Position.RIGHT;
}
return pos;
}
}