1+ /**
2+ * Create spatial index structure. It uses collection of sizes to distribute
3+ * rectangles into buckets of similar size elements. Elements of the same bucket
4+ * are aproximated to points. The search of rectangles is transformed to search of points
5+ * within given range plus offset for maximum linear rectangle size.
6+ *
7+ * @returns {SpatialIndex } Returns spacial index data structure.
8+ */
19primitives . common . SpatialIndex = function ( sizes ) {
2- var _buckets = [ ] ;
10+ var _buckets = [ ] ;
311
4- sizes . sort ( function ( a , b ) { return a - b ; } ) ;
12+ sizes . sort ( function ( a , b ) { return a - b ; } ) ;
513
6- switch ( sizes . length ) {
7- case 0 :
8- _buckets . push ( new Bucket ( 0 , 1000000 ) ) ;
9- break ;
10- case 1 :
11- var size1 = sizes [ 0 ] ;
12- _buckets . push ( new Bucket ( 0 , size1 ) ) ;
13- break ;
14- case 2 :
15- size1 = sizes [ 0 ] ;
16- var size2 = sizes [ 1 ] ;
17- if ( size2 > size1 * 2 ) {
18- _buckets . push ( new Bucket ( 0 , size1 ) ) ;
19- _buckets . push ( new Bucket ( size1 , size2 ) ) ;
20- } else {
21- _buckets . push ( new Bucket ( 0 , size2 ) ) ;
22- }
23- break ;
24- default :
25- var breaks = primitives . common . getLiniarBreaks ( sizes ) ;
26- var minimum = 0 ;
27- for ( var index = 0 ; index < breaks . length ; index += 1 ) {
28- var maximum = sizes [ breaks [ index ] ] ;
29- _buckets . push ( new Bucket ( minimum , maximum ) ) ;
30- minimum = maximum ;
31- }
32- break ;
33- }
14+ switch ( sizes . length ) {
15+ case 0 :
16+ _buckets . push ( new Bucket ( 0 , 1000000 ) ) ;
17+ break ;
18+ case 1 :
19+ var size1 = sizes [ 0 ] ;
20+ _buckets . push ( new Bucket ( 0 , size1 ) ) ;
21+ break ;
22+ case 2 :
23+ size1 = sizes [ 0 ] ;
24+ var size2 = sizes [ 1 ] ;
25+ if ( size2 > size1 * 2 ) {
26+ _buckets . push ( new Bucket ( 0 , size1 ) ) ;
27+ _buckets . push ( new Bucket ( size1 , size2 ) ) ;
28+ } else {
29+ _buckets . push ( new Bucket ( 0 , size2 ) ) ;
30+ }
31+ break ;
32+ default :
33+ var breaks = primitives . common . getLiniarBreaks ( sizes ) ;
34+ var minimum = 0 ;
35+ for ( var index = 0 ; index < breaks . length ; index += 1 ) {
36+ var maximum = sizes [ breaks [ index ] ] ;
37+ _buckets . push ( new Bucket ( minimum , maximum ) ) ;
38+ minimum = maximum ;
39+ }
40+ break ;
41+ }
3442
35- function Bucket ( minimum , maximum ) {
36- this . minimum = minimum ;
37- this . maximum = maximum ;
38- this . quadTree = primitives . common . QuadTree ( maximum * 2 ) ;
39- }
43+ function Bucket ( minimum , maximum ) {
44+ this . minimum = minimum ;
45+ this . maximum = maximum ;
46+ this . quadTree = primitives . common . QuadTree ( maximum * 2 ) ;
47+ }
4048
41- function addRect ( rect ) {
42- var size = Math . max ( rect . width , rect . height ) ;
43- var point = rect . centerPoint ( ) ;
49+ /**
50+ * Adds rectangle to spacial index
51+ * @param {Rect } rect Rectangle
52+ */
53+ function addRect ( rect ) {
54+ var size = Math . max ( rect . width , rect . height ) ;
55+ var point = rect . centerPoint ( ) ;
4456
45- for ( var index = 0 , len = _buckets . length ; index < len ; index += 1 ) {
46- var bucket = _buckets [ index ] ;
57+ for ( var index = 0 , len = _buckets . length ; index < len ; index += 1 ) {
58+ var bucket = _buckets [ index ] ;
4759
48- if ( size <= bucket . maximum || index == len - 1 ) {
49- point . context = rect ;
50- bucket . quadTree . addPoint ( point ) ;
51- break ;
52- }
53- }
54- }
60+ if ( size <= bucket . maximum || index == len - 1 ) {
61+ point . context = rect ;
62+ bucket . quadTree . addPoint ( point ) ;
63+ break ;
64+ }
65+ }
66+ }
5567
56- function loopArea ( thisArg , rect , onItem ) { // onItem = function(itemid) {}
57- if ( onItem != null ) {
58- for ( var index = 0 , len = _buckets . length ; index < len ; index += 1 ) {
59- var bucket = _buckets [ index ] ;
60- var bucketRect = new primitives . common . Rect ( rect ) ;
61- bucketRect . offset ( bucket . maximum / 2.0 ) ;
62- bucket . quadTree . loopArea ( this , bucketRect , function ( point ) {
63- var pointRect = point . context ;
68+ /**
69+ * Callback function for iteration of spacial index rectangles
70+ *
71+ * @callback onSpatialIndexItemCallback
72+ * @param {React } rect Rectangle
73+ * @returns {boolean } Returns true to break iteration process.
74+ */
6475
65- if ( rect . overlaps ( pointRect ) ) {
66- return onItem . call ( thisArg , pointRect ) ;
67- }
68- } ) ;
69- }
70- }
71- }
76+ /**
77+ * Loops rectangular area of spacial index
78+ *
79+ * @param {object } thisArg The callback function invocation context
80+ * @param {Rect }} rect Rectangular search area
81+ * @param {onSpatialIndexItemCallback } onItem Callback function to call for every rectangle intersecting given rectangular search area
82+ */
83+ function loopArea ( thisArg , rect , onItem ) { // onItem = function(itemid) {}
84+ if ( onItem != null ) {
85+ for ( var index = 0 , len = _buckets . length ; index < len ; index += 1 ) {
86+ var bucket = _buckets [ index ] ;
87+ var bucketRect = new primitives . common . Rect ( rect ) ;
88+ bucketRect . offset ( bucket . maximum / 2.0 ) ;
89+ bucket . quadTree . loopArea ( this , bucketRect , function ( point ) {
90+ var pointRect = point . context ;
7291
73- function validate ( ) {
74- var result = true ;
75- for ( var index = 0 , len = _buckets . length ; index < len ; index += 1 ) {
76- var bucket = _buckets [ index ] ;
92+ if ( rect . overlaps ( pointRect ) ) {
93+ return onItem . call ( thisArg , pointRect ) ;
94+ }
95+ } ) ;
96+ }
97+ }
98+ }
7799
78- result = result && bucket . quadTree . validate ( ) ;
79- }
80- return result ;
81- }
100+ /**
101+ * Validates internal data consistency of spacial index data structure
102+ *
103+ * @returns {boolean } Returns true if structure pass validation
104+ */
105+ function validate ( ) {
106+ var result = true ;
107+ for ( var index = 0 , len = _buckets . length ; index < len ; index += 1 ) {
108+ var bucket = _buckets [ index ] ;
82109
83- function getPositions ( selection ) {
84- var result = [ ] ;
85- for ( var index = 0 , len = _buckets . length ; index < len ; index += 1 ) {
86- var bucket = _buckets [ index ] ;
110+ result = result && bucket . quadTree . validate ( ) ;
111+ }
112+ return result ;
113+ }
87114
88- result = result . concat ( bucket . quadTree . getPositions ( selection ) ) ;
89- }
90- return result ;
91- }
115+ /**
116+ * Returns collection of quadrands created in spacial index
117+ * Quadrants exists only when elements exists in them.
118+ * This method is used for visual debugging of the structure.
119+ *
120+ * @param {React } selection Rectangular test area to highlight quadrants
121+ * @returns {Rect[] } Returns collection of available quadrants.
122+ * Quadrants containing points within selection area have context.highlight property set to true.
123+ */
124+ function getPositions ( selection ) {
125+ var result = [ ] ;
126+ for ( var index = 0 , len = _buckets . length ; index < len ; index += 1 ) {
127+ var bucket = _buckets [ index ] ;
92128
93- return {
94- addRect : addRect ,
95- loopArea : loopArea ,
96- validate : validate ,
97- getPositions : getPositions
98- } ;
129+ result = result . concat ( bucket . quadTree . getPositions ( selection ) ) ;
130+ }
131+ return result ;
132+ }
133+
134+ return {
135+ addRect : addRect ,
136+ loopArea : loopArea ,
137+ validate : validate ,
138+ getPositions : getPositions
139+ } ;
99140} ;
0 commit comments