Я работаю над визуализацией тегов, где теги переходят между различными сило-направленными макетами.
У меня было немного проблем с выяснением того, как перейти от пузырьковой диаграммы к диаграмме узлов, но я немного застрял в том, как заставить диаграммы перейти в облако слов. Мои трудности во многом связаны с моей неопытностью в написании пользовательских функций кластеризации/обнаружения столкновений.
Я объявляю силы глобальными, а затем останавливаюсь и запускаю их, когда пользователь нажимает кнопку:
var force1 = d3.layout.force()
.size([width, height])
.charge(0)
.gravity(0.02)
.on("tick", ticka);
//layout for node chart
var force2 = d3.layout.force()
.size([width, height])
.charge(-50)
.gravity(0.005)
.linkDistance(120)
.on("tick", tickb);
//layout for bubble chart
var force3 = d3.layout.force()
.size([width, height])
.charge(0)
.gravity(0.02)
.on("tick", tickc);
Соответствующие функции узлов/связей добавляются в силу, когда вызывается функция, которая рисует узлы (когда данные изменяются в соответствии со значением ползунка).
Код для создания данных узла выглядит следующим образом:
nodes = splicedCounts.map(function(d, e) {
var choice;
var i = 0,
r = d[1],
d = { count: d[1],
sentiment: d[2]/d[1],
cluster: i,
radius: radScale(r),
name: d[0],
index: e,
x: Math.cos(i / m * 2 * Math.PI) * 200 + width / 2 + Math.random(),
y: Math.sin(i / m * 2 * Math.PI) * 200 + height / 2 + Math.random()
};
if (!clusters[i] || (r > clusters[i].radius))
clusters[i] = d;
return d;
});
Чтобы этот вопрос был относительно коротким, код, который я использую для рисования пузырьковой диаграммы, является производным от этого примера: http://bl.ocks.org/mbostock/7881887, а код для рисования диаграммы узлов аналогичным образом является общим (я Я рад предоставить этот код, если он поможет решить мою проблему).
Это где моя проблема приходит:
Я нашел этот хороший пример для обнаружения столкновений между прямоугольниками и включил его в мой код. Однако, поскольку я использую текст SVG и размер шрифта изменяется при переходе, я решил оценить размер текста/размер ограничивающего прямоугольника на основе длины текста и радиуса.
Все функции "галочки" для таблицы слов приведены ниже.
function tickc(e) {
node = nodeGroup.selectAll(".node");
var nodeText = nodeGroup.selectAll(".node text");
node.each(cluster(5 * e.alpha * e.alpha));
var k = e.alpha;
nodeText.each(function(a, i) {
var compWidth = d3.select(this).attr("bWidth");
var compHeight = d3.select(this).attr("bHeight");
nodes.slice(i + 1).forEach(function(b) {
// console.log(a);
var lineWidthA = a["name"].length * a["radius"]/2.5;
var lineHeightA = a["radius"]/0.9;
var lineWidthB = b["name"].length * b["radius"]/2.5;
var lineHeightB = b["radius"]/0.9;
dx = (a.x - b.x)
dy = (a.y - b.y)
adx = Math.abs(dx)
ady = Math.abs(dy)
mdx = (1 + 0.07) * (lineWidthA + lineWidthB)/2
mdy = (1 + 0.07) * (lineHeightA + lineHeightB)/2
if (adx < mdx && ady < mdy) {
l = Math.sqrt(dx * dx + dy * dy)
lx = (adx - mdx) / l * k
ly = (ady - mdy) / l * k
// choose the direction with less overlap
if (lx > ly && ly > 0)
lx = 0;
else if (ly > lx && lx > 0)
ly = 0;
dx *= lx
dy *= ly
a.x -= dx
a.y -= dy
b.x += dx
b.y += dy
}
});
});
node.select("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
node.select("text")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
// Move d to be adjacent to the cluster node.
function cluster2(alpha) {
return function(d) {
var cluster = clusters[d.cluster];
if (cluster === d) return;
var x = d.x - cluster.x,
y = d.y - cluster.y,
l = Math.sqrt(x * x + y * y),
r = (d["name"].length * d["radius"]) + (cluster["name"].length * cluster["radius"]);
};
}
Я не был уверен, как завершить функцию кластеризации, чтобы правильно переместить узлы. Я попытался адаптировать стандартную функцию кластера, т.е.
// Move d to be adjacent to the cluster node.
function cluster(alpha) {
return function(d) {
var cluster = clusters[d.cluster];
if (cluster === d) return;
var x = d.x - cluster.x,
y = d.y - cluster.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + cluster.radius;
if (l != r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
cluster.x += x;
cluster.y += y;
}
};
}
чтобы быть более похожим на вышеупомянутую раскладку силы прямоугольного кластера, но без удачи (боюсь, у меня больше нет копий моих точных попыток).
Боюсь, что я не могу прикрепить изображения из-за отсутствия репутации, но я могу попытаться найти способ предоставить их, если это поможет. Проблема перекрытия с облаком слов незначительна (большинство слов разрешается в соседние, но не соприкасающиеся позиции), но, если возможно, я бы хотел, чтобы оно решалось так же идеально, как пузырьковая диаграмма. Я почти уверен, что эти проблемы возникли из-за а) незавершенной функции кластера и б) моего взлома при использовании длины и радиуса текста для оценки размера текста, а не в правильных координатах ограничивающего прямоугольника, но я не уверен, как именно это исправить эти вещи.