Ejemplo n.º 1
0
 public static H3Index GetUniDirectionalEdge(Code.H3Index origin, Code.H3Index destination)
 {
     return(new H3Index
     {
         Value = H3UniEdge.getH3UnidirectionalEdge(origin, destination).value
     });
 }
Ejemplo n.º 2
0
        public static HexRangeDistancesResult HexRangeDistances(Code.H3Index origin, int k)
        {
            int maxSize   = Algos.maxKringSize(k);
            var outHexes  = MakeEmpty(maxSize);
            var distances = new int[maxSize].ToList();

            var result = Algos.hexRangeDistances(origin, k, ref outHexes, ref distances);
            List <HexRangeMeasurement> combiner = new List <HexRangeMeasurement>();

            for (int i = 0; i < maxSize; i++)
            {
                combiner.Add
                (
                    new HexRangeMeasurement
                {
                    Index = new H3Index {
                        Value = outHexes[i].value
                    },
                    Distance = distances[i]
                }
                );
            }

            return(new HexRangeDistancesResult
            {
                Result = result,
                Values = combiner.ToArray()
            });
        }
Ejemplo n.º 3
0
 public static H3Index GetDestinationH3FromUniDirectionalEdge(Code.H3Index edge)
 {
     return(new H3Index
     {
         Value = H3UniEdge.getDestinationH3IndexFromUnidirectionalEdge(edge)
     });
 }
Ejemplo n.º 4
0
        public static string H3ToString(Code.H3Index h3)
        {
            string s = "";

            Code.H3Index.h3ToString(h3, ref s, 17);
            return(s);
        }
Ejemplo n.º 5
0
 public static H3Index GetOriginH3FromUniDirectionalEdge(Code.H3Index edge)
 {
     return(new H3Index
     {
         Value = H3UniEdge.getOriginH3IndexFromUnidirectionalEdge(edge).value
     });
 }
Ejemplo n.º 6
0
 public static H3Index H3ToParent(Code.H3Index h3, int parentRes)
 {
     return(new H3Index
     {
         Value = Code.H3Index.h3ToParent(h3, parentRes)
     });
 }
Ejemplo n.º 7
0
        public static HexRangeResult HexRanges(Code.H3Index h3Set, int k)
        {
            var tempList = new List <Code.H3Index> {
                h3Set
            };

            return(HexRanges(tempList, k));
        }
Ejemplo n.º 8
0
        public static Api.GeoCoord H3ToGeo(Code.H3Index h3)
        {
            var blank = new Code.GeoCoord();

            Code.H3Index.h3ToGeo(h3, ref blank);
            return(new GeoCoord {
                Latitude = blank.lat, Longitude = blank.lon
            });
        }
Ejemplo n.º 9
0
        public static H3Index[] H3ToChildren(Code.H3Index h3, int childRes)
        {
            var maxSize  = MaxH3ToChildrenSize(h3, childRes);
            var children = MakeEmpty(maxSize);

            Code.H3Index.h3ToChildren(h3, childRes, ref children);
            return(children.Select(v => new H3Index {
                Value = v.value
            }).ToArray());
        }
Ejemplo n.º 10
0
        public static H3Index[] GetH3IndexesFromUnidirectionalEdge(Code.H3Index edge)
        {
            List <Code.H3Index> cells = new List <Code.H3Index> {
                0, 0
            };

            H3UniEdge.getH3IndexesFromUnidirectionalEdge(edge, ref cells);
            return(cells.Select(v => new H3Index {
                Value = v.value
            }).ToArray());
        }
Ejemplo n.º 11
0
        public static H3Index[] Kring(Code.H3Index origin, int k)
        {
            int maxSize  = Algos.maxKringSize(k);
            var outHexes = MakeEmpty(maxSize);

            Algos.kRing(origin, k, ref outHexes);

            return(outHexes.Select(v => new H3Index {
                Value = v.value
            }).ToArray());
        }
Ejemplo n.º 12
0
        public static H3Index[] GetH3UniDirectionalEdgesFromHexagon(Code.H3Index origin)
        {
            var cells = MakeEmpty(6);

            H3UniEdge.getH3UnidirectionalEdgesFromHexagon(origin, cells);
            return(cells.Where(v => v != 0)
                   .Select(v => new H3Index {
                Value = v.value
            })
                   .ToArray());
        }
