Previously, we were modifying the node's x & y position as we were computing
the charge forces. Unfortunately, this causes drift because those positions are
subsequently used to compute other forces. Now we modify the node's previous
position in the opposite direction, which has the same ultimate effect but
improves the stability of the calculation.
This commit also optimizes the force layout such that gravity and charge forces
are not calculated if the corresponding constants are zero.
There was a bug in the previous fix to increase the stability of link
relaxation; the strength of a link would decrease relative to the link degree of
the connected nodes. Instead of decreasing link strength, we should instead bias
the relaxation so that the lighter node moves more than the heavier node, while
preserving the strength of the link.
This also fixes a few bugs in the new implementation, restoring support for the
offset between the mouse and the dragged element by reporting a delta rather
than an absolute offset. Also, this fixes an infinite loop when an element is
removed from the DOM.
The previous implementation was very unstable if a large number of nodes were
connected to a single node (a single node connecting many). The problem was that
the single node in the center would be moved many times for each connected node,
never converging to a stable solution. The fix is to weight each node according
to the number of links; in effect, the Gauss-Seidel relaxation then becomes the
average of the connected links, rather than relaxing each link individually.
Previously, if you set a node to be `fixed`, it was forgotten on mouseover. Now
the preference is sticky. This changes the fixed attribute from a boolean to a
two-bit integer, but truthiness remains for backwards compatibility.
Extracted from d3.layout.force. Fires "dragstart", "drag" and "dragend"
events, with a "position" property. Note: this introduces a dependency
on d3.behavior to d3.layout.force if used (in addition to the existing
dependency on d3.geom).
Fixes#249.
Previously, negative or zero sizes were short-circuiting the worst()
function by always returning Infinity, instead of being ignored for the
score computation.
Fixes#233.
For very large force layouts, Safari was throwing "RangeError: Maximum call
stack size exceeded". I think this is due to Safari having a smaller call stack
than Chrome and FireFox. In any case, this fix replaces forEach with a while
loop, which should be faster in all browsers as well as reducing the call stack
size needed and thus fixing the Safari issue.
Instead of filtering them out, we fix the root of the issue instead, which is
that the scoring function "worst" was returning NaN due to calculating 0/0,
instead of Infinity.
I simplified the implementation to only support single-touch. I also opted not
to include touch support in the force-dynamic example (and other examples), just
because it complicates the examples too much. Touch is nice but I don't want it
to interfere with people learning the basics.
This merge also has an extra bonus fix: you can now have multiple force layouts
with dragging on the same page, and the drag behavior will do the direct the
event to the appropriate force layout.
This allows the Dorling/Demers cartograms to be slightly closer to the real
geography.
Also, fix the Dorling collision detection as self-collisions were previously
being detected. Thanks, Mike!
Lastly, I've removed the variable per-link distance as this is no longer needed.