tnt.tree
Version:
TnT tree display
237 lines (196 loc) • 7.21 kB
JavaScript
var apijs = require("tnt.api");
var tree = {};
tree.label = function () {
"use strict";
var dispatch = d3.dispatch ("click", "dblclick", "mouseover", "mouseout")
// TODO: Not sure if we should be removing by default prev labels
// or it would be better to have a separate remove method called by the vis
// on update
// We also have the problem that we may be transitioning from
// text to img labels and we need to remove the label of a different type
var label = function (node, layout_type, node_size) {
if (typeof (node) !== 'function') {
throw(node);
}
label.display().call(this, node, layout_type)
.attr("class", "tnt_tree_label")
.attr("transform", function (d) {
var t = label.transform()(node, layout_type);
return "translate (" + (t.translate[0] + node_size) + " " + t.translate[1] + ")rotate(" + t.rotate + ")";
})
// TODO: this click event is probably never fired since there is an onclick event in the node g element?
.on("click", function () {
dispatch.click.call(this, node)
})
.on("dblclick", function () {
dispatch.dblclick.call(this, node)
})
.on("mouseover", function () {
dispatch.mouseover.call(this, node)
})
.on("mouseout", function () {
dispatch.mouseout.call(this, node)
})
};
var api = apijs (label)
.getset ('width', function () { throw "Need a width callback" })
.getset ('height', function () { throw "Need a height callback" })
.getset ('display', function () { throw "Need a display callback" })
.getset ('transform', function () { throw "Need a transform callback" })
//.getset ('on_click');
return d3.rebind (label, dispatch, "on");
};
// Text based labels
tree.label.text = function () {
var label = tree.label();
var api = apijs (label)
.getset ('fontsize', 10)
.getset ('fontweight', "normal")
.getset ('color', "#000")
.getset ('text', function (d) {
return d.data().name;
})
label.display (function (node, layout_type) {
var l = d3.select(this)
.append("text")
.attr("text-anchor", function (d) {
if (layout_type === "radial") {
return (d.x%360 < 180) ? "start" : "end";
}
return "start";
})
.text(function(){
return label.text()(node)
})
.style('font-size', function () {
return d3.functor(label.fontsize())(node) + "px";
})
.style('font-weight', function () {
return d3.functor(label.fontweight())(node);
})
.style('fill', d3.functor(label.color())(node));
return l;
});
label.transform (function (node, layout_type) {
var d = node.data();
var t = {
translate : [5, 5],
rotate : 0
};
if (layout_type === "radial") {
t.translate[1] = t.translate[1] - (d.x%360 < 180 ? 0 : label.fontsize())
t.rotate = (d.x%360 < 180 ? 0 : 180)
}
return t;
});
// label.transform (function (node) {
// var d = node.data();
// return "translate(10 5)rotate(" + (d.x%360 < 180 ? 0 : 180) + ")";
// });
label.width (function (node) {
var svg = d3.select("body")
.append("svg")
.attr("height", 0)
.style('visibility', 'hidden');
var text = svg
.append("text")
.style('font-size', d3.functor(label.fontsize())(node) + "px")
.text(label.text()(node));
var width = text.node().getBBox().width;
svg.remove();
return width;
});
label.height (function (node) {
return d3.functor(label.fontsize())(node);
});
return label;
};
// Image based labels
tree.label.img = function () {
var label = tree.label();
var api = apijs (label)
.getset ('src', function () {})
label.display (function (node, layout_type) {
if (label.src()(node)) {
var l = d3.select(this)
.append("image")
.attr("width", label.width()())
.attr("height", label.height()())
.attr("xlink:href", label.src()(node));
return l;
}
// fallback text in case the img is not found?
return d3.select(this)
.append("text")
.text("");
});
label.transform (function (node, layout_type) {
var d = node.data();
var t = {
translate : [10, (-label.height()() / 2)],
rotate : 0
};
if (layout_type === 'radial') {
t.translate[0] = t.translate[0] + (d.x%360 < 180 ? 0 : label.width()()),
t.translate[1] = t.translate[1] + (d.x%360 < 180 ? 0 : label.height()()),
t.rotate = (d.x%360 < 180 ? 0 : 180)
}
return t;
});
return label;
};
// Labels made of 2+ simple labels
tree.label.composite = function () {
var labels = [];
var label = function (node, layout_type, node_size) {
var curr_xoffset = 0;
for (var i=0; i<labels.length; i++) {
var display = labels[i];
(function (offset) {
display.transform (function (node, layout_type) {
var tsuper = display._super_.transform()(node, layout_type);
var t = {
translate : [offset + tsuper.translate[0], tsuper.translate[1]],
rotate : tsuper.rotate
};
return t;
})
})(curr_xoffset);
curr_xoffset += 10;
curr_xoffset += display.width()(node);
display.call(this, node, layout_type, node_size);
}
};
var api = apijs (label)
api.method ('add_label', function (display, node) {
display._super_ = {};
apijs (display._super_)
.get ('transform', display.transform());
labels.push(display);
return label;
});
api.method ('width', function () {
return function (node) {
var tot_width = 0;
for (var i=0; i<labels.length; i++) {
tot_width += parseInt(labels[i].width()(node));
tot_width += parseInt(labels[i]._super_.transform()(node).translate[0]);
}
return tot_width;
}
});
api.method ('height', function () {
return function (node) {
var max_height = 0;
for (var i=0; i<labels.length; i++) {
var curr_height = labels[i].height()(node);
if ( curr_height > max_height) {
max_height = curr_height;
}
}
return max_height;
}
});
return label;
};
module.exports = exports = tree.label;