]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * This class we summarize the data and returns it when required.\r | |
3 | */\r | |
4 | Ext.define("Ext.draw.SegmentTree", {\r | |
5 | \r | |
6 | config: {\r | |
7 | strategy: "double"\r | |
8 | },\r | |
9 | \r | |
10 | /**\r | |
11 | * @private\r | |
12 | * @param {Object} result\r | |
13 | * @param {Number} last\r | |
14 | * @param {Number} dataX\r | |
15 | * @param {Number} dataOpen\r | |
16 | * @param {Number} dataHigh\r | |
17 | * @param {Number} dataLow\r | |
18 | * @param {Number} dataClose\r | |
19 | */\r | |
20 | time: function (result, last, dataX, dataOpen, dataHigh, dataLow, dataClose) {\r | |
21 | var start = 0, lastOffset, lastOffsetEnd,\r | |
22 | minimum = new Date(dataX[result.startIdx[0]]),\r | |
23 | maximum = new Date(dataX[result.endIdx[last - 1]]),\r | |
24 | extDate = Ext.Date,\r | |
25 | units = [\r | |
26 | [extDate.MILLI, 1, 'ms1', null],\r | |
27 | [extDate.MILLI, 2, 'ms2', 'ms1'],\r | |
28 | [extDate.MILLI, 5, 'ms5', 'ms1'],\r | |
29 | [extDate.MILLI, 10, 'ms10', 'ms5'],\r | |
30 | [extDate.MILLI, 50, 'ms50', 'ms10'],\r | |
31 | [extDate.MILLI, 100, 'ms100', 'ms50'],\r | |
32 | [extDate.MILLI, 500, 'ms500', 'ms100'],\r | |
33 | [extDate.SECOND, 1, 's1', 'ms500'],\r | |
34 | [extDate.SECOND, 10, 's10', 's1'],\r | |
35 | [extDate.SECOND, 30, 's30', 's10'],\r | |
36 | [extDate.MINUTE, 1, 'mi1', 's10'],\r | |
37 | [extDate.MINUTE, 5, 'mi5', 'mi1'],\r | |
38 | [extDate.MINUTE, 10, 'mi10', 'mi5'],\r | |
39 | [extDate.MINUTE, 30, 'mi30', 'mi10'],\r | |
40 | [extDate.HOUR, 1, 'h1', 'mi30'],\r | |
41 | [extDate.HOUR, 6, 'h6', 'h1'],\r | |
42 | [extDate.HOUR, 12, 'h12', 'h6'],\r | |
43 | [extDate.DAY, 1, 'd1', 'h12'],\r | |
44 | [extDate.DAY, 7, 'd7', 'd1'],\r | |
45 | [extDate.MONTH, 1, 'mo1', 'd1'],\r | |
46 | [extDate.MONTH, 3, 'mo3', 'mo1'],\r | |
47 | [extDate.MONTH, 6, 'mo6', 'mo3'],\r | |
48 | [extDate.YEAR, 1, 'y1', 'mo3'],\r | |
49 | [extDate.YEAR, 5, 'y5', 'y1'],\r | |
50 | [extDate.YEAR, 10, 'y10', 'y5'],\r | |
51 | [extDate.YEAR, 100, 'y100', 'y10']\r | |
52 | ], unitIdx, currentUnit,\r | |
53 | plainStart = start,\r | |
54 | plainEnd = last,\r | |
55 | first = false,\r | |
56 | startIdxs = result.startIdx,\r | |
57 | endIdxs = result.endIdx,\r | |
58 | minIdxs = result.minIdx,\r | |
59 | maxIdxs = result.maxIdx,\r | |
60 | opens = result.open,\r | |
61 | closes = result.close,\r | |
62 | minXs = result.minX,\r | |
63 | minYs = result.minY,\r | |
64 | maxXs = result.maxX,\r | |
65 | maxYs = result.maxY,\r | |
66 | i, current;\r | |
67 | \r | |
68 | for (unitIdx = 0; last > start + 1 && unitIdx < units.length; unitIdx++) {\r | |
69 | minimum = new Date(dataX[startIdxs[0]]);\r | |
70 | currentUnit = units[unitIdx];\r | |
71 | minimum = extDate.align(minimum, currentUnit[0], currentUnit[1]);\r | |
72 | if (extDate.diff(minimum, maximum, currentUnit[0]) > dataX.length * 2 * currentUnit[1]) {\r | |
73 | continue;\r | |
74 | }\r | |
75 | if (currentUnit[3] && result.map['time_' + currentUnit[3]]) {\r | |
76 | lastOffset = result.map['time_' + currentUnit[3]][0];\r | |
77 | lastOffsetEnd = result.map['time_' + currentUnit[3]][1];\r | |
78 | } else {\r | |
79 | lastOffset = plainStart;\r | |
80 | lastOffsetEnd = plainEnd;\r | |
81 | }\r | |
82 | \r | |
83 | start = last;\r | |
84 | current = minimum;\r | |
85 | first = true;\r | |
86 | \r | |
87 | startIdxs[last] = startIdxs[lastOffset];\r | |
88 | endIdxs[last] = endIdxs[lastOffset];\r | |
89 | minIdxs[last] = minIdxs[lastOffset];\r | |
90 | maxIdxs[last] = maxIdxs[lastOffset];\r | |
91 | opens[last] = opens[lastOffset];\r | |
92 | closes[last] = closes[lastOffset];\r | |
93 | minXs[last] = minXs[lastOffset];\r | |
94 | minYs[last] = minYs[lastOffset];\r | |
95 | maxXs[last] = maxXs[lastOffset];\r | |
96 | maxYs[last] = maxYs[lastOffset];\r | |
97 | current = Ext.Date.add(current, currentUnit[0], currentUnit[1]);\r | |
98 | \r | |
99 | for (i = lastOffset + 1; i < lastOffsetEnd; i++) {\r | |
100 | if (dataX[endIdxs[i]] < +current) {\r | |
101 | endIdxs[last] = endIdxs[i];\r | |
102 | closes[last] = closes[i];\r | |
103 | if (maxYs[i] > maxYs[last]) {\r | |
104 | maxYs[last] = maxYs[i];\r | |
105 | maxXs[last] = maxXs[i];\r | |
106 | maxIdxs[last] = maxIdxs[i];\r | |
107 | }\r | |
108 | if (minYs[i] < minYs[last]) {\r | |
109 | minYs[last] = minYs[i];\r | |
110 | minXs[last] = minXs[i];\r | |
111 | minIdxs[last] = minIdxs[i];\r | |
112 | }\r | |
113 | } else {\r | |
114 | last++;\r | |
115 | startIdxs[last] = startIdxs[i];\r | |
116 | endIdxs[last] = endIdxs[i];\r | |
117 | minIdxs[last] = minIdxs[i];\r | |
118 | maxIdxs[last] = maxIdxs[i];\r | |
119 | opens[last] = opens[i];\r | |
120 | closes[last] = closes[i];\r | |
121 | minXs[last] = minXs[i];\r | |
122 | minYs[last] = minYs[i];\r | |
123 | maxXs[last] = maxXs[i];\r | |
124 | maxYs[last] = maxYs[i];\r | |
125 | current = Ext.Date.add(current, currentUnit[0], currentUnit[1]);\r | |
126 | }\r | |
127 | }\r | |
128 | if (last > start) {\r | |
129 | result.map['time_' + currentUnit[2]] = [start, last];\r | |
130 | }\r | |
131 | }\r | |
132 | },\r | |
133 | \r | |
134 | /**\r | |
135 | * @private\r | |
136 | * @param {Object} result\r | |
137 | * @param {Number} position\r | |
138 | * @param {Number} dataX\r | |
139 | * @param {Number} dataOpen\r | |
140 | * @param {Number} dataHigh\r | |
141 | * @param {Number} dataLow\r | |
142 | * @param {Number} dataClose\r | |
143 | */\r | |
144 | "double": function (result, position, dataX, dataOpen, dataHigh, dataLow, dataClose) {\r | |
145 | var offset = 0, lastOffset, step = 1,\r | |
146 | i,\r | |
147 | startIdx,\r | |
148 | endIdx,\r | |
149 | minIdx,\r | |
150 | maxIdx,\r | |
151 | open,\r | |
152 | close,\r | |
153 | minX,\r | |
154 | minY,\r | |
155 | maxX,\r | |
156 | maxY;\r | |
157 | while (position > offset + 1) {\r | |
158 | lastOffset = offset;\r | |
159 | offset = position;\r | |
160 | step += step;\r | |
161 | for (i = lastOffset; i < offset; i += 2) {\r | |
162 | if (i === offset - 1) {\r | |
163 | startIdx = result.startIdx[i];\r | |
164 | endIdx = result.endIdx[i];\r | |
165 | minIdx = result.minIdx[i];\r | |
166 | maxIdx = result.maxIdx[i];\r | |
167 | open = result.open[i];\r | |
168 | close = result.close[i];\r | |
169 | minX = result.minX[i];\r | |
170 | minY = result.minY[i];\r | |
171 | maxX = result.maxX[i];\r | |
172 | maxY = result.maxY[i];\r | |
173 | } else {\r | |
174 | \r | |
175 | startIdx = result.startIdx[i];\r | |
176 | endIdx = result.endIdx[i + 1];\r | |
177 | open = result.open[i];\r | |
178 | close = result.close[i];\r | |
179 | if (result.minY[i] <= result.minY[i + 1]) {\r | |
180 | minIdx = result.minIdx[i];\r | |
181 | minX = result.minX[i];\r | |
182 | minY = result.minY[i];\r | |
183 | } else {\r | |
184 | minIdx = result.minIdx[i + 1];\r | |
185 | minX = result.minX[i + 1];\r | |
186 | minY = result.minY[i + 1];\r | |
187 | }\r | |
188 | if (result.maxY[i] >= result.maxY[i + 1]) {\r | |
189 | maxIdx = result.maxIdx[i];\r | |
190 | maxX = result.maxX[i];\r | |
191 | maxY = result.maxY[i];\r | |
192 | } else {\r | |
193 | maxIdx = result.maxIdx[i + 1];\r | |
194 | maxX = result.maxX[i + 1];\r | |
195 | maxY = result.maxY[i + 1];\r | |
196 | }\r | |
197 | }\r | |
198 | result.startIdx[position] = startIdx;\r | |
199 | result.endIdx[position] = endIdx;\r | |
200 | result.minIdx[position] = minIdx;\r | |
201 | result.maxIdx[position] = maxIdx;\r | |
202 | result.open[position] = open;\r | |
203 | result.close[position] = close;\r | |
204 | result.minX[position] = minX;\r | |
205 | result.minY[position] = minY;\r | |
206 | result.maxX[position] = maxX;\r | |
207 | result.maxY[position] = maxY;\r | |
208 | position++;\r | |
209 | }\r | |
210 | result.map['double_' + step] = [offset, position];\r | |
211 | }\r | |
212 | },\r | |
213 | \r | |
214 | /**\r | |
215 | * @private\r | |
216 | */\r | |
217 | none: Ext.emptyFn,\r | |
218 | \r | |
219 | /**\r | |
220 | * @private\r | |
221 | *\r | |
222 | * @param {Number} dataX\r | |
223 | * @param {Number} dataOpen\r | |
224 | * @param {Number} dataHigh\r | |
225 | * @param {Number} dataLow\r | |
226 | * @param {Number} dataClose\r | |
227 | * @return {Object}\r | |
228 | */\r | |
229 | aggregateData: function (dataX, dataOpen, dataHigh, dataLow, dataClose) {\r | |
230 | var length = dataX.length,\r | |
231 | startIdx = [],\r | |
232 | endIdx = [],\r | |
233 | minIdx = [],\r | |
234 | maxIdx = [],\r | |
235 | open = [],\r | |
236 | minX = [],\r | |
237 | minY = [],\r | |
238 | maxX = [],\r | |
239 | maxY = [],\r | |
240 | close = [],\r | |
241 | result = {\r | |
242 | startIdx: startIdx,\r | |
243 | endIdx: endIdx,\r | |
244 | minIdx: minIdx,\r | |
245 | maxIdx: maxIdx,\r | |
246 | open: open,\r | |
247 | minX: minX,\r | |
248 | minY: minY,\r | |
249 | maxX: maxX,\r | |
250 | maxY: maxY,\r | |
251 | close: close\r | |
252 | },\r | |
253 | i;\r | |
254 | \r | |
255 | for (i = 0; i < length; i++) {\r | |
256 | startIdx[i] = i;\r | |
257 | endIdx[i] = i;\r | |
258 | minIdx[i] = i;\r | |
259 | maxIdx[i] = i;\r | |
260 | open[i] = dataOpen[i];\r | |
261 | minX[i] = dataX[i];\r | |
262 | minY[i] = dataLow[i];\r | |
263 | maxX[i] = dataX[i];\r | |
264 | maxY[i] = dataHigh[i];\r | |
265 | close[i] = dataClose[i];\r | |
266 | }\r | |
267 | \r | |
268 | result.map = {\r | |
269 | original: [0, length]\r | |
270 | };\r | |
271 | if (length) {\r | |
272 | this[this.getStrategy()](result, length, dataX, dataOpen, dataHigh, dataLow, dataClose);\r | |
273 | }\r | |
274 | return result;\r | |
275 | },\r | |
276 | \r | |
277 | /**\r | |
278 | * @private\r | |
279 | * @param {Object} items\r | |
280 | * @param {Number} start\r | |
281 | * @param {Number} end\r | |
282 | * @param {Number} key\r | |
283 | * @return {*}\r | |
284 | */\r | |
285 | binarySearchMin: function (items, start, end, key) {\r | |
286 | var dx = this.dataX;\r | |
287 | if (key <= dx[items.startIdx[0]]) {\r | |
288 | return start;\r | |
289 | }\r | |
290 | if (key >= dx[items.startIdx[end - 1]]) {\r | |
291 | return end - 1;\r | |
292 | }\r | |
293 | while (start + 1 < end) {\r | |
294 | var mid = (start + end) >> 1,\r | |
295 | val = dx[items.startIdx[mid]];\r | |
296 | if (val === key) {\r | |
297 | return mid;\r | |
298 | } else if (val < key) {\r | |
299 | start = mid;\r | |
300 | } else {\r | |
301 | end = mid;\r | |
302 | }\r | |
303 | }\r | |
304 | return start;\r | |
305 | },\r | |
306 | \r | |
307 | /**\r | |
308 | * @private\r | |
309 | * @param {Object} items\r | |
310 | * @param {Number} start\r | |
311 | * @param {Number} end\r | |
312 | * @param {Number} key\r | |
313 | * @return {*}\r | |
314 | */\r | |
315 | binarySearchMax: function (items, start, end, key) {\r | |
316 | var dx = this.dataX;\r | |
317 | if (key <= dx[items.endIdx[0]]) {\r | |
318 | return start;\r | |
319 | }\r | |
320 | if (key >= dx[items.endIdx[end - 1]]) {\r | |
321 | return end - 1;\r | |
322 | }\r | |
323 | while (start + 1 < end) {\r | |
324 | var mid = (start + end) >> 1,\r | |
325 | val = dx[items.endIdx[mid]];\r | |
326 | if (val === key) {\r | |
327 | return mid;\r | |
328 | } else if (val < key) {\r | |
329 | start = mid;\r | |
330 | } else {\r | |
331 | end = mid;\r | |
332 | }\r | |
333 | }\r | |
334 | return end;\r | |
335 | },\r | |
336 | \r | |
337 | constructor: function (config) {\r | |
338 | this.initConfig(config);\r | |
339 | },\r | |
340 | \r | |
341 | /**\r | |
342 | * Sets the data of the segment tree.\r | |
343 | * @param {Number} dataX\r | |
344 | * @param {Number} dataOpen\r | |
345 | * @param {Number} dataHigh\r | |
346 | * @param {Number} dataLow\r | |
347 | * @param {Number} dataClose\r | |
348 | */\r | |
349 | setData: function (dataX, dataOpen, dataHigh, dataLow, dataClose) {\r | |
350 | if (!dataHigh) {\r | |
351 | dataClose = dataLow = dataHigh = dataOpen;\r | |
352 | }\r | |
353 | this.dataX = dataX;\r | |
354 | this.dataOpen = dataOpen;\r | |
355 | this.dataHigh = dataHigh;\r | |
356 | this.dataLow = dataLow;\r | |
357 | this.dataClose = dataClose;\r | |
358 | if (dataX.length === dataHigh.length &&\r | |
359 | dataX.length === dataLow.length) {\r | |
360 | this.cache = this.aggregateData(dataX, dataOpen, dataHigh, dataLow, dataClose);\r | |
361 | }\r | |
362 | },\r | |
363 | \r | |
364 | /**\r | |
365 | * Returns the minimum range of data that fits the given range and step size.\r | |
366 | *\r | |
367 | * @param {Number} min\r | |
368 | * @param {Number} max\r | |
369 | * @param {Number} estStep\r | |
370 | * @return {Object} The aggregation information.\r | |
371 | * @return {Number} return.start\r | |
372 | * @return {Number} return.end\r | |
373 | * @return {Object} return.data The aggregated data\r | |
374 | */\r | |
375 | getAggregation: function (min, max, estStep) {\r | |
376 | if (!this.cache) {\r | |
377 | return null;\r | |
378 | }\r | |
379 | var minStep = Infinity,\r | |
380 | range = this.dataX[this.dataX.length - 1] - this.dataX[0],\r | |
381 | cacheMap = this.cache.map,\r | |
382 | result = cacheMap.original,\r | |
383 | name, positions, ln, step, minIdx, maxIdx;\r | |
384 | \r | |
385 | for (name in cacheMap) {\r | |
386 | positions = cacheMap[name];\r | |
387 | ln = positions[1] - positions[0] - 1;\r | |
388 | step = range / ln;\r | |
389 | if (estStep <= step && step < minStep) {\r | |
390 | result = positions;\r | |
391 | minStep = step;\r | |
392 | }\r | |
393 | }\r | |
394 | minIdx = Math.max(this.binarySearchMin(this.cache, result[0], result[1], min), result[0]);\r | |
395 | maxIdx = Math.min(this.binarySearchMax(this.cache, result[0], result[1], max) + 1, result[1]);\r | |
396 | return {\r | |
397 | data: this.cache,\r | |
398 | start: minIdx,\r | |
399 | end: maxIdx\r | |
400 | };\r | |
401 | }\r | |
402 | });\r |