Подтвердить что ты не робот

Успокойте начальный тик силовой компоновки

Я только начал заниматься d3 и нашел, что кривая обучения довольно крутая. Процесс полностью отличается от того, к чему я привык, и математика в основном проходит над моей головой.

В любом случае, мой проект состоит из макета силы, представляющего карту интеграции между системами. Эта часть работает очень хорошо, но у меня есть одна серьезная проблема, которая также представлена ​​в демонстрации силовой направленной макеты на сайте Майкла Бостокса: Когда узлы инициируются, они, похоже, отображаются на холсте. После этого происходит серьезная физическая математика, имитирующая гравитационное притяжение, которое направляет узлы на довольно запутанный путь назад и вперед, пока они не успокоятся и не осядут на некоторые случайные координаты. Несмотря на то, что в первый раз, когда демонстрация запускается, когда вы пытаетесь просмотреть статус сетевых интерфейсов от компании, они перемещаются в обратном порядке, и серверы просто остаются неподвижными, через некоторое время становится утомительно.

Я уверен, что у меня есть правильная настройка макета для этого проекта, потому что я хочу, чтобы серверы автоматически выходили из системы, я хочу визуализировать связи между ними. Я, однако, неоднозначен в отношении гравитационного эффекта.

Интересно; можно ли вручную установить начальное положение каждого node, чтобы я мог поместить их ближе к гравитационному центру и немного сократить время отскока?

4b9b3361

Ответ 1

Все вышеперечисленные ответы неверно поняли вопрос Ойстеина Амундсена.

Единственный способ стабилизации силы при запуске - установить node.x и node.y правильное значение. Обратите внимание, что node - это данные d3.js, а не тип DOM.

Например, если вы загружаете

nodes = [{"id": a}, {"id": b}, {"id": c}]

в

d3.layout.force().nodes(nodes)

вам нужно установить все .x и .y всех элементов в массиве узлов это будет так (в coffeescript)

nodes = [{"id": a}, {"id": b}, {"id": c}]
for i in [0..2]
  nodes[i].x = 500 #the initial x position of node
  nodes[i].y = 300 #the initial y position of node
d3.layout.force().nodes(nodes).links(links)

то узлы будут начинаться в позиции, когда force.start(). это позволит избежать хаоса.

Ответ 2

Внутренне, при "нормальном" использовании макет силы повторно вызывает свой собственный метод tick() асинхронно (через setInterval или requestAnimationFrame), пока макет не решит решение. В этот момент его значение alpha() равно или приближается к 0.

Итак, если вы хотите "ускорить перемотку вперед" через этот процесс решения, вы можете синхронно вызывать этот метод tick() снова и снова до тех пор, пока макет альфа не достигнет значения, которое для ваших конкретных требований составляет "достаточно близко", решение. Например:

var force = d3.layout.force(),
    safety = 0;
while(force.alpha() > 0.05) { // You'll want to try out different, "small" values for this
    force.tick();
    if(safety++ > 500) {
      break;// Avoids infinite looping in case this solution was a bad idea
    }
}

if(safety < 500) {
  console.log('success??');
}

После выполнения этого кода вы можете нарисовать свой макет в зависимости от состояния узлов. Или, если вы рисуете свой макет, привязывая к событию галочки (т.е. force.on('tick', drawMyLayout)), вы захотите выполнить привязку после, этот код запускается, потому что в противном случае вы будете бесполезно отображать компоновать сотни раз синхронно во время цикла while.

JohnS сводил этот подход к одной краткой функции. См. Его ответ где-то на этой странице.

Ответ 3

Я имел дело с чем-то вроде этого некоторое время назад. Есть несколько вещей, которые следует учитывать.

1) Итерирующие клещи моделируют систему, которая приходит в равновесие. Таким образом, нет способа избежать обращения к тику столько раз, сколько необходимо, прежде чем система опустится, и у вас будет автоматическая компоновка. Тем не менее, вам не нужно обновлять визуализацию каждого тика, чтобы симуляция работала! Итерации будут намного быстрее, на самом деле, если вы этого не сделаете. Соответствующая часть моего кода:

var iters = 600; // You can get decent results from 300 if you are pressed for time
var thresh = 0.001;
if(!hasCachedLayout || optionsChanged || minorOptionsChanged) {
    force.start(); // Defaults to alpha = 0.1
    if(hasCachedLayout) {
        force.alpha(optionsChanged ? 0.1 : 0.01);
    }
    for (var i = iters; i > 0; --i) {
        force.tick();
        if(force.alpha() < thresh) {
            //console.log("Reached " + force.alpha() + " for " + data.nodes.length + " node chart after " + (iters - i) + " ticks.");
            break;
        }
    }
    force.stop();
}

