Browse Source

Reduce number of object allocations in H3#h3ToGeoBoundary (#91586)

Ignacio Vera 2 years ago
parent
commit
ff9af20b0f

+ 5 - 0
docs/changelog/91586.yaml

@@ -0,0 +1,5 @@
+pr: 91586
+summary: Reduce number of object allocations in H3#h3ToGeoBoundary
+area: Geo
+type: enhancement
+issues: []

+ 153 - 213
libs/h3/src/main/java/org/elasticsearch/h3/FaceIJK.java

@@ -84,6 +84,15 @@ final class FaceIJK {
         11529602  // res 16
     };
 
+    private static final Vec2d[][] maxDimByCIIVec2d = new Vec2d[maxDimByCIIres.length][3];
+    static {
+        for (int i = 0; i < maxDimByCIIres.length; i++) {
+            maxDimByCIIVec2d[i][0] = new Vec2d(3.0 * maxDimByCIIres[i], 0.0);
+            maxDimByCIIVec2d[i][1] = new Vec2d(-1.5 * maxDimByCIIres[i], 3.0 * Constants.M_SQRT3_2 * maxDimByCIIres[i]);
+            maxDimByCIIVec2d[i][2] = new Vec2d(-1.5 * maxDimByCIIres[i], -3.0 * Constants.M_SQRT3_2 * maxDimByCIIres[i]);
+        }
+    }
+
     /**
      * unit scale distance table
      */
@@ -305,6 +314,32 @@ final class FaceIJK {
             new FaceOrientIJK(14, 0, 2, 2, 3)   // jk quadrant
         } };
 
+    // 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
+    private static final int[][] VERTEX_CLASSIII = new int[][] {
+        { 5, 4, 0 },  // 0
+        { 1, 5, 0 },  // 1
+        { 0, 5, 4 },  // 2
+        { 0, 1, 5 },  // 3
+        { 4, 0, 5 },  // 4
+        { 5, 0, 1 }   // 5
+    };
+
+    // 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
+    private static final int[][] VERTEX_CLASSII = new int[][] {
+        { 2, 1, 0 },  // 0
+        { 1, 2, 0 },  // 1
+        { 0, 2, 1 },  // 2
+        { 0, 1, 2 },  // 3
+        { 1, 0, 2 },  // 4
+        { 2, 0, 1 }   // 5
+    };
+
     int face;        // face number
     CoordIJK coord;  // ijk coordinates on that face
 
