Code Monkey home page Code Monkey logo

d3-force's Introduction

d3-force's People

Contributors

curran avatar esjewett avatar fil avatar jheer avatar jlouzado avatar jonyrock avatar mbostock avatar micahstubbs avatar nathanielw avatar oluckyman avatar steveharoz avatar stof avatar tmcw avatar vasturiano avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d3-force's Issues

clarifying d3-force documentation

I believe, for clarity the d3-force documentation...

  • "mutally" should be "mutually" in the sentence, "The many-body (or n-body) force applies mutally amongst all nodes."
  • "number in [0,1]" should be "number in the range [0,1]" (6 instances).

The simulation should initialize positions and velocities.

If it is doesn’t, they are NaN, and bad things happen.

One tricky thing is that the simulation isn’t told the bounds of the display. But maybe that’s not necessary? The simulation could center nodes around the origin, and then you could apply a translate when rendering.

More composable forces, such as charge colors.

Hey there,
first of all, HUGE kudos for D3. It's amazing.

Intro: I just finished my v2 of AssetGraphs = visualizing $NXT blockchain data, the assets around a NXT address. I am using a -heavily modified- version of Force-Directed Graph to show the output of my graph building scripts.

I am superthankful for your work, and I think my first examples are looking great. But now I am running into the -expected- challenges of displaying larger and larger networks.

Data: The structure of my data is bipartite. As nodes I have FEW assets, and MANY shareholders (address == shareholder) ... and all links are always only between partitions, i.e. a shareholder is linked to all the assets he is holding.

Problem: Usually a lot of shareholders are holding the same assets, so I get densely knitted clusters. Which condenses some of those assets into ugly blobs.

A lot of tweaking of the force parameters does often get mildly satisfying configurations, but ... I see a more general solution:

Idea: I would like to have two force algorithm parameter-subsets, one for each partition. Not all force parameters need to be partition specific, but 'charge' seems to be the most important to split. Like this:

  • The shareholders shall have less charge to each other.
  • The assets shall have more charge to each other. <-- that is the essential thing, I guess.
  • Across partition, I don't know yet. A third charge parameter, to fiddle with.

I guess there is a already some solution that has implemented this?

Thx

Calling simulation.tick after alpha < alphaMin?

Currently simulation.tick is a no-op if the current alpha is less than alphaMin. That’s a little weird, especially since there’s no easy way to check if the current alpha is less than alphaMin. So… some options:

  1. Calling simulation.tick could throw an Error if alpha is less than alphaMin. But that’s a little bogus since you’d have to catch the Error.
  2. Calling simulation.tick could return true if alpha >= alphaMin, and false if alpha < alphaMin, making it easy to terminate a loop.
  3. We could introduce a new method to check whether alpha < alphaMin.
  4. Calling simulation.tick could tick regardless of whether alpha >= alphaMin. Only the internal timer (started by simulation.start) would stop when alpha >= alphaMin.

Probably doing both 2 and 4 would be good.

Restart the simulation without 100% heat.

