juijs-chart
Version:
SVG-based JUI chart that can be used in the browser and Node.js. Support many types of charts. (Dashboard, Map, Topology, Full 3D)
391 lines (311 loc) • 12.6 kB
JavaScript
import jui from '../main.js';
import TreemapBrush from './treemap.js';
jui.use(TreemapBrush);
const QuickSort = function (array, isClone) {
var compareFunc = null,
array = (isClone) ? array.slice(0) : array;
function swap(indexA, indexB) {
var temp = array[indexA];
array[indexA] = array[indexB];
array[indexB] = temp;
}
function partition(pivot, left, right) {
var storeIndex = left, pivotValue = array[pivot];
swap(pivot, right);
for (var v = left; v < right; v++) {
if (compareFunc(array[v], pivotValue) || !compareFunc(pivotValue, array[v]) && v % 2 == 1) {
swap(v, storeIndex);
storeIndex++;
}
}
swap(right, storeIndex);
return storeIndex;
}
this.setCompare = function (func) {
compareFunc = func;
}
this.run = function (left, right) {
var pivot = null,
newPivot = null;
if (typeof left !== 'number') {
left = 0;
}
if (typeof right !== 'number') {
right = array.length - 1;
}
if (left < right) {
pivot = left + Math.ceil((right - left) * 0.5);
newPivot = partition(pivot, left, right);
this.run(left, newPivot - 1);
this.run(newPivot + 1, right);
}
return array;
}
}
export default {
name: "chart.brush.flame",
extend: "chart.brush.core",
component: function () {
const _ = jui.include("util.base");
const NodeManager = jui.include("chart.brush.treemap.nodemanager");
const TEXT_MARGIN = 3;
/**
* @class chart.brush.flame
*
* @extends chart.brush.core
*/
const FlameBrush = function () {
var self = this,
g = null,
height = 0,
maxHeight = 0,
nodes = new NodeManager(),
disableOpacity = 1,
newData = [],
activeDepth = null;
function getNodeAndTextOpacity(depth) {
return (activeDepth == null) ? 1 : (depth < activeDepth) ? disableOpacity : 1;
}
function createNodeElement(node, color) {
var newColor = self.chart.color(color);
var r = self.svg.rect({
fill: newColor,
"fill-opacity": getNodeAndTextOpacity(node.depth),
stroke: self.chart.theme("flameNodeBorderColor"),
"stroke-width": self.chart.theme("flameNodeBorderWidth"),
width: node.width,
height: node.height,
x: node.x,
y: node.y,
cursor: "pointer"
});
// 마우스 오버 효과
r.hover(function () {
r.attr({ stroke: newColor });
}, function () {
r.attr({ stroke: self.chart.theme("flameNodeBorderColor") });
});
// 노드 공통 이벤트 설정
self.addEvent(r, node);
// 노드 엘리먼트 캐싱
node.element = {
rect: r
};
return r;
}
function createTextElement(node, color) {
if (!_.typeCheck("function", self.brush.format)) {
return null;
}
var newColor = self.chart.color(color),
fontSize = self.chart.theme("flameTextFontSize"),
startX = node.x;
if (self.brush.textAlign == "middle") {
startX += node.width / 2;
} else if (self.brush.textAlign == "end") {
startX += node.width - TEXT_MARGIN;
} else {
startX += TEXT_MARGIN;
}
var t = self.chart.text({
"font-size": fontSize,
"font-weight": "bold",
fill: self.chart.theme("flameTextFontColor"),
"fill-opacity": getNodeAndTextOpacity(node.depth),
x: startX,
y: node.y + (fontSize / 3) + (height / 2),
"text-anchor": self.brush.textAlign,
cursor: "pointer"
}, self.format(node));
// 마우스 오버 효과
t.hover(function () {
node.element.rect.attr({ stroke: newColor });
}, function () {
node.element.rect.attr({ stroke: self.chart.theme("flameNodeBorderColor") });
});
// 노드 공통 이벤트 설정
self.addEvent(t, node);
// 노드 엘리먼트 캐싱
node.element.text = t;
return t;
}
function drawNodeAll(g, node, width, sx) {
var color = self.color(0);
node.width = width;
node.height = height;
node.x = sx;
// 노드 그리는 위치 설정
if (self.brush.nodeOrient == "bottom") {
node.y = maxHeight - (height * node.depth);
} else {
node.y = height * node.depth;
}
// 노드 컬러 설정
if (_.typeCheck("function", self.brush.nodeColor)) {
color = self.brush.nodeColor.call(self.chart, node);
}
var r = createNodeElement(node, color),
t = createTextElement(node, color);
if (self.brush.nodeAlign == "start") {
var cStartX = node.x;
for (var i = 0; i < node.children.length; i++) {
var cNode = node.children[i],
cRate = cNode.value / node.value,
cWidth = node.width * cRate;
drawNodeAll(g, cNode, cWidth, cStartX);
cStartX += cWidth;
}
} else {
var cStartX = node.x + node.width;
for (var i = node.children.length - 1; i >= 0; i--) {
var cNode = node.children[i],
cRate = cNode.value / node.value,
cWidth = node.width * cRate;
cStartX -= cWidth;
drawNodeAll(g, cNode, cWidth, cStartX);
}
}
g.append(r);
if (t != null) {
g.append(t);
}
}
function getMaxDepth(nodes) {
var maxDepth = 0;
for (var i = 0; i < nodes.length; i++) {
maxDepth = Math.max(maxDepth, nodes[i].depth);
}
return maxDepth;
}
function createFilteredNodes(activeNode) {
setCacheParents(activeNode, activeNode.value);
setCacheChildren(activeNode);
sortingCacheNodes();
var tmpData = createIndexData(activeNode),
tmpNodes = new NodeManager();
for (var i = 0; i < tmpData.length; i++) {
var d = tmpData[i];
tmpNodes.insertNode(d.index, {
text: d.text,
value: d.value,
x: 0,
y: 0,
width: 0,
height: 0
});
}
// 필터링 된 노드 캐싱
self.axis.cacheNodes = tmpNodes;
return tmpNodes.getNode()[0];
}
function setCacheParents(node, value) {
if (node.depth > 0) {
node.value = value;
newData.push(node);
if (node.parent) {
setCacheParents(node.parent, value);
}
}
}
function setCacheChildren(node) {
for (var i = 0; i < node.children.length; i++) {
var cNode = node.children[i];
newData.push(cNode);
if (cNode.children.length > 0) {
setCacheChildren(cNode);
}
}
}
function sortingCacheNodes() {
var qs = new QuickSort(newData);
qs.setCompare(function (a, b) {
return (a.depth < b.depth) ? true : false;
});
qs.run();
}
function createIndexData(node) {
var tmpData = [], index = "";
for (var i = 0; i < newData.length; i++) {
if (index == "") {
index = "0";
} else {
index += ".0";
}
if (newData[i].depth < node.depth) {
tmpData.push({
index: index,
text: newData[i].text,
value: newData[i].value
});
} else {
createChildIndexData(node, index, tmpData);
break;
}
}
return tmpData;
}
function createChildIndexData(node, index, result) {
result.push({
index: index,
value: node.value,
text: node.text
});
for (var i = 0; i < node.children.length; i++) {
var cNode = node.children[i];
createChildIndexData(cNode, index + "." + i, result);
}
}
this.drawBefore = function () {
g = this.svg.group();
for (var i = 0; i < this.axis.data.length; i++) {
var d = this.axis.data[i],
k = this.getValue(d, "index");
nodes.insertNode(k, {
text: "" + this.getValue(d, "text", ""),
value: this.getValue(d, "value", 0),
x: this.getValue(d, "x", 0),
y: this.getValue(d, "y", 0),
width: this.getValue(d, "width", 0),
height: this.getValue(d, "height", 0)
});
}
var maxDepth = (this.brush.maxDepth == null) ? getMaxDepth(nodes.getNodeAll()) : this.brush.maxDepth;
height = this.axis.area("height") / maxDepth;
maxHeight = this.axis.area("height");
// 비활성화 노드 투명도
disableOpacity = this.chart.theme("flameDisableBackgroundOpacity");
}
this.draw = function () {
var area = this.axis.area(),
root = nodes.getNode()[0],
activeIndex = this.brush.activeIndex;
if (root) {
// 액티브 노드가 있을 경우, 노드 재정의
if (_.typeCheck("string", activeIndex)) {
var activeNode = nodes.getNode(activeIndex);
if (activeNode == null) {
activeNode = this.axis.cacheNodes.getNode(activeIndex);
}
root = createFilteredNodes(activeNode);
activeDepth = activeNode.depth;
}
drawNodeAll(g, root, area.width, area.x);
}
return g;
}
}
FlameBrush.setup = function () {
return {
maxDepth: null,
nodeOrient: "bottom",
nodeAlign: "end",
textAlign: "start",
nodeColor: null,
activeIndex: null,
clip: false,
format: null
};
}
return FlameBrush;
}
}