Ejemplo n.º 13
0
        public static GeoBoundary GetH3UnidirectionalEdgeBoundary(Code.H3Index edge)
        {
            Code.GeoBoundary gb = new Code.GeoBoundary();
            H3UniEdge.getH3UnidirectionalEdgeBoundary(edge, ref gb);
            var newVerts = gb.verts.Select(v => new GeoCoord(v)).ToArray();

            return(new GeoBoundary
            {
                VertexCount = gb.numVerts,
                Vertices = newVerts
            });
        }
Ejemplo n.º 14
0
        public static HexRangeResult experimentalLocalIjToH3(Code.H3Index origin, Code.LocalIJ.CoordIJ ij)
        {
            Code.H3Index h3     = new Code.H3Index(0);
            var          result = LocalIJ.experimentalLocalIjToH3(origin, ij, ref h3);

            return(new HexRangeResult
            {
                Result = result,
                Indexes = new H3Index[] { new H3Index {
                                              Value = h3.value
                                          } }
            });
        }
Ejemplo n.º 15
0
        public static HexRangeResult HexRange(Code.H3Index origin, int k)
        {
            var temp   = MakeEmpty(Algos.maxKringSize(k));
            var result = Algos.hexRange(origin, k, ref temp);

            return(new HexRangeResult
            {
                Result = result,
                Indexes = temp.Select(t => new Api.H3Index {
                    Value = t.value
                }).ToArray()
            });
        }
Ejemplo n.º 16
0
        public static ExperimentalIJ ExperimentalH3ToLocalIj(Code.H3Index origin, Code.H3Index h3)
        {
            LocalIJ.CoordIJ ij     = new LocalIJ.CoordIJ();
            int             result = LocalIJ.experimentalH3ToLocalIj(origin, h3, ij);

            return(new ExperimentalIJ
            {
                Result = result,
                IJ = new CoordIJ {
                    I = ij.i, J = ij.j
                }
            });
        }
