/* * Copyright (c) 2016 Vivid Solutions. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * * http://www.eclipse.org/org/documents/edl-v10.php. */ package org.locationtech.jts.operation.buffer; /** * @version 1.7 */ import java.util.Iterator; import java.util.List; import org.locationtech.jts.algorithm.CGAlgorithms; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geomgraph.DirectedEdge; import org.locationtech.jts.geomgraph.DirectedEdgeStar; import org.locationtech.jts.geomgraph.Edge; import org.locationtech.jts.geomgraph.Node; import org.locationtech.jts.geomgraph.Position; import org.locationtech.jts.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 Coordinate extremeCoord; private int minIndex = -1; private Coordinate minCoord = null; private DirectedEdge minDe = null; 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() { } public DirectedEdge getEdge() { return orientedDe; } public Coordinate getCoordinate() { return minCoord; } public void findEdge(List dirEdgeList) { /** * Check all forward DirectedEdges only. This is still general, * because each edge has a forward DirectedEdge. */ for (Iterator i = dirEdgeList.iterator(); i.hasNext();) { DirectedEdge de = (DirectedEdge) i.next(); if (! de.isForward()) continue; checkForRightmostCoordinate(de); } /** * If the rightmost point is a node, we need to identify which of * the incident edges is rightmost. */ Assert.isTrue(minIndex != 0 || minCoord.equals(minDe.getCoordinate()) , "inconsistency in rightmost processing"); if (minIndex == 0 ) { findRightmostEdgeAtNode(); } else { findRightmostEdgeAtVertex(); } /** * now check that the extreme side is the R side. * If not, use the sym instead. */ orientedDe = minDe; int rightmostSide = getRightmostSide(minDe, minIndex); if (rightmostSide == Position.LEFT) { orientedDe = minDe.getSym(); } } private void findRightmostEdgeAtNode() { Node node = minDe.getNode(); DirectedEdgeStar star = (DirectedEdgeStar) node.getEdges(); 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 (! minDe.isForward()) { minDe = minDe.getSym(); minIndex = minDe.getEdge().getCoordinates().length - 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. */ Coordinate[] pts = minDe.getEdge().getCoordinates(); Assert.isTrue(minIndex > 0 && minIndex < pts.length, "rightmost point expected to be interior vertex of edge"); Coordinate pPrev = pts[minIndex - 1]; Coordinate pNext = pts[minIndex + 1]; int orientation = CGAlgorithms.computeOrientation(minCoord, pNext, pPrev); boolean usePrev = false; // both segments are below min point if (pPrev.y < minCoord.y && pNext.y < minCoord.y && orientation == CGAlgorithms.COUNTERCLOCKWISE) { usePrev = true; } else if (pPrev.y > minCoord.y && pNext.y > minCoord.y && 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) { minIndex = minIndex - 1; } } private void checkForRightmostCoordinate(DirectedEdge de) { Coordinate[] coord = de.getEdge().getCoordinates(); for (int i = 0; i < coord.length - 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 if (minCoord == null || coord[i].x > minCoord.x ) { minDe = de; minIndex = i; minCoord = coord[i]; } //} } } private int getRightmostSide(DirectedEdge de, 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 minCoord = null; checkForRightmostCoordinate(de); } return side; } private int getRightmostSideOfSegment(DirectedEdge de, int i) { Edge e = de.getEdge(); Coordinate coord[] = e.getCoordinates(); if (i < 0 || i + 1 >= coord.length) return -1; if (coord[i].y == coord[i + 1].y) return -1; // indicates edge is parallel to x-axis int pos = Position.LEFT; if (coord[i].y < coord[i + 1].y) pos = Position.RIGHT; return pos; } }