@@ -94,17 +94,31 @@ export default class Supercluster {
9494
9595 // cluster points on max zoom, then cluster the results on previous zoom, etc.;
9696 // results in a cluster hierarchy across zoom levels
97+ let recycled = null ;
9798 for ( let z = maxZoom ; z >= minZoom ; z -- ) {
9899 const now = performance . now ( ) ;
99100
100- // allocate a tight Int32 slab for this zoom; output is strictly <= input length
101- const out = new Int32Array ( prevNum * stride ) ;
101+ // allocate a tight Int32 slab for this zoom; output is strictly <= input length.
102+ // on a plateau zoom prev/prevNum don't advance, so the slab is the right size
103+ // for the next iteration too — carry it forward instead of re-allocating.
104+ const out = recycled || new Int32Array ( prevNum * stride ) ;
105+ recycled = null ;
102106 const written = this . _cluster ( prev , prevNum , z , out ) ;
103- const tree = this . trees [ z ] = this . _createTree ( out , written ) ;
104- prev = out ;
105- prevNum = written ;
106107
107- if ( log ) console . log ( `z${ z } : ${ tree . numItems } clusters in ${ ( performance . now ( ) - now ) . toFixed ( 2 ) } ms` ) ;
108+ if ( written === prevNum ) {
109+ // No clusters formed: output coords are bit-identical to input. Reuse the
110+ // parent tree (same coords, same order) and recycle the out slab — its
111+ // only differences vs prev are OFFSET_ZOOM mutations that query paths
112+ // don't read, and _cluster overwrites every slot it uses on next call.
113+ this . trees [ z ] = this . trees [ z + 1 ] ;
114+ recycled = out ;
115+ } else {
116+ this . trees [ z ] = this . _createTree ( out , written ) ;
117+ prev = out ;
118+ prevNum = written ;
119+ }
120+
121+ if ( log ) console . log ( `z${ z } : ${ this . trees [ z ] . numItems } clusters in ${ ( performance . now ( ) - now ) . toFixed ( 2 ) } ms` ) ;
108122 }
109123
110124 if ( log ) console . timeEnd ( 'total time' ) ;
0 commit comments