Example #1
0
        /// <summary>
        /// Get the vertices of a cell as substrate FaceIJK addresses
        /// </summary>
        /// <param name="fijk">The FaceIJK address of the cell.</param>
        /// <param name="res">
        /// The H3 resolution of the cell. This may be adjusted if
        /// necessary for the substrate grid resolution.
        /// </param>
        /// <param name="fijkVerts">array for the vertices</param>
        /// <returns>
        /// Tuple
        /// Item1 Possibly modified fijk
        /// Item2 Possibly modified res
        /// Item3 Array for vertices
        /// </returns>
        /// <!--
        /// faceijk.c
        /// void _faceIjkToVerts
        /// -->
        public static (FaceIjk, int, IList <FaceIjk>) ToVerts(this FaceIjk fijk, int res, IList <FaceIjk> fijkVerts)
        {
            // the vertexes of an origin-centered cell in a Class II resolution on a
            // substrate grid with aperture sequence 33r. The aperture 3 gets us the
            // vertices, and the 3r gets us back to Class II.
            // vertices listed ccw from the i-axes
            CoordIjk[] vertsCii =
            {
                new CoordIjk(2, 1, 0), // 0
                new CoordIjk(1, 2, 0), // 1
                new CoordIjk(0, 2, 1), // 2
                new CoordIjk(0, 1, 2), // 3
                new CoordIjk(1, 0, 2), // 4
                new CoordIjk(2, 0, 1)  // 5
            };

            // the vertexes of an origin-centered cell in a Class III resolution on a
            // substrate grid with aperture sequence 33r7r. The aperture 3 gets us the
            // vertices, and the 3r7r gets us to Class II.
            // vertices listed ccw from the i-axes
            CoordIjk[] vertsCiii =
            {
                new CoordIjk(5, 4, 0), // 0
                new CoordIjk(1, 5, 0), // 1
                new CoordIjk(0, 5, 4), // 2
                new CoordIjk(0, 1, 5), // 3
                new CoordIjk(4, 0, 5), // 4
                new CoordIjk(5, 0, 1)  // 5
            };

            // get the correct set of substrate vertices for this resolution
            var verts = res.IsResClassIii()
                            ? vertsCiii
                            : vertsCii;

            // adjust the center point to be in an aperture 33r substrate grid
            // these should be composed for speed
            fijk = fijk.ReplaceCoord(fijk.Coord.DownAp3().DownAp3R());

            // if res is Class III we need to add a cw aperture 7 to get to
            // icosahedral Class II
            if (res.IsResClassIii())
            {
                fijk = fijk.ReplaceCoord(fijk.Coord.DownAp7R());
                res++;
            }

            // The center point is now in the same substrate grid as the origin
            // cell vertices. Add the center point substrate coordinates
            // to each vertex to translate the vertices to that cell.
            for (int v = 0; v < Constants.H3.NUM_HEX_VERTS; v++)
            {
                fijkVerts[v] = fijkVerts[v]
                               .ReplaceFace(fijk.Face)
                               .ReplaceCoord((fijk.Coord + verts[v]).Normalized());
            }

            return(fijk, res, fijkVerts);
        }
Example #2
0
        /// <summary>
        /// Adjusts a FaceIJK address in place so that the resulting cell address is
        /// relative to the correct icosahedral face.
        /// </summary>
        /// <param name="fijk">The FaceIJK address of the cell.</param>
        /// <param name="res">The H3 resolution of the cell.</param>
        /// <param name="pentLeading4">Whether or not the cell is a pentagon with a leading figit 4</param>
        /// <param name="substrate">Whether or not the cell is in a substrate grid.</param>
        /// <returns>
        /// Tuple
        /// Item1: <see cref="Overage"/>
        /// Item2: Adjusted <see cref="FaceIjk"/>
        /// </returns>
        /// <!--
        /// faceijk.c
        /// Overage _adjustOverageClassII
        /// -->
        public static (Overage, FaceIjk) AdjustOverageClassIi(
            this FaceIjk fijk, int res, int pentLeading4, int substrate
            )
        {
            Overage overage = Overage.NO_OVERAGE;

            var ijk = fijk.Coord;

            // get the maximum dimension value; scale if a substrate grid
            int maxDim = Constants.FaceIjk.MaxDimByCiiRes[res];

            if (substrate != 0)
            {
                maxDim *= 3;
            }

            // check for overage
            if (substrate != 0 && ijk.Sum() == maxDim) // on edge
            {
                overage = Overage.FACE_EDGE;
            }
            else if (ijk.Sum() > maxDim)  // overage
            {
                overage = Overage.NEW_FACE;

                FaceOrientIjk fijkOrient;
                if (ijk.K > 0)
                {
                    if (ijk.J > 0) // jk "quadrant"
                    {
                        fijkOrient = Constants.FaceIjk.FaceNeighbors[fijk.Face, Constants.FaceIjk.JK];
                    }
                    else // ik "quadrant"
                    {
                        fijkOrient = Constants.FaceIjk.FaceNeighbors[fijk.Face, Constants.FaceIjk.KI];
                        // adjust for the pentagonal missing sequence
                        if (pentLeading4 != 0)
                        {
                            // translate origin to center of pentagon
                            var origin = new CoordIjk(maxDim, 0, 0);
                            var tmp    = ijk - origin;
                            // rotate to adjust for the missing sequence
                            tmp = tmp.Rotate60Clockwise();
                            // translate the origin back to the center of the triangle
                            ijk = tmp + origin;
                        }
                    }
                }
                else // ij "quadrant"
                {
                    fijkOrient = Constants.FaceIjk.FaceNeighbors[fijk.Face, Constants.FaceIjk.IJ];
                }

                fijk = fijk.ReplaceFace(fijkOrient.Face);

                // rotate and translate for adjacent face
                for (int i = 0; i < fijkOrient.Ccw60Rotations; i++)
                {
                    ijk = ijk.Rotate60CounterClockwise();
                }

                var transVec  = fijkOrient.Translate;
                int unitScale = Constants.FaceIjk.UnitScaleByCiiRes[res];
                if (substrate != 0)
                {
                    unitScale *= 3;
                }

                transVec *= unitScale;
                ijk      += transVec;
                ijk       = ijk.Normalized();

                // overage points on pentagon boundaries can end up on edges
                if (substrate != 0 && ijk.Sum() == maxDim) // on edge
                {
                    overage = Overage.FACE_EDGE;
                }
            }

            fijk = fijk.ReplaceCoord(ijk);
            return(overage, fijk);
        }
