Uspokojenie początkowego kleszcza układu sił

Właśnie zacząłem parać się d3 i uważam, że krzywa uczenia się jest dość stroma. Proces jest zupełnie inny niż to, do czego jestem przyzwyczajony, a matematyka jest w większości ponad moją głową.

W każdym razie mój projekt składa się z układu sił przedstawiającego mapę integracji między systemami. Ta część działa niesamowicie dobrze, ale mam jeden poważny problem, który jest również reprezentowany w demo układu Force directed na stronie Michaela Bostocksa: Kiedy węzły są inicjowane, wydają się być renderowane z płótna. Po tym, niektóre poważne Matematyki Fizyki przejmuje kontrolę, symulując przyciąganie grawitacyjne, które wysyła węzły na dość mylące ścieżki tam iz powrotem, aż się uspokoić i osiedlić się na niektórych przypadkowych współrzędnych. Chociaż ruchy te są fajne za pierwszym razem, gdy uruchamiane jest demo, kiedy próbujesz zobaczyć status interfejsów sieciowych z punktu widzenia administratorów it firmy, a serwery po prostu nie pozostaną nieruchome, po pewnym czasie staje się to męczące.

Jestem pewien Mam poprawną konfigurację układu dla tego projektu, ponieważ chcę, aby serwery autolayout, chcę wizualizować połączenia między nimi. Jestem jednak ambiwalentny w odniesieniu do efektu grawitacji.

Zastanawiam się; czy jest możliwe, aby ustawić początkową pozycję każdego węzła ręcznie, tak, że mogę umieścić je bliżej centrum grawitacyjnego i skrócić" czas odbicia " trochę?

Author: VividD, 2012-11-20

6 answers

Wszystkie powyższe odpowiedzi zostały źle zrozumiane przez øysteina Amundsena.

Jedynym sposobem na ustabilizowanie siły na starcie jest ustawienie węzła.x i node.y odpowiednią wartość. Należy pamiętać, że węzeł jest danymi d3.js, a nie reprezentowany Typ DOM.

Na przykład, jeśli załadujesz

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

Do

d3.layout.force().nodes(nodes)
Musisz ustawić wszystko .x i .y wszystkich elementów w tablicy węzłów będzie tak ( w 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)

Wtedy węzły zaczną się od pozycji kiedy siła.start(). to pozwoli uniknąć chaosu.

 32
Author: chh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-04 08:14:29

Wewnętrznie, w "normalnym" użyciu, układ siły wielokrotnie wywołuje własną metodę tick() asynchronicznie (poprzez setInterval lub requestAnimationFrame), dopóki układ nie osiądzie na rozwiązaniu. W tym momencie jego alpha() wartość jest równa lub zbliżona do 0.

Więc, jeśli chcesz "przewijać do przodu" przez ten proces rozwiązania, możesz synchronicznie wywoływać tę metodę tick() w kółko, aż Alfa układu osiągnie wartość, która dla Twoich konkretnych wymagań stanowi rozwiązanie "wystarczająco blisko". Jak więc:

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??');
}

Po uruchomieniu tego kodu możesz narysować swój układ na podstawie stanu węzłów. Lub, jeśli rysujesz swój układ przez powiązanie ze zdarzeniem tick (ie force.on('tick', drawMyLayout)), będziesz chciał wykonać powiązanie Po uruchomieniu ten kod, ponieważ w przeciwnym razie niepotrzebnie wyrenderujesz układ setki razy synchronicznie podczas pętli while.

JohnS zredukował to podejście do jednej zwięzłej funkcji. Zobacz jego odpowiedź gdzieś na tej stronie.

 30
Author: meetamit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-06 22:43:51

Jakiś czas temu miałem do czynienia z czymś takim. Jest kilka rzeczy do rozważenia.

1) kleszcze iteracyjne symulują układ, który dochodzi do równowagi. Nie ma więc sposobu, aby uniknąć wywołania tick tyle razy, ile potrzeba, zanim system się ustawi i masz swój automatyczny układ. To powiedziawszy, nie musisz aktualizować wizualizacji za każdym razem, aby symulacja działała! Iteracja pójdzie dużo szybciej, w rzeczywistości, jeśli nie. odpowiednia część mojego kodu idzie:

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();
}

To działa synchronicznie i po jego uruchomieniu tworzę elementy dom dla wszystkich węzłów i linków. W przypadku małych Wykresów działa to dość szybko, ale okaże się, że jest opóźnienie dla większych Wykresów (100+ węzłów) - są one po prostu znacznie bardziej kosztowne obliczeniowo.

2) możesz buforować / seed layouts. Układ sił będzie równomiernie rozdzielał węzły po inicjalizacji , jeśli nie jest ustawiona pozycja! więc jeśli upewnisz się, że twoje węzły mają ustawione X i y atrybuty te zostaną wykorzystane. W szczególności, gdy aktualizuję istniejący Wykres, ponownie użyję pozycji x i y z poprzedniego układu.

Przy dobrym początkowym układzie będziesz potrzebował dużo mniej iteracji, aby osiągnąć stabilną konfigurację. (To właśnie hasCachedLayout śledzi w powyższym kodzie). UWAGA: Jeśli ponownie używasz tych samych węzłów, które tworzą ten sam układ, musisz również upewnić się, że ustawisz px i py na NaN, lub otrzymasz dziwne wyniki.

 11
Author: Superboggly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-21 14:40:11

Na podstawie innych odpowiedzi zrobiłem tę metodę:

function forwardAlpha(layout, alpha, max) {
  alpha = alpha || 0;
  max = max || 1000;
  var i = 0;
  while(layout.alpha() > alpha && i++ < max) layout.tick();
}
 8
Author: Ali Shakiba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-03 03:16:16

Może force.friction(0.5), lub jakaś inna liczba niższa niż domyślna 0.9, pomoże? Przynajmniej daje mniej chaotyczne wrażenie po załadowaniu strony.

 4
Author: Anton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-18 09:22:00

	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>
Przykład tego, czego możesz szukać. Ustawia atrybuty x i y nowych węzłów przed wstawieniem ich do układu. Żądaną lokalizacją jest środek elementu svg.
 0
Author: timebandit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-29 01:22:09