@@ -417,51 +417,72 @@ public LatLng faceIjkToGeo(int res) {
417417 * for this FaceIJK address at a specified resolution.
418418 *
419419 * @param res The H3 resolution of the cell.
420- * @param start The first topological vertex to return.
421- * @param length The number of topological vertexes to return.
422420 */
423- public CellBoundary faceIjkPentToCellBoundary (int res , int start , int length ) {
421+ public CellBoundary faceIjkPentToCellBoundary (int res ) {
424422 // adjust the center point to be in an aperture 33r substrate grid
425423 // these should be composed for speed
426424 this .coord .downAp3 ();
427425 this .coord .downAp3r ();
428426 // if res is Class III we need to add a cw aperture 7 to get to
429427 // icosahedral Class II
430- int adjRes = res ;
431- if (H3Index .isResolutionClassIII (res )) {
432- this .coord .downAp7r ();
433- adjRes += 1 ;
434- }
428+ final int adjRes = adjustRes (this .coord , res );
429+
435430 // If we're returning the entire loop, we need one more iteration in case
436431 // of a distortion vertex on the last edge
437- final int additionalIteration = length == Constants .NUM_PENT_VERTS ? 1 : 0 ;
438- final boolean isResolutionClassIII = H3Index .isResolutionClassIII (res );
439- // convert each vertex to lat/lng
440- // adjust the face of each vertex as appropriate and introduce
441- // edge-crossing vertices as needed
432+ if (H3Index .isResolutionClassIII (res )) {
433+ return faceIjkPentToCellBoundaryClassIII (adjRes );
434+ } else {
435+ return faceIjkPentToCellBoundaryClassII (adjRes );
436+ }
437+ }
438+
439+ private CellBoundary faceIjkPentToCellBoundaryClassII (int adjRes ) {
440+ final LatLng [] points = new LatLng [Constants .NUM_PENT_VERTS ];
441+ final FaceIJK fijk = new FaceIJK (this .face , new CoordIJK (0 , 0 , 0 ));
442+ for (int vert = 0 ; vert < Constants .NUM_PENT_VERTS ; vert ++) {
443+ // The center point is now in the same substrate grid as the origin
444+ // cell vertices. Add the center point substate coordinates
445+ // to each vertex to translate the vertices to that cell.
446+ fijk .coord .reset (
447+ VERTEX_CLASSII [vert ][0 ] + this .coord .i ,
448+ VERTEX_CLASSII [vert ][1 ] + this .coord .j ,
449+ VERTEX_CLASSII [vert ][2 ] + this .coord .k
450+ );
451+ fijk .coord .ijkNormalize ();
452+ fijk .face = this .face ;
453+
454+ fijk .adjustPentVertOverage (adjRes );
455+
456+ points [vert ] = fijk .coord .ijkToGeo (fijk .face , adjRes , true );
457+ }
458+ return new CellBoundary (points , Constants .NUM_PENT_VERTS );
459+ }
460+
461+ private CellBoundary faceIjkPentToCellBoundaryClassIII (int adjRes ) {
442462 final LatLng [] points = new LatLng [CellBoundary .MAX_CELL_BNDRY_VERTS ];
443463 int numPoints = 0 ;
444- final CoordIJK scratch = new CoordIJK (0 , 0 , 0 );
445- final FaceIJK fijk = new FaceIJK (this .face , scratch );
446- final int [][] coord = isResolutionClassIII ? VERTEX_CLASSIII : VERTEX_CLASSII ;
464+ final FaceIJK fijk = new FaceIJK (this .face , new CoordIJK (0 , 0 , 0 ));
447465 final CoordIJK lastCoord = new CoordIJK (0 , 0 , 0 );
448466 int lastFace = this .face ;
449- for (int vert = start ; vert < start + length + additionalIteration ; vert ++) {
467+ for (int vert = 0 ; vert < Constants . NUM_PENT_VERTS + 1 ; vert ++) {
450468 final int v = vert % Constants .NUM_PENT_VERTS ;
451469 // The center point is now in the same substrate grid as the origin
452470 // cell vertices. Add the center point substate coordinates
453471 // to each vertex to translate the vertices to that cell.
454- scratch .reset (coord [v ][0 ], coord [v ][1 ], coord [v ][2 ]);
455- scratch .ijkAdd (this .coord .i , this .coord .j , this .coord .k );
456- scratch .ijkNormalize ();
472+ fijk .coord .reset (
473+ VERTEX_CLASSIII [v ][0 ] + this .coord .i ,
474+ VERTEX_CLASSIII [v ][1 ] + this .coord .j ,
475+ VERTEX_CLASSIII [v ][2 ] + this .coord .k
476+ );
477+ fijk .coord .ijkNormalize ();
457478 fijk .face = this .face ;
458479
459480 fijk .adjustPentVertOverage (adjRes );
460481
461482 // all Class III pentagon edges cross icosa edges
462483 // note that Class II pentagons have vertices on the edge,
463484 // not edge intersections
464- if (isResolutionClassIII && vert > start ) {
485+ if (vert > 0 ) {
465486 // find hex2d of the two vertexes on the last face
466487 final Vec2d orig2d0 = lastCoord .ijkToHex2d ();
467488
@@ -480,35 +501,17 @@ public CellBoundary faceIjkPentToCellBoundary(int res, int start, int length) {
480501
481502 final Vec2d orig2d1 = lastCoord .ijkToHex2d ();
482503
483- // find the appropriate icosa face edge vertexes
484- final Vec2d edge0 ;
485- final Vec2d edge1 ;
486- switch (adjacentFaceDir [fijkOrient .face ][fijk .face ]) {
487- case IJ -> {
488- edge0 = maxDimByCIIVec2d [adjRes ][0 ];
489- edge1 = maxDimByCIIVec2d [adjRes ][1 ];
490- }
491- case JK -> {
492- edge0 = maxDimByCIIVec2d [adjRes ][1 ];
493- edge1 = maxDimByCIIVec2d [adjRes ][2 ];
494- }
495- // case KI:
496- default -> {
497- assert (adjacentFaceDir [fijkOrient .face ][fijk .face ] == KI );
498- edge0 = maxDimByCIIVec2d [adjRes ][2 ];
499- edge1 = maxDimByCIIVec2d [adjRes ][0 ];
500- }
501- }
502-
503504 // find the intersection and add the lat/lng point to the result
504- final Vec2d inter = Vec2d .v2dIntersect (orig2d0 , orig2d1 , edge0 , edge1 );
505- points [numPoints ++] = inter .hex2dToGeo (fijkOrient .face , adjRes , true );
505+ final Vec2d inter = findIntersectionPoint (orig2d0 , orig2d1 , adjRes , adjacentFaceDir [fijkOrient .face ][fijk .face ]);
506+ if (inter != null ) {
507+ points [numPoints ++] = inter .hex2dToGeo (fijkOrient .face , adjRes , true );
508+ }
506509 }
507510
508511 // convert vertex to lat/lng and add to the result
509512 // vert == start + NUM_PENT_VERTS is only used to test for possible
510513 // intersection on last edge
511- if (vert < start + Constants .NUM_PENT_VERTS ) {
514+ if (vert < Constants .NUM_PENT_VERTS ) {
512515 points [numPoints ++] = fijk .coord .ijkToGeo (fijk .face , adjRes , true );
513516 }
514517 lastFace = fijk .face ;
@@ -522,43 +525,72 @@ public CellBoundary faceIjkPentToCellBoundary(int res, int start, int length) {
522525 * FaceIJK address at a specified resolution.
523526 *
524527 * @param res The H3 resolution of the cell.
525- * @param start The first topological vertex to return.
526- * @param length The number of topological vertexes to return.
527528 */
528- public CellBoundary faceIjkToCellBoundary (final int res , final int start , final int length ) {
529+ public CellBoundary faceIjkToCellBoundary (final int res ) {
529530 // adjust the center point to be in an aperture 33r substrate grid
530531 // these should be composed for speed
531532 this .coord .downAp3 ();
532533 this .coord .downAp3r ();
533534
534535 // if res is Class III we need to add a cw aperture 7 to get to
535536 // icosahedral Class II
536- int adjRes = res ;
537- if (H3Index .isResolutionClassIII (res )) {
538- this .coord .downAp7r ();
539- adjRes += 1 ;
540- }
537+ final int adjRes = adjustRes (this .coord , res );
541538
542- // If we're returning the entire loop, we need one more iteration in case
543- // of a distortion vertex on the last edge
544- final int additionalIteration = length == Constants .NUM_HEX_VERTS ? 1 : 0 ;
545- final boolean isResolutionClassIII = H3Index .isResolutionClassIII (res );
546539 // convert each vertex to lat/lng
547540 // adjust the face of each vertex as appropriate and introduce
548541 // edge-crossing vertices as needed
542+ if (H3Index .isResolutionClassIII (res )) {
543+ return faceIjkToCellBoundaryClassIII (adjRes );
544+ } else {
545+ return faceIjkToCellBoundaryClassII (adjRes );
546+ }
547+ }
548+
549+ private static int adjustRes (CoordIJK coord , int res ) {
550+ if (H3Index .isResolutionClassIII (res )) {
551+ coord .downAp7r ();
552+ res += 1 ;
553+ }
554+ return res ;
555+ }
556+
557+ private CellBoundary faceIjkToCellBoundaryClassII (int adjRes ) {
558+ final LatLng [] points = new LatLng [Constants .NUM_HEX_VERTS ];
559+ final FaceIJK fijk = new FaceIJK (this .face , new CoordIJK (0 , 0 , 0 ));
560+ for (int vert = 0 ; vert < Constants .NUM_HEX_VERTS ; vert ++) {
561+ fijk .coord .reset (
562+ VERTEX_CLASSII [vert ][0 ] + this .coord .i ,
563+ VERTEX_CLASSII [vert ][1 ] + this .coord .j ,
564+ VERTEX_CLASSII [vert ][2 ] + this .coord .k
565+ );
566+ fijk .coord .ijkNormalize ();
567+ fijk .face = this .face ;
568+
569+ fijk .adjustOverageClassII (adjRes , false , true );
570+
571+ // convert vertex to lat/lng and add to the result
572+ // vert == start + NUM_HEX_VERTS is only used to test for possible
573+ // intersection on last edge
574+ points [vert ] = fijk .coord .ijkToGeo (fijk .face , adjRes , true );
575+ }
576+ return new CellBoundary (points , Constants .NUM_HEX_VERTS );
577+ }
578+
579+ private CellBoundary faceIjkToCellBoundaryClassIII (int adjRes ) {
549580 final LatLng [] points = new LatLng [CellBoundary .MAX_CELL_BNDRY_VERTS ];
550581 int numPoints = 0 ;
551- final CoordIJK scratch1 = new CoordIJK (0 , 0 , 0 );
552- final FaceIJK fijk = new FaceIJK (this .face , scratch1 );
553- final CoordIJK scratch2 = isResolutionClassIII ? new CoordIJK (0 , 0 , 0 ) : null ;
554- final int [][] verts = isResolutionClassIII ? VERTEX_CLASSIII : VERTEX_CLASSII ;
582+ final FaceIJK fijk = new FaceIJK (this .face , new CoordIJK (0 , 0 , 0 ));
583+ final CoordIJK scratch = new CoordIJK (0 , 0 , 0 );
555584 int lastFace = -1 ;
556585 Overage lastOverage = Overage .NO_OVERAGE ;
557- for (int vert = start ; vert < start + length + additionalIteration ; vert ++) {
558- int v = vert % Constants .NUM_HEX_VERTS ;
559- scratch1 .reset (verts [v ][0 ], verts [v ][1 ], verts [v ][2 ]);
560- scratch1 .ijkAdd (this .coord .i , this .coord .j , this .coord .k );
561- scratch1 .ijkNormalize ();
586+ for (int vert = 0 ; vert < Constants .NUM_HEX_VERTS + 1 ; vert ++) {
587+ final int v = vert % Constants .NUM_HEX_VERTS ;
588+ fijk .coord .reset (
589+ VERTEX_CLASSIII [v ][0 ] + this .coord .i ,
590+ VERTEX_CLASSIII [v ][1 ] + this .coord .j ,
591+ VERTEX_CLASSIII [v ][2 ] + this .coord .k
592+ );
593+ fijk .coord .ijkNormalize ();
562594 fijk .face = this .face ;
563595
564596 final Overage overage = fijk .adjustOverageClassII (adjRes , false , true );
@@ -572,58 +604,28 @@ public CellBoundary faceIjkToCellBoundary(final int res, final int start, final
572604 projection. Note that Class II cell edges have vertices on the face
573605 edge, with no edge line intersections.
574606 */
575- if (isResolutionClassIII && vert > start && fijk .face != lastFace && lastOverage != Overage .FACE_EDGE ) {
607+ if (vert > 0 && fijk .face != lastFace && lastOverage != Overage .FACE_EDGE ) {
576608 // find hex2d of the two vertexes on original face
577609 final int lastV = (v + 5 ) % Constants .NUM_HEX_VERTS ;
578610 // The center point is now in the same substrate grid as the origin
579611 // cell vertices. Add the center point substate coordinates
580612 // to each vertex to translate the vertices to that cell.
581- final int [] vertexLast = verts [lastV ];
582- final int [] vertexV = verts [v ];
583- scratch2 .reset (vertexLast [0 ] + this .coord .i , vertexLast [1 ] + this .coord .j , vertexLast [2 ] + this .coord .k );
584- scratch2 .ijkNormalize ();
585- final Vec2d orig2d0 = scratch2 .ijkToHex2d ();
586- scratch2 .reset (vertexV [0 ] + this .coord .i , vertexV [1 ] + this .coord .j , vertexV [2 ] + this .coord .k );
587- scratch2 .ijkNormalize ();
588- final Vec2d orig2d1 = scratch2 .ijkToHex2d ();
613+ final Vec2d orig2d0 = orig (scratch , VERTEX_CLASSIII [lastV ]);
614+ final Vec2d orig2d1 = orig (scratch , VERTEX_CLASSIII [v ]);
589615
590616 // find the appropriate icosa face edge vertexes
591617 final int face2 = ((lastFace == this .face ) ? fijk .face : lastFace );
592- final Vec2d edge0 ;
593- final Vec2d edge1 ;
594- switch (adjacentFaceDir [this .face ][face2 ]) {
595- case IJ -> {
596- edge0 = maxDimByCIIVec2d [adjRes ][0 ];
597- edge1 = maxDimByCIIVec2d [adjRes ][1 ];
598- }
599- case JK -> {
600- edge0 = maxDimByCIIVec2d [adjRes ][1 ];
601- edge1 = maxDimByCIIVec2d [adjRes ][2 ];
602- }
603- // case KI:
604- default -> {
605- assert (adjacentFaceDir [this .face ][face2 ] == KI );
606- edge0 = maxDimByCIIVec2d [adjRes ][2 ];
607- edge1 = maxDimByCIIVec2d [adjRes ][0 ];
608- }
609- }
610618 // find the intersection and add the lat/lng point to the result
611- final Vec2d inter = Vec2d .v2dIntersect (orig2d0 , orig2d1 , edge0 , edge1 );
612- /*
613- If a point of intersection occurs at a hexagon vertex, then each
614- adjacent hexagon edge will lie completely on a single icosahedron
615- face, and no additional vertex is required.
616- */
617- final boolean isIntersectionAtVertex = orig2d0 .numericallyIdentical (inter ) || orig2d1 .numericallyIdentical (inter );
618- if (isIntersectionAtVertex == false ) {
619+ final Vec2d inter = findIntersectionPoint (orig2d0 , orig2d1 , adjRes , adjacentFaceDir [this .face ][face2 ]);
620+ if (inter != null ) {
619621 points [numPoints ++] = inter .hex2dToGeo (this .face , adjRes , true );
620622 }
621623 }
622624
623625 // convert vertex to lat/lng and add to the result
624626 // vert == start + NUM_HEX_VERTS is only used to test for possible
625627 // intersection on last edge
626- if (vert < start + Constants .NUM_HEX_VERTS ) {
628+ if (vert < Constants .NUM_HEX_VERTS ) {
627629 points [numPoints ++] = fijk .coord .ijkToGeo (fijk .face , adjRes , true );
628630 }
629631 lastFace = fijk .face ;
@@ -632,6 +634,42 @@ public CellBoundary faceIjkToCellBoundary(final int res, final int start, final
632634 return new CellBoundary (points , numPoints );
633635 }
634636
637+ private Vec2d orig (CoordIJK scratch , int [] vertexLast ) {
638+ scratch .reset (vertexLast [0 ] + this .coord .i , vertexLast [1 ] + this .coord .j , vertexLast [2 ] + this .coord .k );
639+ scratch .ijkNormalize ();
640+ return scratch .ijkToHex2d ();
641+ }
642+
643+ private Vec2d findIntersectionPoint (Vec2d orig2d0 , Vec2d orig2d1 , int adjRes , int faceDir ) {
644+ // find the appropriate icosa face edge vertexes
645+ final Vec2d edge0 ;
646+ final Vec2d edge1 ;
647+ switch (faceDir ) {
648+ case IJ -> {
649+ edge0 = maxDimByCIIVec2d [adjRes ][0 ];
650+ edge1 = maxDimByCIIVec2d [adjRes ][1 ];
651+ }
652+ case JK -> {
653+ edge0 = maxDimByCIIVec2d [adjRes ][1 ];
654+ edge1 = maxDimByCIIVec2d [adjRes ][2 ];
655+ }
656+ // case KI:
657+ default -> {
658+ assert (faceDir == KI );
659+ edge0 = maxDimByCIIVec2d [adjRes ][2 ];
660+ edge1 = maxDimByCIIVec2d [adjRes ][0 ];
661+ }
662+ }
663+ // find the intersection and add the lat/lng point to the result
664+ final Vec2d inter = Vec2d .v2dIntersect (orig2d0 , orig2d1 , edge0 , edge1 );
665+ /*
666+ If a point of intersection occurs at a hexagon vertex, then each
667+ adjacent hexagon edge will lie completely on a single icosahedron
668+ face, and no additional vertex is required.
669+ */
670+ return orig2d0 .numericallyIdentical (inter ) || orig2d1 .numericallyIdentical (inter ) ? null : inter ;
671+ }
672+
635673 /**
636674 * compute the corresponding H3Index.
637675 * @param res The cell resolution.
@@ -651,7 +689,6 @@ static long faceIjkToH3(int res, int face, CoordIJK coord) {
651689 // out of range input
652690 throw new IllegalArgumentException (" out of range input" );
653691 }
654-
655692 return H3Index .H3_set_base_cell (h , BaseCells .getBaseCell (face , coord ));
656693 }
657694
0 commit comments