Example #3
0
        /// <summary>
        /// Convert an FaceIJK address to the corresponding H3Index.
        /// </summary>
        /// <param name="fijk">The FaceIJK address.</param>
        /// <param name="res">The cell resolution.</param>
        /// <returns>The encoded H3Index (or H3_NULL on failure).</returns>
        /// <!--
        /// h3index.c
        /// H3Index _faceIjkToH3
        /// -->
        public static H3Index ToH3(this FaceIjk fijk, int res)
        {
            // initialize the index
            H3Index h = Constants.H3Index.H3_INIT;

            h = h.SetMode(H3Mode.Hexagon).SetResolution(res);

            // check for res 0/base cell
            if (res == 0)
            {
                if (fijk.Coord.I > Constants.BaseCells.MaxFaceCoord ||
                    fijk.Coord.J > Constants.BaseCells.MaxFaceCoord ||
                    fijk.Coord.K > Constants.BaseCells.MaxFaceCoord)
                {
                    // out of range input
                    return(Constants.H3Index.H3_INVALID_INDEX);
                }

                return(h.SetBaseCell(fijk.ToBaseCell()));
            }

            // we need to find the correct base cell FaceIJK for this H3 index;
            // start with the passed in face and resolution res ijk coordinates
            // in that face's coordinate system
            var fijkBc = new FaceIjk(fijk);

            // build the H3Index from finest res up
            // adjust r for the fact that the res 0 base cell offsets the indexing
            // digits
            var ijk = new CoordIjk(fijkBc.Coord);

            for (int r = res - 1; r >= 0; r--)
            {
                var      lastIjk = new CoordIjk(ijk);
                CoordIjk lastCenter;
                if ((r + 1).IsResClassIii())
                {
                    // rotate ccw
                    ijk        = ijk.UpAp7();
                    lastCenter = new CoordIjk(ijk).DownAp7();
                }
                else
                {
                    // rotate cw
                    ijk        = ijk.UpAp7R();
                    lastCenter = new CoordIjk(ijk).DownAp7R();
                }

                var diff = (lastIjk - lastCenter).Normalized();
                h = h.SetIndexDigit(r + 1, (ulong)diff.ToDirection());
            }

            fijkBc = fijkBc.ReplaceCoord(ijk);

            // fijkBC should now hold the IJK of the base cell in the
            // coordinate system of the current face
            if (fijkBc.Coord.I > Constants.BaseCells.MaxFaceCoord ||
                fijkBc.Coord.J > Constants.BaseCells.MaxFaceCoord ||
                fijkBc.Coord.K > Constants.BaseCells.MaxFaceCoord)
            {
                // out of range input
                return(Constants.H3Index.H3_INVALID_INDEX);
            }

            // lookup the correct base cell
            int baseCell = fijkBc.ToBaseCell();

            h = h.SetBaseCell(baseCell);

            // rotate if necessary to get canonical base cell orientation
            // for this base cell
            int numRots = fijkBc.ToBaseCellCounterClockwiseRotate60();

            if (baseCell.IsBaseCellPentagon())
            {
                // force rotation out of missing k-axes sub-sequence
                if (h.LeadingNonZeroDigit == Direction.K_AXES_DIGIT)
                {
                    // check for a cw/ccw offset face; default is ccw
                    h = baseCell.IsClockwiseOffset(fijkBc.Face)
                            ? h.Rotate60Clockwise()
                            : h.Rotate60CounterClockwise();
                }

                for (var i = 0; i < numRots; i++)
                {
                    h = h.RotatePent60CounterClockwise();
                }
            }
            else
            {
                for (int i = 0; i < numRots; i++)
                {
                    h = h.Rotate60CounterClockwise();
                }
            }

            return(h);
        }