Это выполняется синхронно, и после его запуска я создаю элементы dom для всех узлов и ссылок. Для небольших графиков это выполняется довольно быстро, но вы обнаружите, что существует задержка для больших графиков (более 100 узлов) - они просто намного дороже вычислить.

2) Вы можете кэшировать/размещать макеты. Макет силы будет равномерно распределять узлы при инициализации, если никакая позиция не установлена! Поэтому, если вы убедитесь, что ваши узлы установили атрибуты x и y, они будут использоваться. В частности, когда я обновляю существующий график, я буду повторно использовать позиции x и y из предыдущего макета.

Вы найдете хороший начальный макет, вам потребуется намного меньше итераций, чтобы достичь стабильной конфигурации. (Это то, что имеет дорожки hasCachedLayout в коде выше). NB: Если вы повторно используете одни и те же узлы с одинаковой компоновкой, вам также необходимо будет установить px и py в NaN или вы получите странные результаты.

Ответ 4

Основываясь на других ответах, я сделал этот метод:

function forwardAlpha(layout, alpha, max) {
  alpha = alpha || 0;
  max = max || 1000;
  var i = 0;
  while(layout.alpha() > alpha && i++ < max) layout.tick();
}

Ответ 5

Может быть, force.friction(0.5) или какое-то другое число, меньшее, чем значение по умолчанию 0.9, поможет? По крайней мере, это дает менее хаотичное впечатление при загрузке страницы.

Ответ 6

	var width = 960,
	  height = 500;

	var fill = d3.scale.category20();

	var force = d3.layout.force()
	  .size([width, height])
	  .nodes([{}]) // initialize with a single node
	  .linkDistance(30)
	  .charge(-60)
	  .on("tick", tick);

	var svg = d3.select("body").append("svg")
	  .attr("width", width)
	  .attr("height", height)
	  .on("mousedown", mousedown);

	svg.append("rect")
	  .attr("width", width)
	  .attr("height", height);

	var nodes = force.nodes(),
	  links = force.links(),
	  node = svg.selectAll(".node"),
	  link = svg.selectAll(".link");

	 // var cursor = svg.append("circle")
	 //     .attr("r", 30)
	 //     .attr("transform", "translate(-100,-100)")
	 //     .attr("class", "cursor");

	restart();

	function mousedown() {
	  var point = d3.mouse(this),
	    node = {
	      x: width / 2,
	      y: height / 2,
	      "number": Math.floor(Math.random() * 100)
	    },
	    n = nodes.push(node);

	  // add links to any nearby nodes
	  /*  nodes.forEach(function(target) {
		    var x = target.x - node.x,
		        y = target.y - node.y;
		    if (Math.sqrt(x * x + y * y) < 30) {
		      links.push({source: node, target: target});
		    }
		  });
		*/
	  restart();
	}

	function tick() {
	  link.attr("x1", function(d) {
	      return d.source.x;
	    })
	    .attr("y1", function(d) {
	      return d.source.y;
	    })
	    .attr("x2", function(d) {
	      return d.target.x;
	    })
	    .attr("y2", function(d) {
	      return d.target.y;
	    });

	  node.attr("transform", function(d) {
	    return "translate(" + d.x + "," + d.y + ")";
	  });
	}

	function restart() {
	  link = link.data(links);

	  link.enter().insert("line", ".node")
	    .attr("class", "link");

	  node = node.data(nodes);

	  // node.enter().insert("circle", ".cursor")
	  //     .attr("class", "node")
	  //     .attr("r", 5)
	  //     .call(force.drag);

	  var nodeEnter = node.enter().insert("svg:g", ".cursor")
	    .attr("class", "node")
	    .call(force.drag);

	  nodeEnter.append("svg:circle")
	    .attr("r", 5)

	  nodeEnter.append("svg:text")
	    .attr("class", "textClass")
	    .attr("x", 14)
	    .attr("y", ".31em")
	    .text(function(d) {
	      return d.number;
	    });

	  force.start();
	}
	rect {
	  fill: none;
	  pointer-events: all;
	}
	.node {
	  fill: #000;
	}
	.cursor {
	  fill: none;
	  stroke: brown;
	  pointer-events: none;
	}
	.link {
	  stroke: #999;
	}
	.textClass {
	  stroke: #323232;
	  font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
	  font-weight: normal;
	  stroke-width: .5;
	  font-size: 14px;
	}
	
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>