Skip to content

Commit

Permalink
Build only one 3D Hilbert space filling curve
Browse files Browse the repository at this point in the history
The tree loops back on itself so there is only one instance of any
curve configuration. This means that once the tree of all sub-curves
is built, this structure supports traversal to infinite depth. Therefore
this one static structure can support all 3D curve queries for all
indexes of this envelope and dimensionality. Building the whole tree
statically also solves a concurrency problem we had with concurrent queries
racing for the lazy creation of the tree we were previously doing.
  • Loading branch information
fickludd committed Mar 16, 2018
1 parent 64e7c93 commit af8a2a1
Showing 1 changed file with 46 additions and 45 deletions.
Expand Up @@ -19,8 +19,8 @@
*/ */
package org.neo4j.gis.spatial.index.curves; package org.neo4j.gis.spatial.index.curves;


import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map;


import org.neo4j.gis.spatial.index.Envelope; import org.neo4j.gis.spatial.index.Envelope;


Expand Down Expand Up @@ -65,14 +65,26 @@ static int rotateYZ( int value )
*/ */
static class HilbertCurve3D extends CurveRule static class HilbertCurve3D extends CurveRule
{ {
CurveRule[] children; HilbertCurve3D[] children;


private HilbertCurve3D( int... npointValues ) private HilbertCurve3D( int... npointValues )
{ {
super( 3, npointValues ); super( 3, npointValues );
assert npointValues[0] == 0 || npointValues[0] == 3 || npointValues[0] == 5 || npointValues[0] == 6; assert npointValues[0] == 0 || npointValues[0] == 3 || npointValues[0] == 5 || npointValues[0] == 6;
} }


@Override
public CurveRule childAt( int npoint )
{
return children[npoint];
}

@Override
public String toString()
{
return name().toString();
}

static String binaryString( int value ) static String binaryString( int value )
{ {
String binary = "00" + Integer.toBinaryString( value ); String binary = "00" + Integer.toBinaryString( value );
Expand Down Expand Up @@ -168,47 +180,36 @@ private HilbertCurve3D rotateAboutX()
return new HilbertCurve3D( newNpoints ); return new HilbertCurve3D( newNpoints );
} }


private HilbertCurve3D singleTon( HilbertCurve3D curve ) private void buildCurveTree( Map<SubCurve3D,HilbertCurve3D> curves )
{ {
SubCurve3D name = curve.name(); if ( children == null )
if ( curves.containsKey( name ) )
{
return curves.get( name );
}
else
{ {
curves.put( name, curve ); makeChildren( curves );
return curve; curves.put( name(), this );
}
}


private void makeChildren() for ( HilbertCurve3D child : children )
{ {
this.children = new HilbertCurve3D[length()]; child.buildCurveTree( curves );
this.children[0] = singleTon( rotateOneThirdDiagonalPos( true ) ); }
this.children[1] = singleTon( rotateOneThirdDiagonalPos( false ) ); }
this.children[2] = singleTon( rotateOneThirdDiagonalPos( false ) );
this.children[3] = singleTon( rotateAboutX() );
this.children[4] = singleTon( rotateAboutX() );
this.children[5] = singleTon( rotateOneThirdDiagonalNeg( true ) );
this.children[6] = singleTon( rotateOneThirdDiagonalNeg( true ) );
this.children[7] = singleTon( rotateOneThirdDiagonalNeg( false ) );
} }


@Override private void makeChildren( Map<SubCurve3D,HilbertCurve3D> curves )
public CurveRule childAt( int npoint )
{ {
if ( children == null ) children = new HilbertCurve3D[length()];
{ children[0] = singleton( curves, rotateOneThirdDiagonalPos( true ) );
makeChildren(); children[1] = singleton( curves, rotateOneThirdDiagonalPos( false ) );
} children[2] = singleton( curves, rotateOneThirdDiagonalPos( false ) );
return children[npoint]; children[3] = singleton( curves, rotateAboutX() );
children[4] = singleton( curves, rotateAboutX() );
children[5] = singleton( curves, rotateOneThirdDiagonalNeg( true ) );
children[6] = singleton( curves, rotateOneThirdDiagonalNeg( true ) );
children[7] = singleton( curves, rotateOneThirdDiagonalNeg( false ) );
} }


@Override private HilbertCurve3D singleton( Map<SubCurve3D,HilbertCurve3D> curves, HilbertCurve3D newCurve )
public String toString()
{ {
return name().toString(); return curves.computeIfAbsent( newCurve.name(), key -> newCurve );
} }
} }


Expand Down Expand Up @@ -254,20 +255,20 @@ public String toString()
} }
} }


static HashMap<SubCurve3D,HilbertCurve3D> curves = new LinkedHashMap<>(); // this is left accessible to make debugging easier
static Map<SubCurve3D,HilbertCurve3D> curves = new LinkedHashMap<>();


private static HilbertCurve3D addCurveRule( int... npointValues ) private static HilbertCurve3D buildTheCurve()
{ {
HilbertCurve3D curve = new HilbertCurve3D( npointValues ); // We start with a UFR curve
SubCurve3D name = curve.name(); int[] npointValues = {0b000, 0b010, 0b011, 0b001, 0b101, 0b111, 0b110, 0b100};
if ( !curves.containsKey( name ) ) HilbertCurve3D theCurve = new HilbertCurve3D( npointValues );
{
curves.put( name, curve ); theCurve.buildCurveTree( curves );
} return theCurve;
return curve;
} }


private static final HilbertCurve3D curveUFR = addCurveRule( 0b000, 0b010, 0b011, 0b001, 0b101, 0b111, 0b110, 0b100 ); private static final HilbertCurve3D THE_CURVE = buildTheCurve();


public static final int MAX_LEVEL = 63 / 3 - 1; public static final int MAX_LEVEL = 63 / 3 - 1;


Expand All @@ -286,6 +287,6 @@ public HilbertSpaceFillingCurve3D( Envelope range, int maxLevel )
@Override @Override
protected CurveRule rootCurve() protected CurveRule rootCurve()
{ {
return curveUFR; return THE_CURVE;
} }
} }

0 comments on commit af8a2a1

Please sign in to comment.