Ejemplo n.º 17
0
        /// <summary>
        /// Returns whether or not an H3 index is valid.
        /// </summary>
        /// <param name="h">The H3 index to validate.</param>
        /// <returns>1 if the H3 index if valid, and 0 if it is not.</returns>
        /// <!-- Based off 3.1.1 -->
        public static int h3IsValid(H3Index h)
        {
            if (H3_GET_MODE(ref h) != Constants.H3_HEXAGON_MODE)
            {
                return(0);
            }

            int baseCell = H3_GET_BASE_CELL(h);

            if (baseCell < 0 || baseCell >= Constants.NUM_BASE_CELLS)
            {
                return(0);
            }

            int  res = H3_GET_RESOLUTION(h);
            bool foundFirstNonZeroDigit = false;

            if (res < 0 || res > Constants.MAX_H3_RES)
            {
                return(0);
            }

            for (int r = 1; r <= res; r++)
            {
                Direction digit = H3_GET_INDEX_DIGIT(h, r);
                if (!foundFirstNonZeroDigit && digit != Direction.CENTER_DIGIT)
                {
                    foundFirstNonZeroDigit = true;
                    if (BaseCells._isBaseCellPentagon(baseCell) && digit == Direction.K_AXES_DIGIT)
                    {
                        return(0);
                    }
                }

                if (digit < Direction.CENTER_DIGIT || digit >= Direction.NUM_DIGITS)
                {
                    return(0);
                }
            }

            for (int r = res + 1; r <= Constants.MAX_H3_RES; r++)
            {
                Direction digit = H3_GET_INDEX_DIGIT(h, r);
                if (digit != Direction.INVALID_DIGIT)
                {
                    return(0);
                }
            }

            return(1);
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Internal: Create a vertex graph from a set of hexagons. It is the
        /// responsibility of the caller to call destroyVertexGraph on the populated
        /// graph, otherwise the memory in the graph nodes will not be freed.
        /// </summary>
        ///
        /// <param name="h3Set">Set of hexagons</param>
        /// <param name="numHexes">Number of hexagons in the set</param>
        /// <param name="graph">Output graph</param>
        /// <!-- Based off 3.1.1 -->
        public static void h3SetToVertexGraph(ref List <H3Index> h3Set, int numHexes,
                                              ref VertexGraph graph)
        {
            GeoBoundary vertices   = new GeoBoundary();
            GeoCoord    fromVertex = new GeoCoord();
            GeoCoord    toVertex   = new GeoCoord();

            VertexGraph.VertexNode edge;
            if (numHexes < 1)
            {
                // We still need to init the graph, or calls to destroyVertexGraph will
                // fail
                graph = new VertexGraph(0, 0);
                return;
            }

            int       res        = H3Index.H3_GET_RESOLUTION(h3Set[0]);
            const int minBuckets = 6;
            // TODO: Better way to calculate/guess?
            int numBuckets = numHexes > minBuckets ? numHexes : minBuckets;

            graph = new VertexGraph(numBuckets, res);

            // Iterate through every hexagon
            for (int i = 0; i < numHexes; i++)
            {
                H3Index.h3ToGeoBoundary(h3Set[i], ref vertices);
                // iterate through every edge
                for (int j = 0; j < vertices.numVerts; j++)
                {
                    fromVertex = new GeoCoord(vertices.verts[j].lat, vertices.verts[j].lon);
                    //fromVtx = vertices.verts[j];
                    int idx = (j + 1) % vertices.numVerts;
                    toVertex = new GeoCoord(vertices.verts[idx].lat, vertices.verts[idx].lon);
                    //toVtx = vertices.verts[(j + 1) % vertices.numVerts];
                    // If we've seen this edge already, it will be reversed
                    edge = VertexGraph.findNodeForEdge(ref graph, toVertex, fromVertex);
                    if (edge != null)
                    {
                        // If we've seen it, drop it. No edge is shared by more than 2
                        // hexagons, so we'll never see it again.
                        VertexGraph.removeVertexNode(ref graph, ref edge);
                    }
                    else
                    {
                        // Add a new node for this edge
                        VertexGraph.addVertexNode(ref graph, fromVertex, toVertex);
                    }
                }
            }
        }
Ejemplo n.º 19
0
        public static Api.GeoBoundary H3ToGeoBoundary(Code.H3Index h3)
        {
            var blank = new Code.GeoBoundary();

            Code.H3Index.h3ToGeoBoundary(h3, ref blank);

            var newVerts = blank.verts.Select(v => new Api.GeoCoord(v)).ToArray();

            return(new GeoBoundary
            {
                VertexCount = blank.numVerts,
                Vertices = newVerts
            });
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Initializes an H3 index.
        /// </summary>
        /// <param name="hp"> The H3 index to initialize.</param>
        /// <param name="res"> The H3 resolution to initialize the index to.</param>
        /// <param name="baseCell"> The H3 base cell to initialize the index to.</param>
        /// <param name="initDigit"> The H3 digit (0-7) to initialize all of the index digits to.</param>
        /// <!-- Based off 3.1.1 -->
        public static void setH3Index(ref H3Index hp, int res, int baseCell, Direction initDigit)
        {
            H3Index h = H3_INIT;

            H3_SET_MODE(ref h, Constants.H3_HEXAGON_MODE);
            H3_SET_RESOLUTION(ref h, res);
            H3_SET_BASE_CELL(ref h, baseCell);
            for (int r = 1; r <= res; r++)
            {
                H3_SET_INDEX_DIGIT(ref h, r, (ulong)initDigit);
            }

            hp = h;
        }
Ejemplo n.º 21
0
        /// <summary>
        /// h3ToChildren takes the given hexagon id and generates all of the children
        /// at the specified resolution storing them into the provided memory pointer.
        /// It's assumed that maxH3ToChildrenSize was used to determine the allocation.
        /// </summary>
        /// <param name="h" H3Index to find the children of</param>
        /// <param name="childRes" int the child level to produce</param>
        /// <param name="children" H3Index* the memory to store the resulting addresses in</param>
        /// <!-- Based off 3.1.1 -->
        public static void h3ToChildren(H3Index h, int childRes, ref List <H3Index> children)
        {
            children = new List <H3Index>();
            int parentRes = H3_GET_RESOLUTION(h);

            if (parentRes > childRes)
            {
                return;
            }

            if (parentRes == childRes)
            {
                children.Add(h);
                return;
            }

            List <H3Index> current = new List <H3Index> {
                h
            };
            List <H3Index> realChildren = new List <H3Index>();
            int            goalRes      = childRes;
            int            currentRes   = parentRes;

            while (currentRes < goalRes)
            {
                realChildren.Clear();
                foreach (var index in current)
                {
                    int isPentagon = h3IsPentagon(index);
                    for (int m = 0; m < 7; m++)
                    {
                        if (isPentagon > 0 && m == (int)Direction.K_AXES_DIGIT)
                        {
                            realChildren.Add(H3_INVALID_INDEX);
                        }
                        else
                        {
                            var child = makeDirectChild(index, m);
                            realChildren.Add(child);
                        }
                    }
                }
                current = new List <H3Index>(realChildren.Where(c => c.value != 0));
                currentRes++;
            }

            children = new List <H3Index>(current);
        }
Ejemplo n.º 22
0
        /// <summary>
        /// k-rings produces indices within k distance of the origin index.
        ///
        /// k-ring 0 is defined as the origin index, k-ring 1 is defined as k-ring 0 and
        /// all neighboring indices, and so on.
        ///
        /// Output is placed in the provided array in no particular order. Elements of
        /// the output array may be left zero, as can happen when crossing a pentagon.
        /// </summary>
        /// <param name="origin">Origin location</param>
        /// <param name="k">k &gt;= 0</param>
        /// <param name="out_hex">Zero-filled array which must be of size <see cref="maxKringSize"/>(k)</param>
        /// <param name="distances">Zero-filled array which must be of size <see cref="maxKringSize"/>(k)</param>
        /// <!-- Based off 3.1.1 -->
        public static void kRingDistances(H3Index origin, int k, ref List <H3Index> out_hex, ref List <int> distances)
        {
            int maxIdx = maxKringSize(k);
            // Optimistically try the faster hexRange algorithm first
            bool failed = hexRangeDistances(origin, k, ref out_hex, ref distances) != 0;

            if (failed)
            {
                // Fast algo failed, fall back to slower, correct algo
                // and also wipe out array because contents untrustworthy
                distances.Clear();
                distances = new int[out_hex.Count].ToList();
                out_hex   = new ulong[distances.Count].Select(cell => new H3Index(cell)).ToList();
                _kRingInternal(origin, k, ref out_hex, ref distances, maxIdx, 0);
            }
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Determines the center point in spherical coordinates of a cell given by 2D
        /// hex coordinates on a particular icosahedral face.
        /// </summary>
        /// <param name="v">The 2D hex coordinates of the cell.</param>
        /// <param name="face">The icosahedral face upon which the 2D hex coordinate system is centered</param>
        /// <param name="res">The H3 resolution of the cell</param>
        /// <param name="substrate">Indicates whether or not this grid is actually a substrate
        /// grid relative to the specified resolution.</param>
        /// <param name="g">The spherical coordinates of the cell center point.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _hex2dToGeo(ref Vec2d v, int face, int res, int substrate, ref GeoCoord g)
        {
            // calculate (r, theta) in hex2d
            double r = Vec2d._v2dMag(v);

            if (r < Constants.EPSILON)
            {
                g = faceCenterGeo[face];
                return;
            }

            double theta = Math.Atan2(v.y, v.x);

            // scale for current resolution length u
            for (int i = 0; i < res; i++)
            {
                r /= M_SQRT7;
            }

            // scale accordingly if this is a substrate grid
            if (substrate > 0)
            {
                r /= 3.0;
                if (H3Index.isResClassIII(res))
                {
                    r /= M_SQRT7;
                }
            }

            r *= Constants.RES0_U_GNOMONIC;

            // perform inverse gnomonic scaling of r
            r = Math.Atan(r);

            // adjust theta for Class III
            // if a substrate grid, then it's already been adjusted for Class III
            if (substrate == 0 && H3Index.isResClassIII(res))
            {
                theta = GeoCoord._posAngleRads(theta + Constants.M_AP7_ROT_RADS);
            }

            // find theta as an azimuth
            theta = GeoCoord._posAngleRads(faceAxesAzRadsCII[face, 0] - theta);

            // now find the point at (r,theta) from the face center
            GeoCoord._geoAzDistanceRads(ref faceCenterGeo[face], theta, r, ref g);
        }
Ejemplo n.º 24
0
        /// <summary>
        /// Internal helper function called recursively for kRingDistances.
        ///
        /// Adds the origin index to the output set (treating it as a hash set)
        /// and recurses to its neighbors, if needed.
        /// </summary>
        /// <param name="origin">Origin index</param>
        /// <param name="k">Maximum distance to move from the origin.</param>
        /// <param name="outHex">Array treated as a hash set, elements being either H3Index or 0</param>
        /// <param name="distances">Scratch area, with elements paralleling the out array</param>
        /// <param name="maxIdx">Size of out and scratch arrays (must be <see cref="maxKringSize"/>(k))</param>
        /// <param name="curK">Current distance from the origin.</param>
        /// <remarks>Elements of distances indicate ijk distance from the origin index to the output index</remarks>
        /// <!-- Based off 3.1.1 -->
        internal static void _kRingInternal(H3Index origin, int k, ref List <H3Index> outHex, ref List <int> distances,
                                            int maxIdx, int curK)
        {
            if (origin == 0)
            {
                //Debug.WriteLine("Initial origin == 0");
                return;
            }

            // Put origin in the output array. out is used as a hash set.
            int off = (int)(origin.value % (ulong)maxIdx);

            while (outHex[off] != 0 && outHex[off] != origin)
            {
                off++;
                if (off >= maxIdx)
                {
                    off = 0;
                }
            }

            // We either got a free slot in the hash set or hit a duplicate
            // We might need to process the duplicate anyways because we got
            // here on a longer path before.
            if (outHex[off] == origin && distances[off] <= curK)
            {
                return;
            }

            outHex[off]    = origin;
            distances[off] = curK;

            // Base case: reached an index k away from the origin.
            if (curK >= k)
            {
                //Debug.WriteLine("Bailing on curK ({0}) >= k ({1})", curK, k);
                return;
            }

            // Recurse to all neighbors in no particular order.
            for (int i = 0; i < 6; i++)
            {
                int rotations = 0;
                var hNR       = h3NeighborRotations(origin, DIRECTIONS[i], ref rotations);
                _kRingInternal(hNR, k, ref outHex, ref distances, maxIdx, curK + 1);
            }
        }
Ejemplo n.º 25
0
        public static HexRangeResult HexRing(Code.H3Index origin, int k)
        {
            int maxSize = (k < 1)
                              ? 1
                              : k * 6;
            var outHexes = MakeEmpty(maxSize);

            var result = Algos.hexRing(origin, k, ref outHexes);

            return(new HexRangeResult
            {
                Result = result,
                Indexes = outHexes.Select(v => new H3Index {
                    Value = v.value
                }).ToArray()
            });
        }
Ejemplo n.º 26
0
        /// <summary>
        /// Returns the destination hexagon from the unidirectional edge H3Index
        /// </summary>
        /// <param name="edge">The edge H3 index</param>
        /// <returns>The destination H3 hexagon index</returns>
        /// <!-- Based off 3.1.1 -->
        public static H3Index getDestinationH3IndexFromUnidirectionalEdge(H3Index edge)
        {
            if (H3Index.H3_GET_MODE(ref edge) != Constants.H3_UNIEDGE_MODE)
            {
                return(H3Index.H3_INVALID_INDEX);
            }
            Direction direction   = (Direction)H3Index.H3_GET_RESERVED_BITS(edge);
            int       rotations   = 0;
            H3Index   destination = Algos
                                    .h3NeighborRotations
                                    (
                getOriginH3IndexFromUnidirectionalEdge(edge),
                direction,
                ref rotations
                                    );

            return(destination);
        }
Ejemplo n.º 27
0
        /// <summary>
        /// Produces the grid distance between the two indexes.
        ///
        /// This function may fail to find the distance between two indexes, for
        /// example if they are very far apart. It may also fail when finding
        /// distances for indexes on opposite sides of a pentagon.
        /// </summary>
        /// <param name="origin">Index to find the distance from</param>
        /// <param name="h3">Index to find the distance to</param>
        /// <returns>
        /// The distance, or a negative number if the library could not compute the distance
        /// </returns>
        /// <!-- Based off 3.2.0 -->
        public static int h3Distance(H3Index origin, H3Index h3)
        {
            CoordIJK originIjk = new CoordIJK();
            CoordIJK h3Ijk     = new CoordIJK();

            if (h3ToLocalIjk(origin, origin, ref originIjk) != 0)
            {
                // Currently there are no tests that would cause getting the coordinates
                // for an index the same as the origin to fail.
                return(-1);  // LCOV_EXCL_LINE
            }
            if (h3ToLocalIjk(origin, h3, ref h3Ijk) != 0)
            {
                return(-1);
            }

            return(CoordIJK.ijkDistance(originIjk, h3Ijk));
        }
Ejemplo n.º 28
0
        /// <summary>
        /// Produces ij coordinates for an index anchored by an origin.
        ///
        /// The coordinate space used by this function may have deleted
        /// regions or warping due to pentagonal distortion.
        ///
        /// Coordinates are only comparable if they come from the same
        /// origin index.
        ///
        /// Failure may occur if the index is too far away from the origin
        /// or if the index is on the other side of a pentagon.
        ///
        /// This function is experimental, and its output is not guaranteed
        /// to be compatible across different versions of H3.
        /// </summary>
        /// <param name="origin">An anchoring index for the ij coordinate system.</param>
        /// <param name="h3">Index to find the coordinates of</param>
        /// <param name="out_coord">ij coordinates of the index will be placed here on success</param>
        /// <returns>0 on success, or another value on failure.</returns>
        /// <!-- Based off 3.2.0 -->
        public static int experimentalH3ToLocalIj(H3Index origin, H3Index h3, CoordIJ out_coord)
        {
            // This function is currently experimental. Once ready to be part of the
            // non-experimental API, this function (with the experimental prefix) will
            // be marked as deprecated and to be removed in the next major version. It
            // will be replaced with a non-prefixed function name.
            CoordIJK ijk    = new CoordIJK();
            int      failed = h3ToLocalIjk(origin, h3, ref ijk);

            if (failed != 0)
            {
                return(failed);
            }

            CoordIJK.ijkToIj(ijk, ref out_coord);

            return(0);
        }
Ejemplo n.º 29
0
        /// <summary>
        /// Converts a string representation of an H3 index into an H3 index.
        /// </summary>
        /// <param name="str"> The string representation of an H3 index.</param>
        /// <returns>
        /// The H3 index corresponding to the string argument, or 0 if invalid.
        /// </returns>
        /// <!-- Based off 3.1.1 -->
        public static H3Index stringToH3(string str)
        {
            H3Index h = H3_INVALID_INDEX;

            // A small risk, but for the most part, we're dealing with hex numbers, so let's use that
            // as our default.
            if (ulong.TryParse(str, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out ulong ul1))
            {
                return(new H3Index(ul1));
            }
            // If failed, h will be unmodified and we should return 0 anyways.
            if (ulong.TryParse(str, out ulong ul2))
            {
                return(new H3Index(ul2));
            }

            return(0);
        }
Ejemplo n.º 30
0
        /// <summary>
        /// Provides the coordinates defining the unidirectional edge.
        /// </summary>
        /// <param name="edge">The unidirectional edge H3Index</param>
        /// <param name="gb">
        /// The geoboundary object to store the edge coordinates.
        /// </param>
        /// <!-- Based off 3.1.1 -->
        public static void getH3UnidirectionalEdgeBoundary(H3Index edge, ref GeoBoundary gb)
        {
            // TODO: More efficient solution :)
            GeoBoundary origin             = new GeoBoundary();
            GeoBoundary destination        = new GeoBoundary();
            GeoCoord    postponedVertex    = new GeoCoord();
            bool        hasPostponedVertex = false;

            H3Index.h3ToGeoBoundary(getOriginH3IndexFromUnidirectionalEdge(edge), ref origin);
            H3Index.h3ToGeoBoundary(getDestinationH3IndexFromUnidirectionalEdge(edge), ref destination);

            int k = 0;

            for (int i = 0; i < origin.numVerts; i++)
            {
                if (_hasMatchingVertex(origin.verts[i], destination))
                {
                    // If we are on vertex 0, we need to handle the case where it's the
                    // end of the edge, not the beginning.
                    if (i == 0 &&
                        !_hasMatchingVertex(origin.verts[i + 1], destination))
                    {
                        postponedVertex    = origin.verts[i];
                        hasPostponedVertex = true;
                    }
                    else
                    {
                        gb.verts[k] = origin.verts[i];
                        k++;
                    }
                }
            }

            // If we postponed adding the last vertex, add it now
            if (hasPostponedVertex)
            {
                gb.verts[k] = postponedVertex;
                k++;
            }
            gb.numVerts = k;
        }