import { getDistance } from '../../../utils/common' ; import { Delaunay , Bounds } from '../../../utils/d3-delaunay' ; import { IndexedGeometry , PointGeometry } from '../../../utils/geometry' ; import { Point } from '../../../utils/point' ; import { DEFAULT_HIGHLIGHT_PADDING } from '../rendering/constants' ; internal export type IndexedGeometrySpatialMapPoint = [ number , number ] ; internal export class IndexedGeometrySpatialMap { private map : Delaunay < IndexedGeometrySpatialMapPoint > | null = null ; private points : IndexedGeometrySpatialMapPoint [ ] = [ ] ; private pointGeometries : PointGeometry [ ] = [ ] ; private searchStartIndex : number = 0 ; private maxRadius = - Infinity ; constructor ( points = [ ] ) { this . set ( points ) ; } get size ( ) { return this . points . length ; } isSpatial ( ) { return this . pointGeometries . length > 0 ; } set ( points ) { this . maxRadius = Math . max ( this . maxRadius , ... points . map ( ( { radius } ) => radius ) ) ; const { pointGeometries } = this ; points . forEach ( ( p ) => pointGeometries . push ( p ) ) ; this . points . push ( ... points . map < IndexedGeometrySpatialMapPoint > ( ( { x , y } ) => { const nonce = Math . random ( ) * 0.000001 ; return [ x + nonce , y ] ; } ) ) ; if ( this . points . length > 0 ) { this . map = Delaunay . from ( this . points ) ; } } triangulation = ( bounds ? ) => this . map ?. voronoi ( bounds ) ; getMergeData ( ) { return [ ... this . pointGeometries ] ; } keys ( ) { return this . pointGeometries . map ( ( { value : { x } } ) => x ) ; } find ( point ) { const elements = [ ] ; if ( this . map !== null ) { const index = this . map . find ( point . x , point . y , this . searchStartIndex ) ; const geometry = this . pointGeometries [ index ] ; if ( geometry ) { this . searchStartIndex = index ; elements . push ( geometry ) ; this . getRadialNeighbors ( index , point , new Set ( [ index ] ) ) . forEach ( ( g ) => elements . push ( g ) ) ; } } return elements ; } param selectedIndex param point param visitedIndices private getRadialNeighbors ( selectedIndex , point , visitedIndices ) { if ( this . map === null ) { return [ ] ; } const neighbors = [ ... this . map . neighbors ( selectedIndex ) ] ; return neighbors . reduce < IndexedGeometry [ ] > ( ( acc , i ) => { if ( visitedIndices . has ( i ) ) { return acc ; } visitedIndices . add ( i ) ; const geometry = this . pointGeometries [ i ] ; if ( geometry ) { acc . push ( geometry ) ; if ( getDistance ( geometry , point ) < Math . min ( this . maxRadius , DEFAULT_HIGHLIGHT_PADDING ) ) { this . getRadialNeighbors ( i , point , visitedIndices ) . forEach ( ( g ) => acc . push ( g ) ) ; } } return acc ; } , [ ] ) ; } }