Currently, simulation.restart sets iteration to zero, thereby setting alpha to one. There’s no way to set the maximum alpha or restart to a slightly later iteration, but I think we’ll need something like this for interactive dragging (#22).

Drag behavior.

I should implement d3-drag and then see about integrating it with d3-force and make sure this API is sufficient.

Automatic scaling to fit the viewport.

I recall implementing this in the past, but I forgot why I didn’t include it. It seems like it would be a nice thing, perhaps in conjunction with d3.forceCenter. Probably it should be specified as a radius; possibly maximum and minimum radii (as measured from the center of mass).

Should forces be parameterized per node?

In the new design, you pass each force the array of nodes (and in some cases links):

var simulation = d3.forceSimulation(nodes)
    .on("beforetick.charge", d3.forceManyBody(nodes))
    .on("beforetick.link", d3.forceLink(nodes, links))
    .on("beforetick.position", d3.forcePosition(nodes).position([width / 2, height / 2]))
    .on("tick", draw);

This has the benefit that you can give each force a different subset of the nodes, but it raises some questions:

  1. What if I want each node’s forces to have different parameters? Do I need to register separate forces for each node? Or should the force be parameterizable, e.g., forceLink.strength takes a function that is evaluated for each link?
  2. If the force is parameterizable, does that mean the parameters are re-evaluated when the array of nodes (or links) changes, or are the parameters evaluated every time the force is invoked? Is it worth trying to optimize the common case of constant-value forces?
  3. If forces are parameterizable, why do we also need to specify the array of nodes to each force—couldn’t they just always use the array of nodes associated with the simulation, and you could define the strength as zero if you didn’t want the force to apply to those nodes?
  4. If forces do always use the simulation’s array of nodes, should the simulation also keep track of the links? (Probably not, since I think links only need to be known by d3.forceLink, whereas multiple parties care about nodes?)

simulation.size()

We used this function to handle window resize in v3 (so the graph shifts into view)

I had a look through the CHANGES document but couldn't find any information relating to why this was removed/how to achieve something similar in v4.

Creating a transition before stopping the layout prevents it from restarting

Creating an unrelated transition before calling layout.stop() has the surprising effect that subsequent calls to layout.restart() have no effect.

See the test case at https://bl.ocks.org/robinhouston/27dd39ede1186d4a7e3e12c16f482b41: if you press the “Transition & stop” button while the layout is still ticking, then the “Restart” button does not restart it.

It’s possible this is actually a bug in d3-timer, since the most obvious connection between layouts and transitions is that both use timers internally.

reproduce v3 simulation under v4

With v3 I created a an application with more intricate cloth than the lattice example. I fail to reproduce the behavior of v3 under v4. Under v4 I only manage to inflate or deflate the graphs or create a complete mess. The links no longer behave like rubber bands. The nodes hardly rearrange themselves unless they collide. Demos of the v3 implementation and v4 attempt. The panel below the two D3 diagrams show the initial state of the left diagram. One line in the left diagram represents two lines in the right diagram. Both diagrams are generated with the same script though the links have different representations. Colored end-markers (and occasional mid-markers) in the left diagram. White end markers in the right diagram are implemented by drawing the links slightly shorter.

The configuration of the v3 d3.layout.force() are shown above this code comment. Below the message an attempt to configure the v4 d3.forceSimulation() and another message pointing to other attempts.

I'm afraid I have too little math background to understand the influence of all the parameters and which I'm missing. Any hints to restore the rubber-band like behavior of the links would be very much appreciated.

Force based graph not loading in ie9

I tried to open force directed graph based blocks in http://bl.ocks.org/mbostock in ie9.

Getting this error:
SCRIPT438: Object doesn't support property or method 'now'
599201e28bf175cc71262294fe033c7372795a0b, line 12 character 1

Getting the same error with my graph created using d3 v4 rc2.

Tree and other layouts seems to be working fine. Also force layouts based on d3 v3 working fine.

Timer.restart calls sleep without argument

restart calls sleep() without argument - not sure this is intended.

Timer.prototype = timer.prototype = {
    constructor: Timer,
    restart: function(callback, delay, time) {
      if (typeof callback !== "function") throw new TypeError("callback is not a function");
      time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
      if (!this._next && taskTail !== this) {
        if (taskTail) taskTail._next = this;
        else taskHead = this;
        taskTail = this;
      }
      this._call = callback;
      this._time = time;
      sleep(); // <<<<<<<<<<<<<<<<<<<<<<<<<< sleep(this._time) ????
    },

Resolve collisions independently, in parallel?

As an alternative to #24, I should consider resolving collisions independently: aggregate the forces for each overlapping node, and then apply them at the end after determining all the overlaps. I did this before looking at the previous position ⟨x,y⟩ and it was too soft, but maybe it would be sufficient when looking at the next position ⟨x + vx,y + vy⟩?

Also if we do this, the forces should be divided in half, since they will apply symmetrically to each pair of overlapping nodes. And it means we don’t have to mutate the quadtree as we iterate over the nodes, and we could probably compare indexes i < j to determine whether we need to test two nodes for overlap.

Alpha affects linkForce differently than the other forces

Hi, I've been working on a project and noticed some weird behaviors around changes in alpha (namely due to alphaDecay) and the strength of links vs other forces; the lower the alpha, the weaker any linkForce is. I've set up a demo to show what I'm talking about.

In the demo, alpha decays as default and then resets to 1 once it reaches a threshold. This causes a "pulsating" behavior, where the graph expands as the links get weaker and then contracts once alpha is 1 and they're at full effect again. This happens regardless of whether I set my own strength function.

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

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

var simulation = d3.forceSimulation()
    .force("x", d3.forceX(200))
    .force("y", d3.forceY(200))
    .force("many", d3.forceManyBody())
    .force("link", d3.forceLink())
    ;

simulation.force("many").strength(-400);

// It's more extreme with more iterations (and obviously higher strength)
// simulation.force("link").iterations(5);

simulation.force("link").distance(50);

mydata = [Object(1), Object(2), Object(3), 
          Object(1), Object(2), Object(3),
          Object(1), Object(2), Object(3),
          Object(1), Object(2), Object(3),
          Object(1), Object(2), Object(3),
          Object(1), Object(2), Object(3)];

var balls = svg.selectAll("circle")
    .data(mydata)

    .enter().append("circle")
    .attr("r", 5)
    ;

//Here I just connect every ball to every other ball
links = []
balls.data().forEach( function (ball) {
    balls.data().forEach( function (ballbud) {
        if (ballbud != ball) {
            links.push({source: ball, target: ballbud});
        }
    });
});

simulation.nodes(balls.data());
simulation.force("link").links(links);

function update(){

    // Here's where I mess with the alpha -------------------------------------
    if (simulation.alpha() < .02) { 
        simulation.alpha(1);
    }

    balls
        .attr("cx", function (d) {return d.x; })
        .attr("cy", function (d) {return d.y; })
        ;
}
simulation.on("tick", update);

</script>

I've tested all 3 of these forces (position, manybody, and link) against eachother in pairs and it's clearly the case that only link is affected; the "pulsating" behavior happens when only link and another of the forces are active, but not when the linkforce is disabled. It's also more evident that only link is affected in my actual project, where the graph is a lot more complex and populated. You can also just set alphaDecay to 0 and manually play with alpha, the result is the same.

Strangely the behavior seems different on one of Mike's examples: https://bl.ocks.org/mbostock/4600693
When you hold down the mouse on a node, everything kinda drifts apart (when I'd expect it to contract)

Hopefully this is helpful! I'm pretty new to d3 (huge fan so far) and JS in general (less of a fan so far); let me know if something in this snippet reflects some technical or stylistic misunderstandings. I'll poke around in the source and see if I can figure anything out.

simulation.fix missing

Hi,

I'm including the following :

<script src="https://d3js.org/d3-collection.v1.min.js"></script> <script src="https://d3js.org/d3-dispatch.v1.min.js"></script> <script src="https://d3js.org/d3-quadtree.v1.min.js"></script> <script src="https://d3js.org/d3-timer.v1.min.js"></script> <script src="https://d3js.org/d3-force.v1.min.js"></script>

When I try to perform a simulation.fix / unfix, the function is not found.

What am I missing ?

Best

The position constraint should be parameterized per-node.

This would make it easier to apply dynamic position constraints.

Should the position (focus) be re-evaluated at every tick, rather than just at initialization time, though? That would enable this sort of thing:

screen shot 2016-04-20 at 10 45 59 am

screen shot 2016-04-20 at 10 45 53 am

It might be worth optimizing if the position is a constant, then?

Node indices not resolved to objects in links

It seems that d3.forceLink does not replace indices in links (source and target) with references to the corresponding nodes. The documentation in #link_id states that this should be the expected behavior, and I noticed you implemented this in the code, but the behavior is different as of d3.v4.0.0-alpha.40 (or perhaps I'm misusing the API?).

Here's an example in JSBin that shows this behavior, also pasted here:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Force Test</title>
    <script src="https://d3js.org/d3.v4.0.0-alpha.40.min.js"></script>
  </head>
  <body>
    <script>

      var graph = {
        "nodes": ["a", "b", "c"],
        "links": [
          { "source": 0, "target": 1 },
          { "source": 1, "target": 2 }
        ]
      };

      var simulation = d3.forceSimulation(graph.nodes)
        .force("link", d3.forceLink(graph.links));

    </script>
  </body>
</html>

The error manifests as

d3.v4.0.0-alpha.40.min.js:4 Uncaught TypeError: Cannot create property 'vx' on string 'a'

Thanks for all your work on D3 4.0, I'm having lots of fun trying out the beta!

Should forces just be listeners?

It seems weird to just piggyback on dispatch.on:

var simulation = d3.forceSimulation(nodes)
    .on("beforetick.charge", d3.forceManyBody(nodes))
    .on("beforetick.link", d3.forceLink(nodes, links))
    .on("beforetick.position", d3.forcePosition(nodes).position([width / 2, height / 2]))
    .on("tick", draw);

Maybe the simulation should track named forces, allowing us to develop a more specific interface for forces?

var simulation = d3.forceSimulation(nodes)
    .force("charge", d3.forceManyBody(nodes))
    .force("link", d3.forceLink(nodes, links))
    .force("position", d3.forcePosition(nodes).position([width / 2, height / 2]))
    .on("tick", draw);

Or, if per #8 forces inherit nodes from the simulation:

var simulation = d3.forceSimulation(nodes)
    .force("charge", d3.forceManyBody)
    .force("link", d3.forceLink(links))
    .force("position", d3.forcePosition.position([width / 2, height / 2]))
    .on("tick", draw);

And if per #7 forces are mutable:

var simulation = d3.forceSimulation(nodes)
    .force("charge", d3.forceManyBody())
    .force("link", d3.forceLink(links))
    .force("position", d3.forcePosition().position([width / 2, height / 2]))
    .on("tick", draw);

Related: it’s a little annoying to give each force a name.

Consider a `.forces()` method that gets/sets the forces map

While building a react component that renders a force layout chart I'm finding that my work would be easier if there was a method on the simulation that allowed me to get all forces on the simulation, so that I can compare to props passed to the component when the props change. It would also be useful to be able to set all the forces simultaneously, but the hard one to do outside of d3-force is getter, because without there's no way of knowing what forces are currently assigned to the simulation.
Would you consider adding such a method?
I'd be happy to send a PR if yes.
Thanks either way,
Nuno

Should forces be mutable?

I’ve modeled forces after curves, easing functions and interpolators, but is it weird that forceLink.strength doesn’t modify the force’s strength, instead returning a new force with the specified strength? So, if you want to change the strength, you need to do something like this:

simulation.on("beforetick.link", simulation.on("beforetick.link").strength(0.2));

That’s… awkward.

Initialize node positions and velocities?

It’s annoying that in the new design you have to initialize the nodes positions manually:

nodes.forEach(function(d) {
  d.x = Math.random() * width;
  d.y = Math.random() * height;
  d.vx = d.vy = 0;
});

Also it means the initial positions don’t account for topology. And what about when nodes are later added to the graph?

d3-force-drag?

I feel myself doing this a lot with d3-force + d3-drag, and it would be nice to encapsulate as a reusable module:

node.call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x, d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x, d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null, d.fy = null;
}

One gotcha is that the pattern is slightly more complicated for Canvas since you need to use simulation.find to pick a node. Another gotcha is that I don’t want to introduce a dependency from d3-force to d3-drag (or vice versa), so this would need to live in a new module d3-force-drag, presumably.

Adding color to nodes depending on node group

How can I add color to nodes depending on the group a node belongs to?
I've tried a few things now relating to context.fillStyle but it usually doesn't color the nodes properly and creates crazy lag in the graphic.

Could someone point me in the right direction regarding coloring nodes in force graphs in D3.v4.

Note: I can do this in D3.v3, that's easy, I just don't know how to do it in v4

manyBody.js: calculate the change of node velocity

var x = quad.x - node.x,
    y = quad.y - node.y,
    w = x2 - x1,
    l = x * x + y * y;

if (w * w / theta2 < l) {
  if (l < distanceMax2) {
    if (x === 0) x = jiggle(), l += x * x;
    if (y === 0) y = jiggle(), l += y * y;
    if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
    node.vx += x * quad.value * alpha / l;
    node.vy += y * quad.value * alpha / l;
  }
  return true;
}

Sorry, I couldn't understand why l = x * x + y * y rather than l = Math.sqrt(x * x + y * y), so I couldn't know the principle of node.vx += x * quad.value * alpha / l. Could you explain to me?

d3.forceFix?

A temporary force for fixing nodes during drag. Something like this:

d3.forceFix = function(node, x, y) {
  x = x == null ? d3.event.x : +x;
  y = y == null ? d3.event.y : +y;
  return function() {
    node.x = x, node.y = y, node.vx = node.vy = 0;
  };
};

Why center doesn't use alpha or velocity?

I just migrated to v4 but I can't manage to get the same behavior as before and so far the culprit seems to be the center force.

It moves my points very abruptly, looking at the code it seems odd that it doesn't use velocity or alpha to do the ticks. Is this intended?

Collision resolution should process nodes from smallest-to-largest.

Currently it processes nodes in input order, which I believe makes it especially bad in the common case where nodes are ordered from largest-to-smallest. Since d3.forceCollide is told the radius function and computes an array of radii, it could create a sorted index at the same time.

It might be worth revisiting how multiple resolutions are combined at the same time.

object fields from v3.x confusion

Hi, im updating my app ( https://getsynapse.co ) from d3 v3.x to 4.x.
I'm using this as a reference: http://bl.ocks.org/rauldiazpoblete/raw/bee7ab77e407c843a21fe0dde213789a/

i have data like this, which works in 3.x, which i pass into the d3 code :

nodes: [{id:xx},{id:'yy'},{id:'zz'},]
links: [{source:0, target:1},{source:1, target:2}]

I also try passing the links in as IDs like this:

links: [{source:'xx', target:'yy'},{source:'yy', target:'zz'}]

I also try naming source/target as source_id/target_id in original data like in the document i mentioned that I'm using as a guide:
http://bl.ocks.org/rauldiazpoblete/raw/bee7ab77e407c843a21fe0dde213789a/cnmv.json

None of my attempts to get the links to show properly have worked yet.

and here's my D3 code:

import PouchDB from 'pouchdb';
const db = new PouchDB('synapse-nodes');
import _ from 'lodash';
import * as d3 from 'd3';
import clickNode from './click-node';

export default function initiateD3(obj){
  console.log(obj);
  var w = window.innerWidth;
  var h = window.innerHeight;

  var svg = d3.select("body").append("svg"),
      width = w,
      height = h;

  var simulation = d3.forceSimulation()
      .force("link", d3.forceLink().id(function(d) { return d.id; }))
      .force("charge", d3.forceManyBody())
      .force("center", d3.forceCenter(w / 2, h / 2));

If i change the 'd.source_id' to 'd.source' i get errors
'Uncaught TypeError: Cannot read property 'x' of undefined
Uncaught (in promise) TypeError: Cannot read property 'index' of undefined

  obj.links.forEach(function(d){
    d.source = d.source_id;
    d.target = d.target_id;
  });

  var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(obj.links)
    .enter().append("line")
    .attr("style", "stroke: rgba(255,255,255,0.5);")

    function click(d) {
      clickNode(d.orig_id, true)
    }
    var node = svg.selectAll(".node")
    .data(obj.nodes)
    .enter().append("g")
    .attr("class", "node")
    .on("click", click)
    .append("circle")
    .attr("r", 6)
    .style("fill", "rgba(255,255,255,0.4)") 

  node.append("title")
      .text(function(d) { return d.id; })

  simulation
      .nodes(obj.nodes)
      .on("tick", ticked);

  simulation.force("link")
      .links(obj.links);

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

    node
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  }
}

Any clarification on precisely what fields objects should have and how i do this would be appreciated!
I'm also trying to append labels to each node. Thanks!
screen shot 2016-07-03 at 14 29 47

Rethink automatic starting of simulations?

Reading the Force-Directed Graph example, I find it hard to understand, when the simulation starts.

According to the the API, "The simulator starts automatically". So that would be here:

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter());

Hmmm, without nodes, there wont be much to simulate. Continue reading the code, I assume, it might happen here:

simulation
  .nodes(graph.nodes)
  .on("tick", ticked);

Now it has nodes and can work. But it still has no links. Hmmm. So does it run considering the other forces? Well, Javascript is singlethreaded, so the time callback cannot execute anyway. So presumably it the timer fires, but the callback will be run after this function, or more exactly, after the XmlHttpRequest callback is handled. Lots of pondering. Continue reading

simulation.force("link")
  .links(graph.links);

I assume that now the simulation has all arguments and can run considering the link forces. Which in fact is correct. Anyway, I find the API a bit confusing in this point (otherwise the new force API is absolutely ingenious!).

Debugging reveals, that force.initialize() is called 3 times, computing

nodeById = map$1(nodes, id),

twice, unnecessarily. So maybe it makes sense to have an API method alike

simulation.start(nodes, links)

or more abstract (or precise)

simulation.start(all parameters required to run the configured simulation)

? For me that would have been clearer. Most functions would fall into category configure while start, restart, stop would be category run.

thx so much for d3
urbanhop

simulation.find(x, y)

This would be convenient:

simulation.find = function(x, y, radius) {
  var i = 0,
      n = nodes.length,
      dx,
      dy,
      d2,
      node,
      closest;

  if (radius == null) radius = Infinity;
  else radius *= radius;

  for (i = 0; i < n; ++i) {
    node = nodes[i];
    dx = x - node.x;
    dy = y - node.y;
    d2 = dx * dx + dy * dy;
    if (d2 < radius) closest = node, radius = d2;
  }

  return closest;
};

Enhance Collision to handle shapes other than circles

I currently use D3 v3 force layout with custom collision detection to prevent overlapping of text. Instead of using circle radius to prevent overlaps, I use rectangle width/height (derived from getBBox()) as this suits text better. Can we support rectangles in D4's Collision and possibly other shapes?

Don’t allow exact coincidence along either x or y.

We currently detect exact coincidence along x and y together, but this leads to some funny behavior if there’s exact coincidence along one dimension. We should check for exact coincidence along each dimension separately. Applies to all the forces that currently check for coincidence:

  • d3.forceCollide
  • d3.forceManyBody
  • d3.forceLink

Normalize link forces per node.

Currently we bias the force exerted by a link based on the number of links for the source and target. For large cliques (related #28), this still doesn’t work very well: all the nodes in the clique have the same, or mostly the same, number of links; and the number of links is proportional to the square of the number of nodes, so the larger the clique, the less stable it becomes.

What if instead we took all the link forces for a single node, and divided the force for each link according to the number of links for that node, such that when the force is applied to the simulation, it affects all nodes equally?

Manual ticking.

It’s not currently possible because simulation.stop sets the iteration to Infinity.

Multiple forces like forceX and forceY

It seems that forceX and forceY could be implemented as one common function with their difference exported as a usage of a general axisForce function

export const forceX = axisForce('x') // or given an accessor fn
export const forceY = axisForce('y')

Which would make it simple to create a forceZ or similar.

My use case is to take something like your beeswarm example and pre-solve positions for both a clustered view and a category separated view similar to your corporate tax rates viz. The problem I have is that the second force overwrites the first.

I've worked it out by doing this:

let data = data.map(d => ( {
  ...d,
  split: { ...d },
  grouped: { ...d }
}))

var grouped = d3.forceSimulation(data.map(d => d.grouped)) // force acts on subobject
  .force("x", d3.forceX(function(d) { return x(d.value); }).strength(1))
  .force("y", d3.forceY(height / 2))
  .force("collide", d3.forceCollide(4))
  .stop();

for (var i = 0; i < 120; ++i) grouped.tick();

var split = d3.forceSimulation(data.map(d => d.split)) // force acts on subobject
  .force("x", d3.forceX(function(d) { return x(d.value); }).strength(1))
  .force("y", d3.forceY(d => categoryScale(d.category)) // different
  .force("collide", d3.forceCollide(4))
  .stop();

for (var i = 0; i < 120; ++i) split.tick();


// usage
function update(grouped){
    circles
      .transition()
      .duration(400)
      .attr("cx", d => d[grouped?'grouped':'split'].x; })
      .attr("cy", d => d[grouped?'grouped':'split'].y; });
}
update(true)

Just wondering if there's a better way to achieve this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.