402 lines
13 KiB
JavaScript
402 lines
13 KiB
JavaScript
/**
|
|
* This class we summarize the data and returns it when required.
|
|
*/
|
|
Ext.define("Ext.draw.SegmentTree", {
|
|
|
|
config: {
|
|
strategy: "double"
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @param result
|
|
* @param last
|
|
* @param dataX
|
|
* @param dataOpen
|
|
* @param dataHigh
|
|
* @param dataLow
|
|
* @param dataClose
|
|
*/
|
|
"time": function (result, last, dataX, dataOpen, dataHigh, dataLow, dataClose) {
|
|
var start = 0, lastOffset, lastOffsetEnd,
|
|
minimum = new Date(dataX[result.startIdx[0]]),
|
|
maximum = new Date(dataX[result.endIdx[last - 1]]),
|
|
extDate = Ext.Date,
|
|
units = [
|
|
[extDate.MILLI, 1, 'ms1', null],
|
|
[extDate.MILLI, 2, 'ms2', 'ms1'],
|
|
[extDate.MILLI, 5, 'ms5', 'ms1'],
|
|
[extDate.MILLI, 10, 'ms10', 'ms5'],
|
|
[extDate.MILLI, 50, 'ms50', 'ms10'],
|
|
[extDate.MILLI, 100, 'ms100', 'ms50'],
|
|
[extDate.MILLI, 500, 'ms500', 'ms100'],
|
|
[extDate.SECOND, 1, 's1', 'ms500'],
|
|
[extDate.SECOND, 10, 's10', 's1'],
|
|
[extDate.SECOND, 30, 's30', 's10'],
|
|
[extDate.MINUTE, 1, 'mi1', 's10'],
|
|
[extDate.MINUTE, 5, 'mi5', 'mi1'],
|
|
[extDate.MINUTE, 10, 'mi10', 'mi5'],
|
|
[extDate.MINUTE, 30, 'mi30', 'mi10'],
|
|
[extDate.HOUR, 1, 'h1', 'mi30'],
|
|
[extDate.HOUR, 6, 'h6', 'h1'],
|
|
[extDate.HOUR, 12, 'h12', 'h6'],
|
|
[extDate.DAY, 1, 'd1', 'h12'],
|
|
[extDate.DAY, 7, 'd7', 'd1'],
|
|
[extDate.MONTH, 1, 'mo1', 'd1'],
|
|
[extDate.MONTH, 3, 'mo3', 'mo1'],
|
|
[extDate.MONTH, 6, 'mo6', 'mo3'],
|
|
[extDate.YEAR, 1, 'y1', 'mo3'],
|
|
[extDate.YEAR, 5, 'y5', 'y1'],
|
|
[extDate.YEAR, 10, 'y10', 'y5'],
|
|
[extDate.YEAR, 100, 'y100', 'y10']
|
|
], unitIdx, currentUnit,
|
|
plainStart = start,
|
|
plainEnd = last,
|
|
first = false,
|
|
startIdxs = result.startIdx,
|
|
endIdxs = result.endIdx,
|
|
minIdxs = result.minIdx,
|
|
maxIdxs = result.maxIdx,
|
|
opens = result.open,
|
|
closes = result.close,
|
|
minXs = result.minX,
|
|
minYs = result.minY,
|
|
maxXs = result.maxX,
|
|
maxYs = result.maxY,
|
|
i, current;
|
|
|
|
for (unitIdx = 0; last > start + 1 && unitIdx < units.length; unitIdx++) {
|
|
minimum = new Date(dataX[startIdxs[0]]);
|
|
currentUnit = units[unitIdx];
|
|
minimum = extDate.align(minimum, currentUnit[0], currentUnit[1]);
|
|
if (extDate.diff(minimum, maximum, currentUnit[0]) > dataX.length * 2 * currentUnit[1]) {
|
|
continue;
|
|
}
|
|
if (currentUnit[3] && result.map['time_' + currentUnit[3]]) {
|
|
lastOffset = result.map['time_' + currentUnit[3]][0];
|
|
lastOffsetEnd = result.map['time_' + currentUnit[3]][1];
|
|
} else {
|
|
lastOffset = plainStart;
|
|
lastOffsetEnd = plainEnd;
|
|
}
|
|
|
|
start = last;
|
|
current = minimum;
|
|
first = true;
|
|
|
|
startIdxs[last] = startIdxs[lastOffset];
|
|
endIdxs[last] = endIdxs[lastOffset];
|
|
minIdxs[last] = minIdxs[lastOffset];
|
|
maxIdxs[last] = maxIdxs[lastOffset];
|
|
opens[last] = opens[lastOffset];
|
|
closes[last] = closes[lastOffset];
|
|
minXs[last] = minXs[lastOffset];
|
|
minYs[last] = minYs[lastOffset];
|
|
maxXs[last] = maxXs[lastOffset];
|
|
maxYs[last] = maxYs[lastOffset];
|
|
current = Ext.Date.add(current, currentUnit[0], currentUnit[1]);
|
|
|
|
for (i = lastOffset + 1; i < lastOffsetEnd; i++) {
|
|
if (dataX[endIdxs[i]] < +current) {
|
|
endIdxs[last] = endIdxs[i];
|
|
closes[last] = closes[i];
|
|
if (maxYs[i] > maxYs[last]) {
|
|
maxYs[last] = maxYs[i];
|
|
maxXs[last] = maxXs[i];
|
|
maxIdxs[last] = maxIdxs[i];
|
|
}
|
|
if (minYs[i] < minYs[last]) {
|
|
minYs[last] = minYs[i];
|
|
minXs[last] = minXs[i];
|
|
minIdxs[last] = minIdxs[i];
|
|
}
|
|
} else {
|
|
last++;
|
|
startIdxs[last] = startIdxs[i];
|
|
endIdxs[last] = endIdxs[i];
|
|
minIdxs[last] = minIdxs[i];
|
|
maxIdxs[last] = maxIdxs[i];
|
|
opens[last] = opens[i];
|
|
closes[last] = closes[i];
|
|
minXs[last] = minXs[i];
|
|
minYs[last] = minYs[i];
|
|
maxXs[last] = maxXs[i];
|
|
maxYs[last] = maxYs[i];
|
|
current = Ext.Date.add(current, currentUnit[0], currentUnit[1]);
|
|
}
|
|
}
|
|
if (last > start) {
|
|
result.map['time_' + currentUnit[2]] = [start, last];
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @param result
|
|
* @param position
|
|
* @param dataX
|
|
* @param dataOpen
|
|
* @param dataHigh
|
|
* @param dataLow
|
|
* @param dataClose
|
|
*/
|
|
"double": function (result, position, dataX, dataOpen, dataHigh, dataLow, dataClose) {
|
|
var offset = 0, lastOffset, step = 1,
|
|
i,
|
|
startIdx,
|
|
endIdx,
|
|
minIdx,
|
|
maxIdx,
|
|
open,
|
|
close,
|
|
minX,
|
|
minY,
|
|
maxX,
|
|
maxY;
|
|
while (position > offset + 1) {
|
|
lastOffset = offset;
|
|
offset = position;
|
|
step += step;
|
|
for (i = lastOffset; i < offset; i += 2) {
|
|
if (i === offset - 1) {
|
|
startIdx = result.startIdx[i];
|
|
endIdx = result.endIdx[i];
|
|
minIdx = result.minIdx[i];
|
|
maxIdx = result.maxIdx[i];
|
|
open = result.open[i];
|
|
close = result.close[i];
|
|
minX = result.minX[i];
|
|
minY = result.minY[i];
|
|
maxX = result.maxX[i];
|
|
maxY = result.maxY[i];
|
|
} else {
|
|
|
|
startIdx = result.startIdx[i];
|
|
endIdx = result.endIdx[i + 1];
|
|
open = result.open[i];
|
|
close = result.close[i];
|
|
if (result.minY[i] <= result.minY[i + 1]) {
|
|
minIdx = result.minIdx[i];
|
|
minX = result.minX[i];
|
|
minY = result.minY[i];
|
|
} else {
|
|
minIdx = result.minIdx[i + 1];
|
|
minX = result.minX[i + 1];
|
|
minY = result.minY[i + 1];
|
|
}
|
|
if (result.maxY[i] >= result.maxY[i + 1]) {
|
|
maxIdx = result.maxIdx[i];
|
|
maxX = result.maxX[i];
|
|
maxY = result.maxY[i];
|
|
} else {
|
|
maxIdx = result.maxIdx[i + 1];
|
|
maxX = result.maxX[i + 1];
|
|
maxY = result.maxY[i + 1];
|
|
}
|
|
}
|
|
result.startIdx[position] = startIdx;
|
|
result.endIdx[position] = endIdx;
|
|
result.minIdx[position] = minIdx;
|
|
result.maxIdx[position] = maxIdx;
|
|
result.open[position] = open;
|
|
result.close[position] = close;
|
|
result.minX[position] = minX;
|
|
result.minY[position] = minY;
|
|
result.maxX[position] = maxX;
|
|
result.maxY[position] = maxY;
|
|
position++;
|
|
}
|
|
result.map['double_' + step] = [offset, position];
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
"none": Ext.emptyFn,
|
|
|
|
/**
|
|
* @private
|
|
*
|
|
* @param dataX
|
|
* @param dataOpen
|
|
* @param dataHigh
|
|
* @param dataLow
|
|
* @param dataClose
|
|
* @return {Object}
|
|
*/
|
|
aggregateData: function (dataX, dataOpen, dataHigh, dataLow, dataClose) {
|
|
var length = dataX.length,
|
|
startIdx = [],
|
|
endIdx = [],
|
|
minIdx = [],
|
|
maxIdx = [],
|
|
open = [],
|
|
minX = [],
|
|
minY = [],
|
|
maxX = [],
|
|
maxY = [],
|
|
close = [],
|
|
result = {
|
|
startIdx: startIdx,
|
|
endIdx: endIdx,
|
|
minIdx: minIdx,
|
|
maxIdx: maxIdx,
|
|
open: open,
|
|
minX: minX,
|
|
minY: minY,
|
|
maxX: maxX,
|
|
maxY: maxY,
|
|
close: close
|
|
},
|
|
i;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
startIdx[i] = i;
|
|
endIdx[i] = i;
|
|
minIdx[i] = i;
|
|
maxIdx[i] = i;
|
|
open[i] = dataOpen[i];
|
|
minX[i] = dataX[i];
|
|
minY[i] = dataLow[i];
|
|
maxX[i] = dataX[i];
|
|
maxY[i] = dataHigh[i];
|
|
close[i] = dataClose[i];
|
|
}
|
|
|
|
result.map = {
|
|
original: [0, length]
|
|
};
|
|
if (length) {
|
|
this[this.getStrategy()](result, length, dataX, dataOpen, dataHigh, dataLow, dataClose);
|
|
}
|
|
return result;
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @param items
|
|
* @param start
|
|
* @param end
|
|
* @param key
|
|
* @return {*}
|
|
*/
|
|
binarySearchMin: function (items, start, end, key) {
|
|
var dx = this.dataX;
|
|
if (key <= dx[items.startIdx[0]]) {
|
|
return start;
|
|
}
|
|
if (key >= dx[items.startIdx[end - 1]]) {
|
|
return end - 1;
|
|
}
|
|
while (start + 1 < end) {
|
|
var mid = (start + end) >> 1,
|
|
val = dx[items.startIdx[mid]];
|
|
if (val === key) {
|
|
return mid;
|
|
} else if (val < key) {
|
|
start = mid;
|
|
} else {
|
|
end = mid;
|
|
}
|
|
}
|
|
return start;
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @param items
|
|
* @param start
|
|
* @param end
|
|
* @param key
|
|
* @return {*}
|
|
*/
|
|
binarySearchMax: function (items, start, end, key) {
|
|
var dx = this.dataX;
|
|
if (key <= dx[items.endIdx[0]]) {
|
|
return start;
|
|
}
|
|
if (key >= dx[items.endIdx[end - 1]]) {
|
|
return end - 1;
|
|
}
|
|
while (start + 1 < end) {
|
|
var mid = (start + end) >> 1,
|
|
val = dx[items.endIdx[mid]];
|
|
if (val === key) {
|
|
return mid;
|
|
} else if (val < key) {
|
|
start = mid;
|
|
} else {
|
|
end = mid;
|
|
}
|
|
}
|
|
return end;
|
|
},
|
|
|
|
constructor: function (config) {
|
|
this.initConfig(config);
|
|
},
|
|
|
|
/**
|
|
* Sets the data of the segment tree.
|
|
* @param dataX
|
|
* @param dataOpen
|
|
* @param dataHigh
|
|
* @param dataLow
|
|
* @param dataClose
|
|
*/
|
|
setData: function (dataX, dataOpen, dataHigh, dataLow, dataClose) {
|
|
if (!dataHigh) {
|
|
dataClose = dataLow = dataHigh = dataOpen;
|
|
}
|
|
this.dataX = dataX;
|
|
this.dataOpen = dataOpen;
|
|
this.dataHigh = dataHigh;
|
|
this.dataLow = dataLow;
|
|
this.dataClose = dataClose;
|
|
if (dataX.length === dataHigh.length &&
|
|
dataX.length === dataLow.length) {
|
|
this.cache = this.aggregateData(dataX, dataOpen, dataHigh, dataLow, dataClose);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns the minimum range of data that fits the given range and step size.
|
|
*
|
|
* @param {Number} min
|
|
* @param {Number} max
|
|
* @param {Number} estStep
|
|
* @return {Object} The aggregation information.
|
|
* @return {Number} return.start
|
|
* @return {Number} return.end
|
|
* @return {Object} return.data The aggregated data
|
|
*/
|
|
getAggregation: function (min, max, estStep) {
|
|
if (!this.cache) {
|
|
return null;
|
|
}
|
|
var minStep = Infinity,
|
|
range = this.dataX[this.dataX.length - 1] - this.dataX[0],
|
|
cacheMap = this.cache.map,
|
|
result = cacheMap.original,
|
|
name, positions, ln, step, minIdx, maxIdx;
|
|
|
|
for (name in cacheMap) {
|
|
positions = cacheMap[name];
|
|
ln = positions[1] - positions[0] - 1;
|
|
step = range / ln;
|
|
if (estStep <= step && step < minStep) {
|
|
result = positions;
|
|
minStep = step;
|
|
}
|
|
}
|
|
minIdx = Math.max(this.binarySearchMin(this.cache, result[0], result[1], min), result[0]);
|
|
maxIdx = Math.min(this.binarySearchMax(this.cache, result[0], result[1], max) + 1, result[1]);
|
|
return {
|
|
data: this.cache,
|
|
start: minIdx,
|
|
end: maxIdx
|
|
};
|
|
}
|
|
}); |