-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathSpatialIndex.js
More file actions
150 lines (136 loc) · 4.41 KB
/
SpatialIndex.js
File metadata and controls
150 lines (136 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import getLiniarBreaks from './getLiniarBreaks';
import QuadTree from './QuadTree';
import Rect from '../graphics/structs/Rect';
/**
* Create spatial index structure. It uses collection of sizes to distribute
* rectangles into buckets of similar size elements. Elements of the same bucket
* are approximated to points. The search of rectangles is transformed to search of points
* within given range plus offset for maximum linear rectangle size.
*
* @class SpatialIndex
*
* @param {Array} sizes Reference to optional collection of possible sizes of items we plan to store
* in the index
*
* @returns {SpatialIndex} Returns spacial index data structure.
*/
export default function SpatialIndex(sizes) {
var _buckets = [];
sizes.sort(function (a, b) { return a - b; });
switch (sizes.length) {
case 0:
_buckets.push(new Bucket(0, 1000000));
break;
case 1:
var size1 = sizes[0];
_buckets.push(new Bucket(0, size1));
break;
case 2:
size1 = sizes[0];
var size2 = sizes[1];
if (size2 > size1 * 2) {
_buckets.push(new Bucket(0, size1));
_buckets.push(new Bucket(size1, size2));
} else {
_buckets.push(new Bucket(0, size2));
}
break;
default:
var breaks = getLiniarBreaks(sizes);
var minimum = 0;
for (var index = 0; index < breaks.length; index += 1) {
var maximum = sizes[breaks[index]];
_buckets.push(new Bucket(minimum, maximum));
minimum = maximum;
}
break;
}
function Bucket(minimum, maximum) {
this.minimum = minimum;
this.maximum = maximum;
this.quadTree = QuadTree(maximum * 2);
}
/**
* Adds rectangle to spacial index
* @param {Rect} rect Rectangle
*/
function addRect(rect) {
var size = Math.max(rect.width, rect.height);
var point = rect.centerPoint();
for (var index = 0, len = _buckets.length; index < len; index += 1) {
var bucket = _buckets[index];
if (size <= bucket.maximum || index == len - 1) {
point.context = rect;
bucket.quadTree.addPoint(point);
break;
}
}
}
/**
* Callback function for iteration of spacial index rectangles
*
* @callback onSpatialIndexItemCallback
* @param {React} rect Rectangle
* @returns {boolean} Returns true to break iteration process.
*/
/**
* Loops rectangular area of spacial index
*
* @param {object} thisArg The callback function invocation context
* @param {Rect} rect Rectangular search area
* @param {onSpatialIndexItemCallback} onItem Callback function to call for every rectangle intersecting given rectangular search area
*/
function loopArea(thisArg, rect, onItem) { // onItem = function(itemid) {}
if (onItem != null) {
for (var index = 0, len = _buckets.length; index < len; index += 1) {
var bucket = _buckets[index];
var bucketRect = new Rect(rect);
bucketRect.offset(bucket.maximum / 2.0);
bucket.quadTree.loopArea(this, bucketRect, function (point) {
var pointRect = point.context;
if (rect.overlaps(pointRect)) {
return onItem.call(thisArg, pointRect);
}
});
}
}
}
/**
* Validates internal data consistency of spacial index data structure
*
* @ignore
* @returns {boolean} Returns true if structure pass validation
*/
function validate() {
var result = true;
for (var index = 0, len = _buckets.length; index < len; index += 1) {
var bucket = _buckets[index];
result = result && bucket.quadTree.validate();
}
return result;
}
/**
* Returns collection of quadrants created in spacial index
* Quadrants exists only when elements exists in them.
* This method is used for visual debugging of the structure.
*
* @ignore
* @param {React} selection Rectangular test area to highlight quadrants
* @returns {Rect[]} Returns collection of available quadrants.
* Quadrants containing points within selection area have context.highlight property set to true.
*/
function getPositions(selection) {
var result = [];
for (var index = 0, len = _buckets.length; index < len; index += 1) {
var bucket = _buckets[index];
result = result.concat(bucket.quadTree.getPositions(selection));
}
return result;
}
return {
addRect: addRect,
loopArea: loopArea,
validate: validate,
getPositions: getPositions
};
};