@@ -399,80 +434,87 @@ final class FaceIJK {
      * @param length The number of topological vertexes to return.
      */
     public CellBoundary faceIjkPentToCellBoundary(int res, int start, int length) {
-        FaceIJK[] fijkVerts = new FaceIJK[Constants.NUM_PENT_VERTS];
-        int adjRes = faceIjkPentToVerts(res, fijkVerts);
-
+        // adjust the center point to be in an aperture 33r substrate grid
+        // these should be composed for speed
+        this.coord.downAp3();
+        this.coord.downAp3r();
+        // if res is Class III we need to add a cw aperture 7 to get to
+        // icosahedral Class II
+        int adjRes = res;
+        if (H3Index.isResolutionClassIII(res)) {
+            this.coord.downAp7r();
+            adjRes += 1;
+        }
         // If we're returning the entire loop, we need one more iteration in case
         // of a distortion vertex on the last edge
-        int additionalIteration = length == Constants.NUM_PENT_VERTS ? 1 : 0;
-
+        final int additionalIteration = length == Constants.NUM_PENT_VERTS ? 1 : 0;
+        final boolean isResolutionClassIII = H3Index.isResolutionClassIII(res);
         // convert each vertex to lat/lng
         // adjust the face of each vertex as appropriate and introduce
         // edge-crossing vertices as needed
-        CellBoundary boundary = new CellBoundary();
-        FaceIJK lastFijk = null;
+        final CellBoundary boundary = new CellBoundary();
+        final CoordIJK scratch = new CoordIJK(0, 0, 0);
+        final FaceIJK fijk = new FaceIJK(this.face, scratch);
+        final int[][] coord = isResolutionClassIII ? VERTEX_CLASSIII : VERTEX_CLASSII;
+        final CoordIJK lastCoord = new CoordIJK(0, 0, 0);
+        int lastFace = this.face;
         for (int vert = start; vert < start + length + additionalIteration; vert++) {
-            int v = vert % Constants.NUM_PENT_VERTS;
-
-            FaceIJK fijk = fijkVerts[v];
+            final int v = vert % Constants.NUM_PENT_VERTS;
+            // The center point is now in the same substrate grid as the origin
+            // cell vertices. Add the center point substate coordinates
+            // to each vertex to translate the vertices to that cell.
+            scratch.reset(coord[v][0], coord[v][1], coord[v][2]);
+            scratch.ijkAdd(this.coord.i, this.coord.j, this.coord.k);
+            scratch.ijkNormalize();
+            fijk.face = this.face;
 
             fijk.adjustPentVertOverage(adjRes);
 
             // all Class III pentagon edges cross icosa edges
             // note that Class II pentagons have vertices on the edge,
             // not edge intersections
-            if (H3Index.isResolutionClassIII(res) && vert > start) {
+            if (isResolutionClassIII && vert > start) {
                 // find hex2d of the two vertexes on the last face
-                FaceIJK tmpFijk = new FaceIJK(fijk.face, new CoordIJK(fijk.coord.i, fijk.coord.j, fijk.coord.k));
-
-                Vec2d orig2d0 = lastFijk.coord.ijkToHex2d();
-
-                int currentToLastDir = adjacentFaceDir[tmpFijk.face][lastFijk.face];
-
-                FaceOrientIJK fijkOrient = faceNeighbors[tmpFijk.face][currentToLastDir];
+                final Vec2d orig2d0 = lastCoord.ijkToHex2d();
 
-                tmpFijk.face = fijkOrient.face;
-                CoordIJK ijk = tmpFijk.coord;
+                final int currentToLastDir = adjacentFaceDir[fijk.face][lastFace];
+                final FaceOrientIJK fijkOrient = faceNeighbors[fijk.face][currentToLastDir];
 
+                lastCoord.reset(fijk.coord.i, fijk.coord.j, fijk.coord.k);
                 // rotate and translate for adjacent face
                 for (int i = 0; i < fijkOrient.ccwRot60; i++) {
-                    ijk.ijkRotate60ccw();
+                    lastCoord.ijkRotate60ccw();
                 }
 
-                int unitScale = unitScaleByCIIres[adjRes] * 3;
-                ijk.ijkAdd(fijkOrient.translateI * unitScale, fijkOrient.translateJ * unitScale, fijkOrient.translateK * unitScale);
-                ijk.ijkNormalize();
+                final int unitScale = unitScaleByCIIres[adjRes] * 3;
+                lastCoord.ijkAdd(fijkOrient.translateI * unitScale, fijkOrient.translateJ * unitScale, fijkOrient.translateK * unitScale);
+                lastCoord.ijkNormalize();
 
-                Vec2d orig2d1 = ijk.ijkToHex2d();
+                final Vec2d orig2d1 = lastCoord.ijkToHex2d();
 
                 // find the appropriate icosa face edge vertexes
-                int maxDim = maxDimByCIIres[adjRes];
-                Vec2d v0 = new Vec2d(3.0 * maxDim, 0.0);
-                Vec2d v1 = new Vec2d(-1.5 * maxDim, 3.0 * Constants.M_SQRT3_2 * maxDim);
-                Vec2d v2 = new Vec2d(-1.5 * maxDim, -3.0 * Constants.M_SQRT3_2 * maxDim);
-
-                Vec2d edge0;
-                Vec2d edge1;
-                switch (adjacentFaceDir[tmpFijk.face][fijk.face]) {
-                    case IJ:
-                        edge0 = v0;
-                        edge1 = v1;
-                        break;
-                    case JK:
-                        edge0 = v1;
-                        edge1 = v2;
-                        break;
-                    case KI:
-                    default:
-                        assert (adjacentFaceDir[tmpFijk.face][fijk.face] == KI);
-                        edge0 = v2;
-                        edge1 = v0;
-                        break;
+                final Vec2d edge0;
+                final Vec2d edge1;
+                switch (adjacentFaceDir[fijkOrient.face][fijk.face]) {
+                    case IJ -> {
+                        edge0 = maxDimByCIIVec2d[adjRes][0];
+                        edge1 = maxDimByCIIVec2d[adjRes][1];
+                    }
+                    case JK -> {
+                        edge0 = maxDimByCIIVec2d[adjRes][1];
+                        edge1 = maxDimByCIIVec2d[adjRes][2];
+                    }
+                    // case KI:
+                    default -> {
+                        assert (adjacentFaceDir[fijkOrient.face][fijk.face] == KI);
+                        edge0 = maxDimByCIIVec2d[adjRes][2];
+                        edge1 = maxDimByCIIVec2d[adjRes][0];
+                    }
                 }
 
                 // find the intersection and add the lat/lng point to the result
-                Vec2d inter = Vec2d.v2dIntersect(orig2d0, orig2d1, edge0, edge1);
-                LatLng point = inter.hex2dToGeo(tmpFijk.face, adjRes, true);
+                final Vec2d inter = Vec2d.v2dIntersect(orig2d0, orig2d1, edge0, edge1);
+                final LatLng point = inter.hex2dToGeo(fijkOrient.face, adjRes, true);
                 boundary.add(point);
             }
 
@@ -480,12 +522,12 @@ final class FaceIJK {
             // vert == start + NUM_PENT_VERTS is only used to test for possible
             // intersection on last edge
             if (vert < start + Constants.NUM_PENT_VERTS) {
-                Vec2d vec = fijk.coord.ijkToHex2d();
-                LatLng point = vec.hex2dToGeo(fijk.face, adjRes, true);
+                final Vec2d vec = fijk.coord.ijkToHex2d();
+                final LatLng point = vec.hex2dToGeo(fijk.face, adjRes, true);
                 boundary.add(point);
             }
-
-            lastFijk = fijk;
+            lastFace = fijk.face;
+            lastCoord.reset(fijk.coord.i, fijk.coord.j, fijk.coord.k);
         }
         return boundary;
     }
@@ -498,27 +540,42 @@ final class FaceIJK {
      * @param start  The first topological vertex to return.
      * @param length The number of topological vertexes to return.
      */
-    public CellBoundary faceIjkToCellBoundary(int res, int start, int length) {
-        FaceIJK fijkVerts[] = new FaceIJK[Constants.NUM_HEX_VERTS];
-        int adjRes = faceIjkToVerts(res, fijkVerts);
+    public CellBoundary faceIjkToCellBoundary(final int res, final int start, final int length) {
+        // adjust the center point to be in an aperture 33r substrate grid
+        // these should be composed for speed
+        this.coord.downAp3();
+        this.coord.downAp3r();
+
+        // if res is Class III we need to add a cw aperture 7 to get to
+        // icosahedral Class II
+        int adjRes = res;
+        if (H3Index.isResolutionClassIII(res)) {
+            this.coord.downAp7r();
+            adjRes += 1;
+        }
+
         // If we're returning the entire loop, we need one more iteration in case
         // of a distortion vertex on the last edge
-        int additionalIteration = length == Constants.NUM_HEX_VERTS ? 1 : 0;
-
+        final int additionalIteration = length == Constants.NUM_HEX_VERTS ? 1 : 0;
+        final boolean isResolutionClassIII = H3Index.isResolutionClassIII(res);
         // convert each vertex to lat/lng
         // adjust the face of each vertex as appropriate and introduce
         // edge-crossing vertices as needed
-        CellBoundary boundary = new CellBoundary();
+        final CellBoundary boundary = new CellBoundary();
+        final CoordIJK scratch1 = new CoordIJK(0, 0, 0);
+        final FaceIJK fijk = new FaceIJK(this.face, scratch1);
+        final CoordIJK scratch2 = isResolutionClassIII ? new CoordIJK(0, 0, 0) : null;
+        final int[][] verts = isResolutionClassIII ? VERTEX_CLASSIII : VERTEX_CLASSII;
         int lastFace = -1;
         Overage lastOverage = Overage.NO_OVERAGE;
         for (int vert = start; vert < start + length + additionalIteration; vert++) {
             int v = vert % Constants.NUM_HEX_VERTS;
+            scratch1.reset(verts[v][0], verts[v][1], verts[v][2]);
+            scratch1.ijkAdd(this.coord.i, this.coord.j, this.coord.k);
+            scratch1.ijkNormalize();
+            fijk.face = this.face;
 
-            FaceIJK fijk = new FaceIJK(fijkVerts[v].face, new CoordIJK(fijkVerts[v].coord.i, fijkVerts[v].coord.j, fijkVerts[v].coord.k));
-
-            //
-            final boolean pentLeading4 = false; // may change in c code when calling method
-            Overage overage = fijk.adjustOverageClassII(adjRes, pentLeading4, true);
+            final Overage overage = fijk.adjustOverageClassII(adjRes, false, true);
 
             /*
             Check for edge-crossing. Each face of the underlying icosahedron is a
@@ -529,48 +586,51 @@ final class FaceIJK {
             projection. Note that Class II cell edges have vertices on the face
             edge, with no edge line intersections.
             */
-            if (H3Index.isResolutionClassIII(res) && vert > start && fijk.face != lastFace && lastOverage != Overage.FACE_EDGE) {
+            if (isResolutionClassIII && vert > start && fijk.face != lastFace && lastOverage != Overage.FACE_EDGE) {
                 // find hex2d of the two vertexes on original face
-                int lastV = (v + 5) % Constants.NUM_HEX_VERTS;
-                Vec2d orig2d0 = fijkVerts[lastV].coord.ijkToHex2d();
-                Vec2d orig2d1 = fijkVerts[v].coord.ijkToHex2d();
+                final int lastV = (v + 5) % Constants.NUM_HEX_VERTS;
+                // The center point is now in the same substrate grid as the origin
+                // cell vertices. Add the center point substate coordinates
+                // to each vertex to translate the vertices to that cell.
+                final int[] vertexLast = verts[lastV];
+                final int[] vertexV = verts[v];
+                scratch2.reset(vertexLast[0] + coord.i, vertexLast[1] + coord.j, vertexLast[2] + coord.k);
+                scratch2.ijkNormalize();
+                final Vec2d orig2d0 = scratch2.ijkToHex2d();
+                scratch2.reset(vertexV[0] + coord.i, vertexV[1] + coord.j, vertexV[2] + coord.k);
+                scratch2.ijkNormalize();
+                final Vec2d orig2d1 = scratch2.ijkToHex2d();
 
                 // find the appropriate icosa face edge vertexes
-                int maxDim = maxDimByCIIres[adjRes];
-                Vec2d v0 = new Vec2d(3.0 * maxDim, 0.0);
-                Vec2d v1 = new Vec2d(-1.5 * maxDim, 3.0 * Constants.M_SQRT3_2 * maxDim);
-                Vec2d v2 = new Vec2d(-1.5 * maxDim, -3.0 * Constants.M_SQRT3_2 * maxDim);
-
-                int face2 = ((lastFace == this.face) ? fijk.face : lastFace);
+                final int face2 = ((lastFace == this.face) ? fijk.face : lastFace);
                 final Vec2d edge0;
                 final Vec2d edge1;
                 switch (adjacentFaceDir[this.face][face2]) {
-                    case IJ:
-                        edge0 = v0;
-                        edge1 = v1;
-                        break;
-                    case JK:
-                        edge0 = v1;
-                        edge1 = v2;
-                        break;
+                    case IJ -> {
+                        edge0 = maxDimByCIIVec2d[adjRes][0];
+                        edge1 = maxDimByCIIVec2d[adjRes][1];
+                    }
+                    case JK -> {
+                        edge0 = maxDimByCIIVec2d[adjRes][1];
+                        edge1 = maxDimByCIIVec2d[adjRes][2];
+                    }
                     // case KI:
-                    default:
+                    default -> {
                         assert (adjacentFaceDir[this.face][face2] == KI);
-                        edge0 = v2;
-                        edge1 = v0;
-                        break;
+                        edge0 = maxDimByCIIVec2d[adjRes][2];
+                        edge1 = maxDimByCIIVec2d[adjRes][0];
+                    }
                 }
-
                 // find the intersection and add the lat/lng point to the result
-                Vec2d inter = Vec2d.v2dIntersect(orig2d0, orig2d1, edge0, edge1);
+                final Vec2d inter = Vec2d.v2dIntersect(orig2d0, orig2d1, edge0, edge1);
                 /*
                 If a point of intersection occurs at a hexagon vertex, then each
                 adjacent hexagon edge will lie completely on a single icosahedron
                 face, and no additional vertex is required.
                 */
-                boolean isIntersectionAtVertex = orig2d0.equals(inter) || orig2d1.equals(inter);
+                final boolean isIntersectionAtVertex = orig2d0.equals(inter) || orig2d1.equals(inter);
                 if (isIntersectionAtVertex == false) {
-                    LatLng point = inter.hex2dToGeo(this.face, adjRes, true);
+                    final LatLng point = inter.hex2dToGeo(this.face, adjRes, true);
                     boundary.add(point);
                 }
             }
@@ -579,8 +639,8 @@ final class FaceIJK {
             // vert == start + NUM_HEX_VERTS is only used to test for possible
             // intersection on last edge
             if (vert < start + Constants.NUM_HEX_VERTS) {
-                Vec2d vec = fijk.coord.ijkToHex2d();
-                LatLng point = vec.hex2dToGeo(fijk.face, adjRes, true);
+                final Vec2d vec = fijk.coord.ijkToHex2d();
+                final LatLng point = vec.hex2dToGeo(fijk.face, adjRes, true);
                 boundary.add(point);
             }
             lastFace = fijk.face;
@@ -677,125 +737,6 @@ final class FaceIJK {
         return h;
     }
 
-    /**
-     * Populate the vertices of this cell as substrate FaceIJK addresses.
-     *
-     * @param res The H3 resolution of the cell. This may be adjusted if
-     *            necessary for the substrate grid resolution.
-     */
-    private int faceIjkToVerts(int res, FaceIJK[] fijkVerts) {
-        // get the correct set of substrate vertices for this resolution
-        CoordIJK[] verts;
-        if (H3Index.isResolutionClassIII(res)) {
-            // 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
-            verts = new CoordIJK[] {
-                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
-            };
-        } else {
-            // 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
-            verts = new CoordIJK[] {
-                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
-            };
-        }
-
-        // adjust the center point to be in an aperture 33r substrate grid
-        // these should be composed for speed
-        this.coord.downAp3();
-        this.coord.downAp3r();
-
-        // if res is Class III we need to add a cw aperture 7 to get to
-        // icosahedral Class II
-        if (H3Index.isResolutionClassIII(res)) {
-            this.coord.downAp7r();
-            res += 1;
-        }
-
-        // The center point is now in the same substrate grid as the origin
-        // cell vertices. Add the center point substate coordinates
-        // to each vertex to translate the vertices to that cell.
-
-        for (int v = 0; v < Constants.NUM_HEX_VERTS; v++) {
-            verts[v].ijkAdd(this.coord.i, this.coord.j, this.coord.k);
-            verts[v].ijkNormalize();
-            fijkVerts[v] = new FaceIJK(this.face, verts[v]);
-        }
-        return res;
-    }
-
-    /**
-     * Populate the vertices of this pentagon cell as substrate FaceIJK addresses
-     *
-     * @param res The H3 resolution of the cell. This may be adjusted if
-     *            necessary for the substrate grid resolution.
-     */
-    private int faceIjkPentToVerts(int res, FaceIJK[] fijkVerts) {
-        // get the correct set of substrate vertices for this resolution
-        CoordIJK[] verts;
-        if (H3Index.isResolutionClassIII(res)) {
-            // the vertexes of an origin-centered pentagon 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
-            verts = new CoordIJK[] {
-                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
-            };
-        } else {
-            // the vertexes of an origin-centered pentagon 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
-            verts = new CoordIJK[] {
-                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
-            };
-        }
-
-        // adjust the center point to be in an aperture 33r substrate grid
-        // these should be composed for speed
-        this.coord.downAp3();
-        this.coord.downAp3r();
-
-        // if res is Class III we need to add a cw aperture 7 to get to
-        // icosahedral Class II
-        if (H3Index.isResolutionClassIII(res)) {
-            this.coord.downAp7r();
-            res += 1;
-        }
-
-        // The center point is now in the same substrate grid as the origin
-        // cell vertices. Add the center point substate coordinates
-        // to each vertex to translate the vertices to that cell.
-        for (int v = 0; v < Constants.NUM_PENT_VERTS; v++) {
-            verts[v].ijkAdd(this.coord.i, this.coord.j, this.coord.k);
-            verts[v].ijkNormalize();
-            fijkVerts[v] = new FaceIJK(this.face, verts[v]);
-        }
-        return res;
-    }
-
     /**
      * Adjusts a FaceIJK address for a pentagon vertex in a substrate grid in
      * place so that the resulting cell address is relative to the correct
@@ -803,11 +744,10 @@ final class FaceIJK {
      *
      * @param res The H3 resolution of the cell.
      */
-    private Overage adjustPentVertOverage(int res) {
+    private void adjustPentVertOverage(int res) {
         Overage overage;
         do {
             overage = adjustOverageClassII(res, false, true);
         } while (overage == Overage.NEW_FACE);
-        return overage;
     }
 }

+ 4 - 3
libs/h3/src/main/java/org/elasticsearch/h3/H3Index.java

@@ -206,8 +206,9 @@ final class H3Index {
         }
         // if we're here we have the potential for an "overage"; i.e., it is
         // possible that c lies on an adjacent face
-
-        CoordIJK origIJK = new CoordIJK(fijk.coord.i, fijk.coord.j, fijk.coord.k);
+        int origI = fijk.coord.i;
+        int origJ = fijk.coord.j;
+        int origK = fijk.coord.k;
 
         // if we're in Class III, drop into the next finer Class II grid
         int res = H3Index.H3_get_resolution(h3);
@@ -234,7 +235,7 @@ final class H3Index {
                 fijk.coord.upAp7r();
             }
         } else if (res != H3Index.H3_get_resolution(h3)) {
-            fijk.coord = origIJK;
+            fijk.coord.reset(origI, origJ, origK);
         }
         return fijk;
     }

+ 11 - 5
libs/h3/src/main/java/org/elasticsearch/h3/Vec2d.java

@@ -146,11 +146,12 @@ final class Vec2d {
 
         // adjust theta for Class III
         // if a substrate grid, then it's already been adjusted for Class III
-        if (substrate == false && H3Index.isResolutionClassIII(res)) theta = posAngleRads(theta + Constants.M_AP7_ROT_RADS);
+        if (substrate == false && H3Index.isResolutionClassIII(res)) {
+            theta = posAngleRads(theta + Constants.M_AP7_ROT_RADS);
+        }
 
         // find theta as an azimuth
         theta = posAngleRads(faceAxesAzRadsCII[face][0] - theta);
-
         // now find the point at (r,theta) from the face center
         return geoAzDistanceRads(faceCenterGeo[face], theta, r);
     }
@@ -353,7 +354,11 @@ final class Vec2d {
                 lon = constrainLng(p1.getLonRad());
             }
         } else { // not due north or south
-            sinlat = Math.sin(p1.getLatRad()) * Math.cos(distance) + Math.cos(p1.getLatRad()) * Math.sin(distance) * Math.cos(az);
+            final double sinDistance = Math.sin(distance);
+            final double cosDistance = Math.cos(distance);
+            final double sinP1Lat = Math.sin(p1.getLatRad());
+            final double cosP1Lat = Math.cos(p1.getLatRad());
+            sinlat = sinP1Lat * cosDistance + cosP1Lat * sinDistance * Math.cos(az);
             if (sinlat > 1.0) {
                 sinlat = 1.0;
             }
@@ -370,8 +375,9 @@ final class Vec2d {
                 lat = -M_PI_2;
                 lon = 0.0;
             } else {
-                sinlng = Math.sin(az) * Math.sin(distance) / Math.cos(lat);
-                coslng = (Math.cos(distance) - Math.sin(p1.getLatRad()) * Math.sin(lat)) / Math.cos(p1.getLatRad()) / Math.cos(lat);
+                final double cosLat = Math.cos(lat);
+                sinlng = Math.sin(az) * sinDistance / cosLat;
+                coslng = (cosDistance - sinP1Lat * Math.sin(lat)) / cosP1Lat / cosLat;
                 if (sinlng > 1.0) {
                     sinlng = 1.0;
                 }