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

gov.nasa.worldwind.formats.vpf.VPFWingedEdgeTraverser Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.worldwind.formats.vpf;

/**
 * Encapsulation of the Wiged-Edge Algorithm for VPF ring primitives, described in DIGEST Part 2, Annex C2.4.3. Given a
 * row from the ring primitive table, navigate the ring and edge primitive tables to construct the edge information
 * associated with the specified ring.
 *
 * @author dcollins
 * @version $Id: VPFWingedEdgeTraverser.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public class VPFWingedEdgeTraverser
{
    public interface EdgeTraversalListener
    {
        void nextEdge(int index, int primitiveId, boolean reverseCoordinates);
    }

    protected enum Orientation
    {
        LEFT,
        RIGHT,
        LEFT_AND_RIGHT
    }

    public VPFWingedEdgeTraverser()
    {
    }

    /**
     * Implementation of the Wiged-Edge Algorithm for ring primitives, described in DIGEST Part 2, Annex C2.4.3. Given a
     * row from the ring primitive table, navigate the ring and edge primitive tables to construct the edge information
     * associated with the specified ring.
     *
     * @param edgeInfoArray the edge primitive data.
     * @param listener      the ring edge listener, may be null.
     *
     * @return the number of edges composing the specified ring.
     */
    public int traverseRing(int faceId, int startEdgeId, VPFPrimitiveData.PrimitiveInfo[] edgeInfoArray,
        EdgeTraversalListener listener)
    {
        // 1. Determine which face primitive to construct.
        // The face is determined for us by the selection of a row in the face primitive table.

        // 2. Identify the start edge.
        // Select the row in the ring primitive table which is associated with the face primitive. Then select the row
        // in the edge primitive table which corresponds to the ring primitive. Essentially, we follow the face to a
        // ring, and the ring to a starting edge.

        // 3. Follow the edge network until we arrive back at the starting edge, or the data does not specify a next
        // edge. Travel in the direction according to which side of the edge (left or right) the face belongs to. If we
        // reach an auxiliary edge (face is both left and right of the edge), then travel to the next edge which does
        // not cause us to backtrack.

        int count = 0;
        int prevEdgeId;
        int curEdgeId = -1;
        int nextEdgeId = startEdgeId;

        do
        {
            prevEdgeId = curEdgeId;
            curEdgeId = nextEdgeId;

            if (listener != null)
            {
                listener.nextEdge(count, curEdgeId, this.getMustReverseCoordinates(faceId, prevEdgeId, curEdgeId,
                    edgeInfoArray));
            }

            count++;
        }
        while ((nextEdgeId = this.nextEdgeId(faceId, prevEdgeId, curEdgeId, edgeInfoArray)) > 0
            && (nextEdgeId != startEdgeId));

        return count;
    }

    protected int nextEdgeId(int faceId, int prevEdgeId, int curEdgeId, VPFPrimitiveData.PrimitiveInfo[] edgeInfoArray)
    {
        // The next edge depends on which side of this edge (left or right) the face belongs to. If the face is on
        // the left side of this edge, we travel to the left edge, and visa versa. However if this is an auxiliary
        // edge (face is both left and right of the edge), then travel to the next edge which does not cause us to
        // backtrack.

        Orientation o = this.getOrientation(faceId, curEdgeId, edgeInfoArray);
        if (o == null)
        {
            return -1;
        }

        switch (o)
        {
            case LEFT:
                return getEdgeInfo(edgeInfoArray, curEdgeId).getLeftEdge();
            case RIGHT:
                return getEdgeInfo(edgeInfoArray, curEdgeId).getRightEdge();
            case LEFT_AND_RIGHT:
                return (prevEdgeId > 0) ? this.auxiliaryNextEdgeId(prevEdgeId, curEdgeId, edgeInfoArray) : -1;
            default:
                return -1;
        }
    }

    protected Orientation getOrientation(int faceId, int edgeId, VPFPrimitiveData.PrimitiveInfo[] edgeInfo)
    {
        VPFPrimitiveData.EdgeInfo thisInfo = getEdgeInfo(edgeInfo, edgeId);
        boolean matchLeft = thisInfo.getLeftFace() == faceId;
        boolean matchRight = thisInfo.getRightFace() == faceId;

        if (matchLeft && matchRight) // Auxiliary edge has the same face on both sides.
        {
            return Orientation.LEFT_AND_RIGHT;
        }
        else if (matchLeft)
        {
            return Orientation.LEFT;
        }
        else if (matchRight)
        {
            return Orientation.RIGHT;
        }

        return null;
    }

    protected boolean getMustReverseCoordinates(int faceId, int prevEdgeId, int curEdgeId,
        VPFPrimitiveData.PrimitiveInfo[] edgeInfo)
    {
        // Determine whether or not this edge's coordinate array must be reversed to provide a consistent ordering
        // of ring coordinates. There are two cases which cause the coordinates to need reversal:
        // 1. If the edge has left orientation, then we will travel backwards along this edge to arrive at the next
        // edge in the ring.
        // 2. If the edge is an auxiliary edge (a connecting edge between an inner and outer loop which), then
        // we *may* travel backwards along this edge.

        Orientation o = this.getOrientation(faceId, curEdgeId, edgeInfo);
        if (o == null)
        {
            return false;
        }

        switch (o)
        {
            case LEFT:
                return true;
            case RIGHT:
                return false;
            case LEFT_AND_RIGHT:
                return (prevEdgeId > 0) && this.auxiliaryMustReverseCoordinates(prevEdgeId, curEdgeId, edgeInfo);
            default:
                return false;
        }
    }

    protected int auxiliaryNextEdgeId(int prevEdgeId, int curEdgeId,
        VPFPrimitiveData.PrimitiveInfo[] edgeInfoArray)
    {
        // Note: an edge which has the same face for both its left and right faces is known to be an "auxiliary edge".
        // Using auxiliary edges is a solution to defining a face (polygon) with holes. The face becomes a single loop,
        // with an auxiliary edge joining each inner and outer loop. The auxiliary edge is traversed twice; one upon
        // entering the inner ring, then  again upon exit.

        VPFPrimitiveData.EdgeInfo prevInfo = getEdgeInfo(edgeInfoArray, prevEdgeId);
        VPFPrimitiveData.EdgeInfo curInfo = getEdgeInfo(edgeInfoArray, curEdgeId);

        // Previous edge is adjacent to starting node.
        if (curInfo.getStartNode() == prevInfo.getStartNode() || curInfo.getStartNode() == prevInfo.getEndNode())
        {
            return (curInfo.getRightEdge() != curEdgeId) ? curInfo.getRightEdge() : curInfo.getLeftEdge();
        }
        // Previous edge is adjacent to ending node.
        else if (curInfo.getEndNode() == prevInfo.getStartNode() || curInfo.getEndNode() == prevInfo.getEndNode())
        {
            return (curInfo.getLeftEdge() != curEdgeId) ? curInfo.getLeftEdge() : curInfo.getRightEdge();
        }
        // Edges are not actually adjacent. This should never happen, but we check anyway.
        else
        {
            return -1;
        }
    }

    protected boolean auxiliaryMustReverseCoordinates(int prevEdgeId, int curEdgeId,
        VPFPrimitiveData.PrimitiveInfo[] edgeInfoArray)
    {
        VPFPrimitiveData.EdgeInfo prevInfo = getEdgeInfo(edgeInfoArray, prevEdgeId);
        VPFPrimitiveData.EdgeInfo curInfo = getEdgeInfo(edgeInfoArray, curEdgeId);

        return curInfo.getEndNode() == prevInfo.getStartNode() || curInfo.getEndNode() == prevInfo.getEndNode();
    }

    protected static VPFPrimitiveData.EdgeInfo getEdgeInfo(VPFPrimitiveData.PrimitiveInfo[] edgeInfo, int id)
    {
        return (VPFPrimitiveData.EdgeInfo) edgeInfo[VPFBufferedRecordData.indexFromId(id)];
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy