/// <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); }
/// <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); }
/// <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); }