All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tinfour.edge.QuadEdge Maven / Gradle / Ivy

/* --------------------------------------------------------------------
 * Copyright 2015 Gary W. Lucas.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ---------------------------------------------------------------------
 */

 /*
 * -----------------------------------------------------------------------
 *
 * Revision History:
 * Date     Name         Description
 * ------   ---------    -------------------------------------------------
 * 07/2015  G. Lucas     Created
 * 12/2016  G. Lucas     Introduced support for constrained Delaunay
 * 11/2017  G. Lucas     Refactored to support constrained regions
 *
 * Notes:
 * The layout of this class is intended to accomplish the following:
 *  a) conserve memory in applications with a very large number of edges
 *  b) support the use of an edge pool by providing an application ID field.
 *
 * The memory layout is based on the following ideas
 *  a) In most JVM's,  the size in memory of an object must be a multiple
 *     of eight.  So an object containing one byte would still have to
 *     be eight bytes large in memory.
 *  b) In addition to the memory required by the object itself, all objects
 *     require a certain amount of memory to control the allocation of
 *     objects on the heap. This value is out-of-the-control of any
 *     code and is JVM-implementation-dependent.  Eight bytes appear to be
 *     the minimum.
 *  c) integers are 4 bytes. doubles 8. floats 4.
 *  d) although it is implementation dependent, many Java JVM's can represent
 *     object references in 4 bytes provided the memory size of the JVM is
 *     less than 32 gigabytes.  Although the normal range of a 4 byte memory
 *     address would be 4 gigabytes, some of the major JVM's (Hotspot for
 *     Windows) take advantage of the fact that objects are aligned in memory
 *     on 8-byte boundaries to "compress"  references by dividing them by 8
 *     when storing them in an object and multiplying them by eight when
 *     accessing them.
 *       so assume references are 4 bytes.
 *  e) all objects include a reference to their own class definition.
 *
 *  So this class contains the following fundamental member elements
 *  all of which are 4 bytes in JVMs where references are compressed:
 *
 *      reference to the class        (4)
 *      reference to the dual         (4)
 *      reference to the vertex       (4)
 *      reference to the forward edge (4)
 *      reference to the reverse edge (4)
 *           total                    20
 *
 *  Since the size of the class must be a multiple of 8, that leaves 4
 *  bytes which will be allocated in memory no matter what.  So we
 *  put that to use by defining an integer application data element
 *
 *      integer index                (4)
 *          total                     24
 *
 * The index element of this class is used to assign a unique integer value
 * to each edge created in the TIN-building process or other applications.
 * In the case of the EdgePool, it is used to manage allocation of edges.
 * If instances are not managed by an EdgePool, the index value is free
 * for use in other interpretations.
 *
 * CONSTRAINTS
 *   In QuadEdgePartner, the index value is used as a way of indicating
 * whether the edge is a constrained edge according to the definition
 * of a Constrained Delaunay Triangulation.   To conserve memory and
 * keep the size of the class small, the low order two bytes are
 * allocated to indicating the "constraint index". The use of a constraint
 * index is also intended to support operations in which the constraint index
 * of an edge can be traced back to the constraint that defined it.
 * So, typically, the constraint index is an index back to the list of
 * constraints that was added to the incremental-TIN implementation.
 * If the lower two bytes of the QuadEdgePartner's index element are clear,
 * so that (index&0xffff) == 0, the edge is considered not constrained.  When
 * the constraint-index is set using the setConstraint() method, the code adds
 * one to the value stored. The getConstraintIndex() method masks out the
 * index field and subtracts a value of 1 from the result before returning it.
 * The consequence of this design choice is that the maximum value that can
 * be stored in the low-order two bytes of the index element is (2^16-1)-1,
 * or 65534...
 *
 * Special considerations for setForward() and setReverse()
 *   Even though this class does implement the IQuadEdge interface, the
 * setForward() and setReverse() methods do not accept IQuadEdge as an
 * interface.  In an earlier version, there was an experiment that used
 * IQuadEdge for the forward and reverse member elements, however the overhead
 * due to Java type casting resulted in a 20 percent degradation in performance.
 * -----------------------------------------------------------------------
 */
package org.tinfour.edge;

