o: ActiveSupport::Cache::Entry	:@compressedF:@expires_in0:@created_atf1372843461.5556428:@value"x�{I"
class:EFI"BundledAsset;�FI"logical_path;�FI"Core/PolygonPipeline.js;�TI"
pathname;�FI"T/Users/bwrona/www/engines/cesium/app/assets/javascripts/Core/PolygonPipeline.js;�TI"content_type;�FI"application/javascript;�FI"
mtime;�FI"2013-07-03T11:02:49+02:00;�FI"length;�Fi��I"digest;�F"%9797666446f9d91951701d8f01d84c9fI"source;�FI"��/*global define*/

define([
        './DeveloperError',
        './Math',
        './Cartesian2',
        './Cartesian3',
        './Ellipsoid',
        './EllipsoidTangentPlane',
        './defaultValue',
        './pointInsideTriangle2D',
        './ComponentDatatype',
        './PrimitiveType',
        './Queue',
        './WindingOrder'
    ], function(
        DeveloperError,
        CesiumMath,
        Cartesian2,
        Cartesian3,
        Ellipsoid,
        EllipsoidTangentPlane,
        defaultValue,
        pointInsideTriangle2D,
        ComponentDatatype,
        PrimitiveType,
        Queue,
        WindingOrder) {
    "use strict";

    function DoublyLinkedList() {
        this.head = undefined;
        this.tail = undefined;
        this.length = 0;
    }

    DoublyLinkedList.prototype.add = function(item) {
        if (typeof item !== 'undefined') {
            var node = {
                item : item,
                previous : this.tail,
                next : undefined
            };

            if (typeof this.tail !== 'undefined') {
                this.tail.next = node;
                this.tail = node;
            } else {
                // Insert into empty list.
                this.head = node;
                this.tail = node;
            }

            ++this.length;
        }
    };

    DoublyLinkedList.prototype.remove = function(item) {
        if (typeof item !== 'undefined') {
            if (typeof item.previous !== 'undefined' && typeof item.next  !== 'undefined') {
                item.previous.next = item.next;
                item.next.previous = item.previous;
            } else if (typeof item.previous  !== 'undefined') {
                // Remove last node.
                item.previous.next = undefined;
                this.tail = item.previous;
            } else if (typeof item.next !== 'undefined') {
                // Remove first node.
                item.next.previous = undefined;
                this.head = item.next;
            } else {
                // Remove last node in linked list.
                this.head = undefined;
                this.tail = undefined;
            }

            --this.length;
        }
    };

    function isTipConvex(p0, p1, p2) {
        var u = p1.subtract(p0);
        var v = p2.subtract(p1);

        // Use the sign of the z component of the cross product
        return ((u.x * v.y) - (u.y * v.x)) >= 0.0;
    }

    /**
     * Returns the index of the vertex with the maximum X value.
     *
     * @param {Array} positions An array of the Cartesian points defining the polygon's vertices.
     * @returns {Number} The index of the positions with the maximum X value.
     *
     * @private
     */
    function getRightmostPositionIndex(positions) {
        var maximumX = positions[0].x;
        var rightmostPositionIndex = 0;
        for ( var i = 0; i < positions.length; i++) {
            if (positions[i].x > maximumX) {
                maximumX = positions[i].x;
                rightmostPositionIndex = i;
            }
        }
        return rightmostPositionIndex;
    }

    /**
     * Returns the index of the ring that contains the rightmost vertex.
     *
     * @param {Array} rings An array of arrays of Cartesians. Each array contains the vertices defining a polygon.
     * @returns {Number} The index of the ring containing the rightmost vertex.
     *
     * @private
     */
    function getRightmostRingIndex(rings) {
        var rightmostX = rings[0][0].x;
        var rightmostRingIndex = 0;
        for ( var ring = 0; ring < rings.length; ring++) {
            var maximumX = rings[ring][getRightmostPositionIndex(rings[ring])].x;
            if (maximumX > rightmostX) {
                rightmostX = maximumX;
                rightmostRingIndex = ring;
            }
        }

        return rightmostRingIndex;
    }

    /**
     * Returns a list containing the reflex vertices for a given polygon.
     *
     * @param {Array} polygon An array of Cartesian elements defining the polygon.
     * @returns {Array}
     *
     * @private
     */
    function getReflexVertices(polygon) {
        var reflexVertices = [];
        for ( var i = 0; i < polygon.length; i++) {
            var p0 = polygon[((i - 1) + polygon.length) % polygon.length];
            var p1 = polygon[i];
            var p2 = polygon[(i + 1) % polygon.length];

            if (!isTipConvex(p0, p1, p2)) {
                reflexVertices.push(p1);
            }
        }
        return reflexVertices;
    }

    /**
     * Returns true if the given point is contained in the list of positions.
     *
     * @param {Array} positions A list of Cartesian elements defining a polygon.
     * @param {Cartesian} point The point to check.
     * @returns {Boolean} <code>true></code> if <code>point</code> is found in <code>polygon</code>, <code>false</code> otherwise.
     *
     * @private
     */
    function isVertex(positions, point) {
        for ( var i = 0; i < positions.length; i++) {
            if (point.equals(positions[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * Given a point inside a polygon, find the nearest point directly to the right that lies on one of the polygon's edges.
     *
     * @param {Cartesian} point A point inside the polygon defined by <code>ring</code>.
     * @param {Array} ring A list of Cartesian points defining a polygon.
     * @param {Array} [edgeIndices]  An array containing the indices two endpoints of the edge containing the intersection.
     *
     * @returns {Cartesian} The intersection point.
     * @private
     */
    function intersectPointWithRing(point, ring, edgeIndices) {
        edgeIndices = defaultValue(edgeIndices, []);

        var minDistance = Number.MAX_VALUE;
        var rightmostVertexIndex = getRightmostPositionIndex(ring);
        var intersection = new Cartesian3(ring[rightmostVertexIndex].x, point.y, 0.0);
        edgeIndices.push(rightmostVertexIndex);
        edgeIndices.push((rightmostVertexIndex + 1) % ring.length);

        var boundaryMinX = ring[0].x;
        var boundaryMaxX = boundaryMinX;
        for ( var i = 1; i < ring.length; ++i) {
            if (ring[i].x < boundaryMinX) {
                boundaryMinX = ring[i].x;
            } else if (ring[i].x > boundaryMaxX) {
                boundaryMaxX = ring[i].x;
            }
        }
        boundaryMaxX += (boundaryMaxX - boundaryMinX);
        var point2 = new Cartesian3(boundaryMaxX, point.y, 0.0);

        // Find the nearest intersection.
        for (i = 0; i < ring.length; i++) {
            var v1 = ring[i];
            var v2 = ring[(i + 1) % ring.length];

            if (((v1.x >= point.x) || (v2.x >= point.x)) && (((v1.y >= point.y) && (v2.y <= point.y)) || ((v1.y <= point.y) && (v2.y >= point.y)))) {
                var temp = ((v2.y - v1.y) * (point2.x - point.x)) - ((v2.x - v1.x) * (point2.y - point.y));
                if (temp !== 0.0) {
                    temp = 1.0 / temp;
                    var ua = (((v2.x - v1.x) * (point.y - v1.y)) - ((v2.y - v1.y) * (point.x - v1.x))) * temp;
                    var ub = (((point2.x - point.x) * (point.y - v1.y)) - ((point2.y - point.y) * (point.x - v1.x))) * temp;
                    if ((ua >= 0.0) && (ua <= 1.0) && (ub >= 0.0) && (ub <= 1.0)) {
                        var tempIntersection = new Cartesian2(point.x + ua * (point2.x - point.x), point.y + ua * (point2.y - point.y));
                        var dist = tempIntersection.subtract(point);
                        temp = dist.magnitudeSquared();
                        if (temp < minDistance) {
                            intersection = tempIntersection;
                            minDistance = temp;
                            edgeIndices[0] = i;
                            edgeIndices[1] = (i + 1) % ring.length;
                        }
                    }
                }
            }
        }

        return intersection;
    }

    /**
     * Given an outer ring and multiple inner rings, determine the point on the outer ring that is visible
     * to the rightmost vertex of the rightmost inner ring.
     *
     * @param {Array} outerRing An array of Cartesian points defining the outer boundary of the polygon.
     * @param {Array} innerRings An array of arrays of Cartesian points, where each array represents a hole in the polygon.
     * @returns {Number} The index of the vertex in <code>outerRing</code> that is mutually visible to the rightmost vertex in <code>inenrRing</code>.
     *
     * @private
     */
    function getMutuallyVisibleVertexIndex(outerRing, innerRings) {
        var innerRingIndex = getRightmostRingIndex(innerRings);
        var innerRing = innerRings[innerRingIndex];
        var innerRingVertexIndex = getRightmostPositionIndex(innerRing);
        var innerRingVertex = innerRing[innerRingVertexIndex];
        var edgeIndices = [];
        var intersection = intersectPointWithRing(innerRingVertex, outerRing, edgeIndices);

        var visibleVertex;
        if (isVertex(outerRing, intersection)) {
            visibleVertex = intersection;
        } else {
            // Set P to be the edge endpoint closest to the inner ring vertex
            var d1 = (outerRing[edgeIndices[0]].subtract(innerRingVertex)).magnitudeSquared();
            var d2 = (outerRing[edgeIndices[1]].subtract(innerRingVertex)).magnitudeSquared();
            var p = (d1 < d2) ? outerRing[edgeIndices[0]] : outerRing[edgeIndices[1]];

            var reflexVertices = getReflexVertices(outerRing);
            var reflexIndex = reflexVertices.indexOf(p);
            if (reflexIndex !== -1) {
                reflexVertices.splice(reflexIndex, 1); // Do not include p if it happens to be reflex.
            }

            var pointsInside = [];
            for ( var i = 0; i < reflexVertices.length; i++) {
                var vertex = reflexVertices[i];
                if (pointInsideTriangle2D(vertex, innerRingVertex, intersection, p)) {
                    pointsInside.push(vertex);
                }
            }

            // If all reflexive vertices are outside the triangle formed by points
            // innerRingVertex, intersection and P, then P is the visible vertex.
            // Otherwise, return the reflex vertex that minimizes the angle between <1,0> and <k, reflex>.
            var minAngle = Number.MAX_VALUE;
            if (pointsInside.length > 0) {
                var v1 = new Cartesian2(1.0, 0.0, 0.0);
                for (i = 0; i < pointsInside.length; i++) {
                    var v2 = pointsInside[i].subtract(innerRingVertex);
                    var denominator = v1.magnitude() * v2.magnitude();
                    if (denominator !== 0) {
                        var angle = Math.abs(Math.acos(v1.dot(v2) / denominator));
                        if (angle < minAngle) {
                            minAngle = angle;
                            p = pointsInside[i];
                        }
                    }
                }
            }
            visibleVertex = p;
        }

        return outerRing.indexOf(visibleVertex);
    }

    /**
     * Given a polygon defined by an outer ring with one or more inner rings (holes), return a single list of points representing
     * a polygon with the rightmost hole added to it. The added hole is removed from <code>innerRings</code>.
     *
     * @param {Array} outerRing An array of Cartesian points defining the outer boundary of the polygon.
     * @param {Array} innerRings An array of arrays of Cartesian points, where each array represents a hole in the polygon.
     *
     * @return A single list of Cartesian points defining the polygon, including the eliminated inner ring.
     *
     * @private
     */
    function eliminateHole(outerRing, innerRings, ellipsoid) {
        // Check that the holes are defined in the winding order opposite that of the outer ring.
        var windingOrder = PolygonPipeline.computeWindingOrder2D(outerRing);
        for ( var i = 0; i < innerRings.length; i++) {
            var ring = innerRings[i];

            // Ensure each hole's first and last points are the same.
            if (!(ring[0]).equals(ring[ring.length - 1])) {
                ring.push(ring[0]);
            }

            var innerWindingOrder = PolygonPipeline.computeWindingOrder2D(ring);
            if (innerWindingOrder === windingOrder) {
                ring.reverse();
            }
        }

        // Project points onto a tangent plane to find the mutually visible vertex.
        var tangentPlane = EllipsoidTangentPlane.fromPoints(outerRing, ellipsoid);
        var tangentOuterRing = tangentPlane.projectPointsOntoPlane(outerRing);
        var tangentInnerRings = [];
        for (i = 0; i < innerRings.length; i++) {
            tangentInnerRings.push(tangentPlane.projectPointsOntoPlane(innerRings[i]));
        }

        var visibleVertexIndex = getMutuallyVisibleVertexIndex(tangentOuterRing, tangentInnerRings);
        var innerRingIndex = getRightmostRingIndex(tangentInnerRings);
        var innerRingVertexIndex = getRightmostPositionIndex(tangentInnerRings[innerRingIndex]);

        var innerRing = innerRings[innerRingIndex];
        var newPolygonVertices = [];

        for (i = 0; i < outerRing.length; i++) {
            newPolygonVertices.push(outerRing[i]);
        }

        var j;
        var holeVerticesToAdd = [];

        // If the rightmost inner vertex is not the starting and ending point of the ring,
        // then some other point is duplicated in the inner ring and should be skipped once.
        if (innerRingVertexIndex !== 0) {
            for (j = 0; j <= innerRing.length; j++) {
                var index = (j + innerRingVertexIndex) % innerRing.length;
                if (index !== 0) {
                    holeVerticesToAdd.push(innerRing[index]);
                }
            }
        } else {
            for (j = 0; j < innerRing.length; j++) {
                holeVerticesToAdd.push(innerRing[(j + innerRingVertexIndex) % innerRing.length]);
            }
        }

        var lastVisibleVertexIndex = newPolygonVertices.lastIndexOf(outerRing[visibleVertexIndex]);

        holeVerticesToAdd.push(outerRing[lastVisibleVertexIndex]);

        var front = newPolygonVertices.slice(0, lastVisibleVertexIndex + 1);
        var back = newPolygonVertices.slice(lastVisibleVertexIndex + 1);
        newPolygonVertices = front.concat(holeVerticesToAdd, back);

        innerRings.splice(innerRingIndex, 1);

        return newPolygonVertices;
    }

    var c3 = new Cartesian3();
    function getXZIntersectionOffsetPoints(p, p1, u1, v1) {
        p.add(p1.subtract(p, c3).multiplyByScalar(p.y/(p.y-p1.y), c3), u1);
        Cartesian3.clone(u1, v1);
        offsetPointFromXZPlane(u1, true);
        offsetPointFromXZPlane(v1, false);
    }

    function offsetPointFromXZPlane(p, isBehind) {
        if (Math.abs(p.y) < CesiumMath.EPSILON11){
            if (isBehind) {
                p.y = -CesiumMath.EPSILON11;
            } else {
                p.y = CesiumMath.EPSILON11;
            }
        }
    }

    var scaleToGeodeticHeightN = new Cartesian3();
    var scaleToGeodeticHeightP = new Cartesian3();

    /**
     * DOC_TBA
     *
     * @exports PolygonPipeline
     */
    var PolygonPipeline = {
        /**
         * DOC_TBA
         *
         * Cleans up a simple polygon by removing duplicate adjacent positions and making
         * the first position not equal the last position.
         *
         * @exception {DeveloperError} positions is required.
         * @exception {DeveloperError} At least three positions are required.
         */
        cleanUp : function(positions) {
            if (typeof positions  === 'undefined') {
                throw new DeveloperError('positions is required.');
            }

            var length = positions.length;
            if (length < 3) {
                throw new DeveloperError('At least three positions are required.');
            }

            var cleanedPositions = [];

            for ( var i0 = length - 1, i1 = 0; i1 < length; i0 = i1++) {
                var v0 = positions[i0];
                var v1 = positions[i1];

                if (!v0.equals(v1)) {
                    cleanedPositions.push(v1); // Shallow copy!
                }
            }

            return cleanedPositions;
        },

        /**
         * DOC_TBA
         *
         * @exception {DeveloperError} positions is required.
         * @exception {DeveloperError} At least three positions are required.
         */
        computeArea2D : function(positions) {
            if (typeof positions  === 'undefined') {
                throw new DeveloperError('positions is required.');
            }

            var length = positions.length;
            if (length < 3) {
                throw new DeveloperError('At least three positions are required.');
            }

            var area = 0.0;

            for ( var i0 = length - 1, i1 = 0; i1 < length; i0 = i1++) {
                var v0 = positions[i0];
                var v1 = positions[i1];

                area += (v0.x * v1.y) - (v1.x * v0.y);
            }

            return area * 0.5;
        },

        /**
         * DOC_TBA
         *
         * @return {WindingOrder} DOC_TBA
         *
         * @exception {DeveloperError} positions is required.
         * @exception {DeveloperError} At least three positions are required.
         */
        computeWindingOrder2D : function(positions) {
            var area = PolygonPipeline.computeArea2D(positions);
            return (area >= 0.0) ? WindingOrder.COUNTER_CLOCKWISE : WindingOrder.CLOCKWISE;
        },

        /**
         * DOC_TBA
         *
         * @exception {DeveloperError} positions is required.
         * @exception {DeveloperError} At least three positions are required.
         */
        earClip2D : function(positions) {
            // PERFORMANCE_IDEA:  This is slow at n^3.  Make it faster with:
            //   * http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
            //   * http://cgm.cs.mcgill.ca/~godfried/publications/triangulation.held.ps.gz
            //   * http://blogs.agi.com/insight3d/index.php/2008/03/20/triangulation-rhymes-with-strangulation/

            if (typeof positions  === 'undefined') {
                throw new DeveloperError('positions is required.');
            }

            var length = positions.length;
            if (length < 3) {
                throw new DeveloperError('At least three positions are required.');
            }

            var remainingPositions = new DoublyLinkedList();

            for ( var i = 0; i < length; ++i) {
                remainingPositions.add({
                    position : positions[i],
                    index : i
                });
            }

            var indices = [];

            var previousNode = remainingPositions.head;
            var node = previousNode.next;
            var nextNode = node.next;

            var bailCount = length * length;

            while (remainingPositions.length > 3) {
                var p0 = previousNode.item.position;
                var p1 = node.item.position;
                var p2 = nextNode.item.position;

                if (isTipConvex(p0, p1, p2)) {
                    var isEar = true;

                    for ( var n = (nextNode.next ? nextNode.next : remainingPositions.head); n !== previousNode; n = (n.next ? n.next : remainingPositions.head)) {
                        if (pointInsideTriangle2D(n.item.position, p0, p1, p2)) {
                            isEar = false;
                            break;
                        }
                    }

                    if (isEar) {
                        indices.push(previousNode.item.index);
                        indices.push(node.item.index);
                        indices.push(nextNode.item.index);

                        remainingPositions.remove(node);

                        node = nextNode;
                        nextNode = nextNode.next ? nextNode.next : remainingPositions.head;
                        continue;
                    }
                }

                previousNode = previousNode.next ? previousNode.next : remainingPositions.head;
                node = node.next ? node.next : remainingPositions.head;
                nextNode = nextNode.next ? nextNode.next : remainingPositions.head;

                if (--bailCount === 0) {
                    break;
                }
            }

            var n0 = remainingPositions.head;
            var n1 = n0.next;
            var n2 = n1.next;
            indices.push(n0.item.index);
            indices.push(n1.item.index);
            indices.push(n2.item.index);

            return indices;
        },

        /**
         * Subdivides a {@link Polygon} such that no triangles cross the &plusmn;180 degree meridian of an ellipsoid.
         * @memberof PolygonPipeline
         *
         * @param {Array} positions The Cartesian positions of triangles that make up a polygon.
         * @param {Array} indices The indices of positions in the positions array that make up triangles
         *
         * @returns {Object} The full set of indices, including those for positions added for newly created triangles
         *
         * @exception {DeveloperError} positions and indices are required
         * @exception {DeveloperError} At least three indices are required.
         * @exception {DeveloperError} The number of indices must be divisable by three.
         *
         * @see Polygon
         *
         * @example
         * var positions = [new Cartesian3(-1, -1, 0), new Cartesian3(-1, 1, 2), new Cartesian3(-1, 2, 2)];
         * var indices = [0, 1, 2];
         * indices = PolygonPipeline.wrapLongitude(positions, indices);
         */

        wrapLongitude : function(positions, indices) {
            if ((typeof positions === 'undefined') ||
                (typeof indices === 'undefined')) {
                throw new DeveloperError('positions and indices are required.');
            }

            if (indices.length < 3) {
                throw new DeveloperError('At least three indices are required.');
            }

            if (indices.length % 3 !== 0) {
                throw new DeveloperError('The number of indices must be divisable by three.');
            }

            var newIndices = [];

            var len = indices.length;
            for (var i = 0; i < len; i += 3) {
                var i0 = indices[i];
                var i1 = indices[i + 1];
                var i2 = indices[i + 2];
                var p0 = positions[i0];
                var p1 = positions[i1];
                var p2 = positions[i2];

                // In WGS84 coordinates, for a triangle approximately on the
                // ellipsoid to cross the IDL, first it needs to be on the
                // negative side of the plane x = 0.
                if ((p0.x < 0.0) && (p1.x < 0.0) && (p2.x < 0.0)) {
                    var p0Behind = p0.y < 0.0;
                    var p1Behind = p1.y < 0.0;
                    var p2Behind = p2.y < 0.0;

                    offsetPointFromXZPlane(p0, p0Behind);
                    offsetPointFromXZPlane(p1, p1Behind);
                    offsetPointFromXZPlane(p2, p2Behind);

                    var numBehind = 0;
                    numBehind += p0Behind ? 1 : 0;
                    numBehind += p1Behind ? 1 : 0;
                    numBehind += p2Behind ? 1 : 0;

                    var u1, u2, v1, v2;

                    if (numBehind === 1 || numBehind === 2) {
                        u1 = new Cartesian3();
                        u2 = new Cartesian3();
                        v1 = new Cartesian3();
                        v2 = new Cartesian3();
                    }
                    var iu1 = positions.length;
                    if (numBehind === 1) {
                        if (p0Behind) {
                            getXZIntersectionOffsetPoints(p0, p1, u1, v1);
                            getXZIntersectionOffsetPoints(p0, p2, u2, v2);
                            positions.push(u1, u2, v1, v2);
                            newIndices.push(i0, iu1, iu1+1, i1, i2, iu1+3, i1, iu1+3, iu1+2);
                        } else if (p1Behind) {
                            getXZIntersectionOffsetPoints(p1, p0, u1, v1);
                            getXZIntersectionOffsetPoints(p1, p2, u2, v2);
                            positions.push(u1, u2, v1, v2);
                            newIndices.push(i1, iu1, iu1+1, i2, i0, iu1+3, i2, iu1+3, iu1+2);
                        } else if (p2Behind) {
                            getXZIntersectionOffsetPoints(p2, p0, u1, v1);
                            getXZIntersectionOffsetPoints(p2, p1, u2, v2);
                            positions.push(u1, u2, v1, v2);
                            newIndices.push(i2, iu1, iu1+1, i0, i1, iu1+3, i0, iu1+3, iu1+2);
                        }
                    } else if (numBehind === 2) {
                        if (!p0Behind) {
                            getXZIntersectionOffsetPoints(p0, p1, u1, v1);
                            getXZIntersectionOffsetPoints(p0, p2, u2, v2);
                            positions.push(u1, u2, v1, v2);
                            newIndices.push(i1, i2, iu1+1, i1, iu1+1, iu1, i0, iu1+2, iu1+3);
                        } else if (!p1Behind) {
                            getXZIntersectionOffsetPoints(p1, p2, u1, v1);
                            getXZIntersectionOffsetPoints(p1, p0, u2, v2);
                            positions.push(u1, u2, v1, v2);
                            newIndices.push(i2, i0, iu1+1, i2, iu1+1, iu1, i1, iu1+2, iu1+3);
                        } else if (!p2Behind) {
                            getXZIntersectionOffsetPoints(p2, p0, u1, v1);
                            getXZIntersectionOffsetPoints(p2, p1, u2, v2);
                            positions.push(u1, u2, v1, v2);
                            newIndices.push(i0, i1, iu1+1, i0, iu1+1, iu1, i2, iu1+2, iu1+3);
                        }
                    } else {
                        newIndices.push(i0, i1, i2);
                    }
                } else {
                    newIndices.push(i0, i1, i2);
                }
            }
            return newIndices;
        },

        /**
         * DOC_TBA
         *
         * @param {DOC_TBA} positions DOC_TBA
         * @param {DOC_TBA} indices DOC_TBA
         * @param {Number} [granularity] DOC_TBA
         *
         * @exception {DeveloperError} positions is required.
         * @exception {DeveloperError} indices is required.
         * @exception {DeveloperError} At least three indices are required.
         * @exception {DeveloperError} The number of indices must be divisable by three.
         * @exception {DeveloperError} Granularity must be greater than zero.
         */
        computeSubdivision : function(positions, indices, granularity) {
            if (typeof positions === 'undefined') {
                throw new DeveloperError('positions is required.');
            }

            if (typeof indices === 'undefined') {
                throw new DeveloperError('indices is required.');
            }

            if (indices.length < 3) {
                throw new DeveloperError('At least three indices are required.');
            }

            if (indices.length % 3 !== 0) {
                throw new DeveloperError('The number of indices must be divisable by three.');
            }

            granularity = defaultValue(granularity, CesiumMath.toRadians(1.0));
            if (granularity <= 0.0) {
                throw new DeveloperError('granularity must be greater than zero.');
            }

            // Use a queue for triangles that need (or might need) to be subdivided.
            var triangles = new Queue();

            var indicesLength = indices.length;
            for ( var j = 0; j < indicesLength; j += 3) {
                triangles.enqueue({
                    i0 : indices[j],
                    i1 : indices[j + 1],
                    i2 : indices[j + 2]
                });
            }

            // New positions due to edge splits are appended to the positions list.
            var subdividedPositions = positions.slice(0); // shadow copy!
            var subdividedIndices = [];

            // Used to make sure shared edges are not split more than once.
            var edges = {};

            var i;
            while (triangles.length > 0) {
                var triangle = triangles.dequeue();

                var v0 = subdividedPositions[triangle.i0];
                var v1 = subdividedPositions[triangle.i1];
                var v2 = subdividedPositions[triangle.i2];

                var g0 = v0.angleBetween(v1);
                var g1 = v1.angleBetween(v2);
                var g2 = v2.angleBetween(v0);

                var max = Math.max(g0, Math.max(g1, g2));
                var edge;

                if (max > granularity) {
                    if (g0 === max) {
                        edge = Math.min(triangle.i0, triangle.i1).toString() + ' ' + Math.max(triangle.i0, triangle.i1).toString();

                        i = edges[edge];
                        if (!i) {
                            subdividedPositions.push(v0.add(v1).multiplyByScalar(0.5));
                            i = subdividedPositions.length - 1;
                            edges[edge] = i;
                        }

                        triangles.enqueue({
                            i0 : triangle.i0,
                            i1 : i,
                            i2 : triangle.i2
                        });
                        triangles.enqueue({
                            i0 : i,
                            i1 : triangle.i1,
                            i2 : triangle.i2
                        });
                    } else if (g1 === max) {
                        edge = Math.min(triangle.i1, triangle.i2).toString() + ' ' + Math.max(triangle.i1, triangle.i2).toString();

                        i = edges[edge];
                        if (!i) {
                            subdividedPositions.push(v1.add(v2).multiplyByScalar(0.5));
                            i = subdividedPositions.length - 1;
                            edges[edge] = i;
                        }

                        triangles.enqueue({
                            i0 : triangle.i1,
                            i1 : i,
                            i2 : triangle.i0
                        });
                        triangles.enqueue({
                            i0 : i,
                            i1 : triangle.i2,
                            i2 : triangle.i0
                        });
                    } else if (g2 === max) {
                        edge = Math.min(triangle.i2, triangle.i0).toString() + ' ' + Math.max(triangle.i2, triangle.i0).toString();

                        i = edges[edge];
                        if (!i) {
                            subdividedPositions.push(v2.add(v0).multiplyByScalar(0.5));
                            i = subdividedPositions.length - 1;
                            edges[edge] = i;
                        }

                        triangles.enqueue({
                            i0 : triangle.i2,
                            i1 : i,
                            i2 : triangle.i1
                        });
                        triangles.enqueue({
                            i0 : i,
                            i1 : triangle.i0,
                            i2 : triangle.i1
                        });
                    }
                } else {
                    subdividedIndices.push(triangle.i0);
                    subdividedIndices.push(triangle.i1);
                    subdividedIndices.push(triangle.i2);
                }
            }

            // PERFORMANCE_IDEA Rather that waste time re-iterating the entire set of positions
            // here, all of the above code can be refactored to flatten as values are added
            // Removing the need for this for loop.
            var length = subdividedPositions.length;
            var flattenedPositions = new Array(length * 3);
            var q = 0;
            for (i = 0; i < length; i++) {
                var item = subdividedPositions[i];
                flattenedPositions[q++] = item.x;
                flattenedPositions[q++] = item.y;
                flattenedPositions[q++] = item.z;
            }

            return {
                attributes : {
                    position : {
                        componentDatatype : ComponentDatatype.FLOAT,
                        componentsPerAttribute : 3,
                        values : flattenedPositions
                    }
                },

                indexLists : [{
                    primitiveType : PrimitiveType.TRIANGLES,
                    values : subdividedIndices
                }]
            };
        },

        /**
         * DOC_TBA
         *
         * @exception {DeveloperError} ellipsoid is required.
         */
        scaleToGeodeticHeight : function(mesh, height, ellipsoid) {
            ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);

            var n = scaleToGeodeticHeightN;
            var p = scaleToGeodeticHeightP;

            height = defaultValue(height, 0.0);

            if (typeof mesh !== 'undefined' && typeof mesh.attributes !== 'undefined' && typeof mesh.attributes.position !== 'undefined') {
                var positions = mesh.attributes.position.values;
                var length = positions.length;

                for ( var i = 0; i < length; i += 3) {
                    p.x = positions[i];
                    p.y = positions[i + 1];
                    p.z = positions[i + 2];

                    ellipsoid.scaleToGeodeticSurface(p, p);
                    ellipsoid.geodeticSurfaceNormal(p, n);
                    Cartesian3.multiplyByScalar(n, height, n);
                    Cartesian3.add(p, n, p);

                    positions[i] = p.x;
                    positions[i + 1] = p.y;
                    positions[i + 2] = p.z;
                }
            }

            return mesh;
        },

        /**
         * Given a polygon defined by an outer ring with one or more inner rings (holes), return a single list of points representing
         * a polygon defined by the outer ring with the inner holes removed.
         *
         * @param {Array} outerRing An array of Cartesian points defining the outer boundary of the polygon.
         * @param {Array} innerRings An array of arrays of Cartesian points, where each array represents a hole in the polygon.
         *
         * @return A single list of Cartesian points defining the polygon, including the eliminated inner ring.
         *
         * @exception {DeveloperError} <code>outerRing</code> is required.
         * @exception {DeveloperError} <code>outerRing</code> must not be empty.
         * @exception {DeveloperError} <code>innerRings</code> is required.
         *
         * @example
         * // Simplifying a polygon with multiple holes.
         * outerRing = PolygonPipeline.eliminateHoles(outerRing, innerRings);
         * polygon.setPositions(outerRing);
         */
        eliminateHoles : function(outerRing, innerRings, ellipsoid) {
            if (typeof outerRing === 'undefined') {
                throw new DeveloperError('outerRing is required.');
            }
            if (outerRing.length === 0) {
                throw new DeveloperError('outerRing must not be empty.');
            }
            if (typeof innerRings === 'undefined') {
                throw new DeveloperError('innerRings is required.');
            }
            ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);

            var innerRingsCopy = [];
            for ( var i = 0; i < innerRings.length; i++) {
                var innerRing = [];
                for ( var j = 0; j < innerRings[i].length; j++) {
                    innerRing.push(Cartesian3.clone(innerRings[i][j]));
                }
                innerRingsCopy.push(innerRing);
            }

            var newPolygonVertices = outerRing;
            while (innerRingsCopy.length > 0) {
                newPolygonVertices = eliminateHole(newPolygonVertices, innerRingsCopy, ellipsoid);
            }
            return newPolygonVertices;
        }
    };

    return PolygonPipeline;
});
;�FI"required_assets_digest;�F"%cf35868ee51d17198b72ad1bd245a8eaI"
_version;�F"%6776f581a4329e299531e1d52aa59832