/* * 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.elevation.tin.quadedge.intscale; import com.revolsys.geometry.model.Side; /** * A class that represents the edge data structure which implements the quadedge algebra. * The quadedge algebra was described in a well-known paper by Guibas and Stolfi, * "Primitives for the manipulation of general subdivisions and the computation of Voronoi diagrams", * <i>ACM Transactions on Graphics</i>, 4(2), 1985, 75-123. * <p> * Each edge object is part of a quartet of 4 edges, * linked via their <tt>rot</tt> references. * Any edge in the group may be accessed using a series of {@link #rot()} operations. * Quadedges in a subdivision are linked together via their <tt>next</tt> references. * The linkage between the quadedge quartets determines the topology * of the subdivision. * <p> * The edge class does not contain separate information for vertices or faces; a Point is implicitly * defined as a ring of edges (created using the <tt>next</tt> field). * * @author David Skea * @author Martin Davis */ public class QuadEdge { private QuadEdge next; // A reference to a connected edge // the dual of this edge, directed from right to left private QuadEdge rot; private PointIntXYZ fromPoint; // The Point that this edge // represents private short visitIndex = Short.MIN_VALUE; /** * Quadedges must be made using {@link makeEdge}, * to ensure proper construction. */ QuadEdge() { } QuadEdge(final PointIntXYZ fromPoint) { this.fromPoint = fromPoint; this.rot = null; this.next = this; } public QuadEdge(final PointIntXYZ fromPoint, final PointIntXYZ toPoint) { final QuadEdge q2 = new QuadEdge(toPoint); final QuadEdge q1 = new QuadEdge(null, q2, null); final QuadEdge q3 = new QuadEdge(null, this, q1); q2.rot = q3; q1.next = q3; this.fromPoint = fromPoint; this.rot = q1; this.next = this; } QuadEdge(final PointIntXYZ fromPoint, final QuadEdge rot, final QuadEdge next) { this.fromPoint = fromPoint; this.rot = rot; this.next = next; } // final QuadEdge q1 = base.rot(); // final QuadEdge q2 = new QuadEdge(toPoint); // final QuadEdge q3 = q2.rot(); // // q1.init(q2, q3); // q3.init(base, q1); /** * Marks this quadedge as being deleted. * This does not free the memory used by * this quadedge quartet, but indicates * that this edge no longer participates * in a subdivision. * */ public void delete() { this.rot = null; } /** * Gets the next CCW edge around (into) the destination of this edge. * * @return the next destination edge. */ public final QuadEdge dNext() { return this.sym().next.sym(); } @Override public boolean equals(final Object other) { return other == this; } /** * Gets the next CCW edge around the origin of this edge. * * @return the next linked edge. */ public final QuadEdge getFromNextEdge() { return this.next; } /** * Gets the Point for the edge's origin * * @return the origin Point */ public final PointIntXYZ getFromPoint() { return this.fromPoint; } /** * Gets the CCW edge around the left face following this edge. * * @return the next left face edge. */ public final QuadEdge getLeftNext() { return invRot().next.rot; } /** * Gets the CCW edge around the left face before this edge. * * @return the previous left face edge. */ public final QuadEdge getLeftPrevious() { return this.next.sym(); } /** * Gets the primary edge of this quadedge and its <tt>sym</tt>. * The primary edge is the one for which the origin * and destination coordinates are ordered * according to the standard {@link Coordinates} ordering * * @return the primary quadedge */ public QuadEdge getPrimary() { if (this.fromPoint.compareTo(getToPoint()) <= 0) { return this; } else { return sym(); } } /** * Gets the edge around the right face ccw following this edge. * * @return the next right face edge. */ public final QuadEdge getRightNext() { return this.rot.next.invRot(); } /** * Gets the edge around the right face ccw before this edge. * * @return the previous right face edge. */ public final QuadEdge getRightPrevious() { return this.sym().next; } /** * Gets the next CW edge around (into) the destination of this edge. * * @return the previous destination edge. */ public final QuadEdge getToNextEdge() { return this.invRot().next.invRot(); } /** * Gets the Point for the edge's destination * * @return the destination Point */ public final PointIntXYZ getToPoint() { return sym().fromPoint; } void init(final QuadEdge rot, final QuadEdge next) { this.rot = rot; this.next = next; } /** * Gets the dual of this edge, directed from its left to its right. * * @return the inverse rotated edge. */ public final QuadEdge invRot() { return this.rot.sym(); } /** * Tests whether this edge has been deleted. * * @return true if this edge has not been deleted. */ public boolean isLive() { return this.rot != null; } public boolean isSwapRequired(final int x, final int y) { final QuadEdge previousEdge = oPrev(); final PointIntXYZ previousToPoint = previousEdge.getToPoint(); final int previousToX = previousToPoint.getX(); final int previousToY = previousToPoint.getY(); final PointIntXYZ fromPoint = this.fromPoint; final int fromX = fromPoint.getX(); final int fromY = fromPoint.getY(); final PointIntXYZ toPoint = getToPoint(); final int toX = toPoint.getX(); final int toY = toPoint.getY(); final Side side = Side.getSide(fromX, fromY, toX, toY, previousToX, previousToY); if (side == Side.RIGHT) { final long deltaX1 = fromX - x; final long deltaY1 = fromY - y; final long deltaX2 = previousToX - x; final long deltaY2 = previousToY - y; final long deltaX3 = toX - x; final long deltaY3 = toY - y; final double abdet = deltaX1 * deltaY2 - deltaX2 * deltaY1; final double bcdet = deltaX2 * deltaY3 - deltaX3 * deltaY2; final double cadet = deltaX3 * deltaY1 - deltaX1 * deltaY3; final double alift = deltaX1 * deltaX1 + deltaY1 * deltaY1; final double blift = deltaX2 * deltaX2 + deltaY2 * deltaY2; final double clift = deltaX3 * deltaX3 + deltaY3 * deltaY3; final double disc = alift * bcdet + blift * cadet + clift * abdet; final boolean inCircle = disc > 0; if (inCircle) { return true; } } return false; } public boolean isVisited(final short visitIndex) { return this.visitIndex == visitIndex; } /** * Gets the next CW edge around (from) the origin of this edge. * * @return the previous edge. */ public final QuadEdge oPrev() { return this.rot.next.rot; } /** * Gets the dual of this edge, directed from its right to its left. * * @return the rotated edge */ public final QuadEdge rot() { return this.rot; } /*********************************************************************************************** * Data Access **********************************************************************************************/ /** * Sets the Point for this edge's origin * * @param fromPoint the origin Point */ void setFromPoint(final PointIntXYZ fromPoint) { this.fromPoint = fromPoint; } public void setVisited(final short visitIndex) { this.visitIndex = visitIndex; } /** * Splices two edges together or apart. * Splice affects the two edge rings around the origins of a and b, and, independently, the two * edge rings around the left faces of <tt>a</tt> and <tt>b</tt>. * In each case, (i) if the two rings are distinct, * Splice will combine them into one, or (ii) if the two are the same ring, Splice will break it * into two separate pieces. Thus, Splice can be used both to attach the two edges together, and * to break them apart. * * @param edge an edge to splice * */ public void splice(final QuadEdge edge) { final QuadEdge fromNextEdge1 = this.next; final QuadEdge fromNextEdge2 = edge.next; final QuadEdge alpha = fromNextEdge1.rot; final QuadEdge beta = fromNextEdge2.rot; final QuadEdge fromNextEdgeRot1 = beta.next; final QuadEdge fromNextEdgeRot2 = alpha.next; this.next = fromNextEdge2; edge.next = fromNextEdge1; alpha.next = fromNextEdgeRot1; beta.next = fromNextEdgeRot2; } /** * Turns an edge counterclockwise inside its enclosing quadrilateral. * * @param edge the quadedge to turn */ public void swap() { final QuadEdge edgePrevious = oPrev(); final QuadEdge sym = sym(); final QuadEdge b = sym.oPrev(); splice(edgePrevious); sym.splice(b); splice(edgePrevious.getLeftNext()); sym.splice(b.getLeftNext()); setFromPoint(edgePrevious.getToPoint()); sym.setFromPoint(b.getToPoint()); } /** * Gets the edge from the destination to the origin of this edge. * * @return the sym of the edge */ public final QuadEdge sym() { return this.rot.rot; } @Override public String toString() { final PointIntXYZ fromPoint = this.fromPoint; final PointIntXYZ toPoint = getToPoint(); return "LINESTRING(" + fromPoint.getX() + " " + fromPoint.getY() + " " + fromPoint.getZ() + "," + toPoint.getX() + " " + toPoint.getY() + " " + toPoint.getZ() + ")"; } }