import java.util.Formatter;
import org.tinfour.common.IQuadEdge;
import org.tinfour.common.Vertex;
import static org.tinfour.edge.QuadEdgeConstants.CONSTRAINT_REGION_INTERIOR_FLAG;
import static org.tinfour.edge.QuadEdgeConstants.CONSTRAINT_REGION_BORDER_FLAG;
import static org.tinfour.edge.QuadEdgeConstants.SYNTHETIC_EDGE_FLAG;

/**
 * A representation of an edge with forward and reverse links on one
 * side and counterpart links attached to its dual (other side).
 * 

* This concept is based on the structure popularized by * Guibas, L. and Stolfi, J. (1985) "Primitives for the * manipulation of subdivisions and the computation of Voronoi diagrams" * ACM Transactions on Graphics, 4(2), 1985, p. 75-123. */ public class QuadEdge implements IQuadEdge { /** * An arbitrary index value. For IncrementalTin, the index * is used to manage the edge pool. */ int index; /** * The dual of this edge (always valid, never null. */ QuadEdge dual; /** * The initial vertex of this edge, the second vertex of * the dual. */ Vertex v; /** * The forward link of this edge. */ QuadEdge f; /** * The reverse link of this edge. */ QuadEdge r; /** * Constructs the edge and its dual. */ QuadEdge() { dual = new QuadEdgePartner(this); } /** * Construct the edge setting its dual with the specified reference. * * @param partner a valid element. */ QuadEdge(final QuadEdge partner) { dual = partner; } /** * Construct the edge and its dual assigning the pair the specified index. * * @param index an arbitrary integer value. */ public QuadEdge(final int index) { dual = new QuadEdgePartner(this); this.index = index; } /** * Sets the vertices for this edge (and its dual). * * @param a the initial vertex, must be a valid reference. * @param b the second vertex, may be a valid reference or a * null for a ghost edge. */ public void setVertices(final Vertex a, final Vertex b) { this.v = a; this.dual.v = b; } /** * Gets the initial vertex for this edge. * * @return a valid reference. */ @Override public final Vertex getA() { return v; } /** * Sets the initial vertex for this edge. * * @param a a valid reference. */ public final void setA(final Vertex a) { this.v = a; } /** * Gets the second vertex for this edge. * * @return a valid reference or a null for a ghost edge. */ @Override public final Vertex getB() { return dual.v; } /** * Sets the second (B) vertex for this edge (also the A reference of * the dual edge). * * @param b a valid reference or a null for a ghost edge. */ public final void setB(final Vertex b) { dual.v = b; } /** * Gets the forward reference of the edge. * * @return a valid reference. */ @Override public final QuadEdge getForward() { return f; } /** * Gets the reverse reference of the edge. * * @return a valid reference. */ @Override public final QuadEdge getReverse() { return r; } /** * Gets the forward reference of the dual. * * @return a valid reference */ @Override public final QuadEdge getForwardFromDual() { return dual.f; } /** * Gets the reverse link of the dual. * * @return a valid reference */ @Override public final QuadEdge getReverseFromDual() { return dual.r; } /** * Gets the dual of the reverse link. * * @return a valid reference */ @Override public final QuadEdge getDualFromReverse() { return r.dual; } /** * Sets the forward reference for this edge. * * @param e a valid reference */ public final void setForward(final QuadEdge e) { this.f = e; e.r = this; // forwardCheck(this, e); } /** * Sets the reverse reference for this edge. * * @param e a valid reference */ public final void setReverse(final QuadEdge e) { this.r = e; e.f = this; // forwardCheck(e, this); } /** * Sets the forward link to the dual of this edge. * * @param e a valid reference */ public final void setDualForward(final QuadEdge e) { dual.f = e; e.r = dual; // forwardCheck(dual, e); } /** * Sets the reverse link of the dual to this edge. * * @param e a valid reference */ public final void setDualReverse(final QuadEdge e) { dual.r = e; e.f = dual; // forwardCheck(e, dual); } /** * Gets the dual edge to this instance. * * @return a valid edge. */ @Override public final QuadEdge getDual() { return dual; } /** * Gets the index value for this edge. * * @return an integer value */ @Override public int getIndex() { return index; } /** * Sets the index value for this edge. Because this index value is * used by edge-pool implementations and for other data management activities, * the scope of this method is limited to protected. The actual definition * of this element is left to the application that uses it. * * @param index an integer value */ protected void setIndex(final int index) { this.index = index; } /** * Gets the reference to the side-zero edge of the pair. * * @return a link to the side-zero edge of the pair. */ @Override public QuadEdge getBaseReference() { return this; } /** * Gets the index of the constraint associated with this edge. * Constraint index values must be in the range 0 to Integer.MAX_VALUE, * with negative numbers being reserved for internal use by the * Tinfour library, * * @return if constrained, a positive integer; otherwise, a negative value. */ @Override public int getConstraintIndex() { return dual.getConstraintIndex(); } @Override public void setConstraintIndex(int constraintIndex) { dual.setConstraintIndex(constraintIndex); } /** * Gets the index of the constrain associated with * * @return true if the edge is constrained; otherwise, false. */ @Override public boolean isConstrained() { return dual.isConstrained(); } @Override public void setConstrained(int constraintIndex) { dual.setConstrained(constraintIndex); } /** * Sets all vertices and link references to null (the link to a dual * is not affected). */ public void clear() { // note that the index of the partner is set to -1, // but the index of the base, which is used for management purposes // is left alone. this.v = null; this.f = null; this.r = null; dual.v = null; dual.f = null; dual.r = null; dual.index = 0; } /** * Gets a name string for the edge by prepending the index value * with a + or - string depending on its side (+ for side zero, - for side 1). * * @return a valid string. */ String getName() { return Integer.toString(getIndex()) ; } @Override public String toString() { Vertex a = v; Vertex b = dual.v; if (a == null && b == null) { return String.format("%9d -- Undefined", getIndex()); } StringBuilder sb = new StringBuilder(); try (Formatter fmt = new Formatter(sb)) { fmt.format("%9s %9s <-- (%9s,%9s) --> %9s", getName(), (r == null ? "null" : r.getName()), (a == null ? "gv" : a.getLabel()), (b == null ? "gv" : b.getLabel()), (f == null ? "null" : f.getName()) ); fmt.flush(); } if (this.isConstrained()) { sb.append(" constrained "); if (this.isConstrainedRegionBorder()) { sb.append("region border "); } sb.append(Integer.toString(getConstraintIndex())); } else if (isConstrainedRegionInterior()) { sb.append(" constrained region interior "); sb.append(Integer.toString(getConstraintIndex())); } return sb.toString(); } /** * Gets the length of the edge. * * @return a positive floating point value */ @Override public double getLength() { if (v == null || dual.v == null) { return Double.NaN; } double dx = v.x - dual.v.x; double dy = v.y - dual.v.y; return Math.sqrt(dx * dx + dy * dy); } /** * Indicates which side of an edge a particular QuadEdge instance is * attached to. The side value is a strictly arbitrary index used for * algorithms that need to be able to assign a unique index to * both sides of an edge. * * @return a value of 0 or 1. */ @Override public int getSide() { return 0; } /** * An implementation of the equals method which check for a matching * reference. * * @param o a valid reference or a null * @return true if the specified reference matches this. */ @Override public boolean equals(Object o) { if (o instanceof QuadEdge) { return this == o; } return false; } @Override public int hashCode() { int hash = 7; hash = 11 * hash + this.index; return hash; } @Override public boolean isConstrainedRegionMember() { return dual.isConstrainedRegionMember(); } @Override public boolean isConstrainedRegionInterior() { return dual.isConstrainedRegionInterior(); } @Override public boolean isConstrainedRegionBorder() { return dual.isConstrainedRegionBorder(); } @Override public void setConstrainedRegionBorderFlag() { dual.index |= (CONSTRAINT_REGION_BORDER_FLAG); } @Override public void setConstrainedRegionInteriorFlag() { dual.index |= CONSTRAINT_REGION_INTERIOR_FLAG; } @Override public void setSynthetic(boolean status){ if(status){ dual.index |= SYNTHETIC_EDGE_FLAG; }else{ dual.index &= ~SYNTHETIC_EDGE_FLAG; } } @Override public boolean isSynthetic(){ return (dual.index&SYNTHETIC_EDGE_FLAG)!=0; } @Override public Iterable pinwheel() { return new QuadEdgePinwheel(this); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy