Permalink
Browse files

Merge branch 'force'

  • Loading branch information...
square-build-bot committed Apr 27, 2011
2 parents 020aaa5 + f19d265 commit 9d80ac2d4699df58aa27161ecaba1fb757742e18
Showing with 148 additions and 46 deletions.
  1. +5 −4 d3.js
  2. +65 −16 d3.layout.js
  3. +1 −1 d3.layout.min.js
  4. +2 −2 d3.min.js
  5. +3 −3 examples/force/force-dynamic.html
  6. +2 −0 examples/force/force.js
  7. +1 −1 src/core/core.js
  8. +4 −3 src/core/selection.js
  9. +65 −16 src/layout/force.js
View
9 d3.js
@@ -1,4 +1,4 @@
-(function(){d3 = {version: "1.12.0"}; // semver
+(function(){d3 = {version: "1.13.0"}; // semver
if (!Date.now) Date.now = function() {
return +new Date();
};
@@ -1513,7 +1513,8 @@ function d3_selection(groups) {
// type can be namespaced, e.g., "click.foo"
// listener can be null for removal
- groups.on = function(type, listener) {
+ groups.on = function(type, listener, capture) {
+ if (arguments.length < 3) capture = false;
// parse the type specifier
var i = type.indexOf("."),
@@ -1522,8 +1523,8 @@ function d3_selection(groups) {
// remove the old event listener, and add the new event listener
return groups.each(function(d, i) {
- if (this[name]) this.removeEventListener(typo, this[name], false);
- if (listener) this.addEventListener(typo, this[name] = l, false);
+ if (this[name]) this.removeEventListener(typo, this[name], capture);
+ if (listener) this.addEventListener(typo, this[name] = l, capture);
// wrapped event listener that preserves d, i
function l(e) {
View
@@ -157,9 +157,9 @@ d3.layout.force = function() {
size = [1, 1],
alpha,
drag = .9,
- distance = 30,
- charge = -60,
- gravity = .001,
+ distance = 20,
+ charge = -30,
+ gravity = .1,
theta = .8,
interval,
nodes,
@@ -257,7 +257,7 @@ d3.layout.force = function() {
o = nodes[i];
s = x - o.x;
t = y - o.y;
- l = kg * Math.sqrt(s * s + t * t);
+ l = kg * Math.pow(s * s + t * t, .01);
s *= l;
t *= l;
o.fx += s;
@@ -345,19 +345,16 @@ d3.layout.force = function() {
force.start = function() {
var i,
+ j,
n = nodes.length,
m = links.length,
w = size[0],
h = size[1],
+ neighbors,
o;
- // TODO initialize positions of new nodes using constraints (links)
for (i = 0; i < n; ++i) {
- o = nodes[i];
- if (isNaN(o.x)) o.x = Math.random() * w;
- if (isNaN(o.y)) o.y = Math.random() * h;
- if (isNaN(o.px)) o.px = o.x;
- if (isNaN(o.py)) o.py = o.y;
+ (o = nodes[i]).index = i;
}
for (i = 0; i < m; ++i) {
@@ -366,6 +363,40 @@ d3.layout.force = function() {
if (typeof o.target == "number") o.target = nodes[o.target];
}
+ for (i = 0; i < n; ++i) {
+ o = nodes[i];
+ if (isNaN(o.x)) o.x = position("x", w);
+ if (isNaN(o.y)) o.y = position("y", h);
+ if (isNaN(o.px)) o.px = o.x;
+ if (isNaN(o.py)) o.py = o.y;
+ }
+
+ // initialize node position based on first neighbor
+ function position(dimension, size) {
+ var neighbors = neighbor(i),
+ j = -1,
+ m = neighbors.length,
+ x;
+ while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x;
+ return Math.random() * size;
+ }
+
+ // initialize neighbors lazily
+ function neighbor() {
+ if (!neighbors) {
+ neighbors = [];
+ for (j = 0; j < n; ++j) {
+ neighbors[j] = [];
+ }
+ for (j = 0; j < m; ++j) {
+ var o = links[j];
+ neighbors[o.source.index].push(o.target);
+ neighbors[o.target.index].push(o.source);
+ }
+ }
+ return neighbors[i];
+ }
+
return force.resume();
};
@@ -384,27 +415,43 @@ d3.layout.force = function() {
force.drag = function() {
this
- .on("mouseover", d3_layout_forceDragOver)
- .on("mouseout", d3_layout_forceDragOut)
- .on("mousedown", d3_layout_forceDragDown);
+ .on("mouseover.force", d3_layout_forceDragOver)
+ .on("mouseout.force", d3_layout_forceDragOut)
+ .on("mousedown.force", d3_layout_forceDragDown);
d3.select(window)
- .on("mousemove", dragmove)
- .on("mouseup", dragup);
+ .on("mousemove.force", dragmove)
+ .on("mouseup.force", dragup, true);
return force;
};
function dragmove() {
if (!d3_layout_forceDragNode) return;
+
+ // O NOES! The drag element was removed from the DOM.
+ if (!d3_layout_forceDragElement.parentNode) {
+ d3_layout_forceDragNode.fixed = false;
+ d3_layout_forceDragNode = d3_layout_forceDragElement = null;
+ return;
+ }
+
var m = d3.svg.mouse(d3_layout_forceDragElement);
+ d3_layout_forceDragMoved = true;
d3_layout_forceDragNode.px = m[0];
d3_layout_forceDragNode.py = m[1];
force.resume(); // restart annealing
}
function dragup() {
if (!d3_layout_forceDragNode) return;
+
+ // If the node was moved, prevent the mouseup from propagating.
+ if (d3_layout_forceDragMoved) {
+ d3.event.stopPropagation();
+ d3.event.preventDefault();
+ }
+
dragmove();
d3_layout_forceDragNode.fixed = false;
d3_layout_forceDragNode = d3_layout_forceDragElement = null;
@@ -414,6 +461,7 @@ d3.layout.force = function() {
};
var d3_layout_forceDragNode,
+ d3_layout_forceDragMoved,
d3_layout_forceDragElement;
function d3_layout_forceDragOver(d) {
@@ -426,8 +474,9 @@ function d3_layout_forceDragOut(d) {
}
}
-function d3_layout_forceDragDown(d) {
+function d3_layout_forceDragDown(d, i) {
(d3_layout_forceDragNode = d).fixed = true;
+ d3_layout_forceDragMoved = false;
d3_layout_forceDragElement = this;
d3.event.stopPropagation();
d3.event.preventDefault();
View

Large diffs are not rendered by default.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -39,6 +39,7 @@
.attr("height", h);
var force = d3.layout.force()
+ .distance(30)
.nodes(nodes)
.links(links)
.size([w, h]);
@@ -84,6 +85,7 @@
restart();
function restart() {
+ force.start();
vis.selectAll("line.link")
.data(links)
@@ -100,10 +102,8 @@
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
- .attr("r", 5)
+ .attr("r", 4.5)
.call(force.drag);
-
- force.start();
}
</script>
View
@@ -9,6 +9,8 @@ var vis = d3.select("#chart")
d3.json("miserables.json", function(json) {
var force = d3.layout.force()
+ .charge(-120)
+ .distance(30)
.nodes(json.nodes)
.links(json.links)
.size([w, h])
View
@@ -1 +1 @@
-d3 = {version: "1.12.0"}; // semver
+d3 = {version: "1.13.0"}; // semver
View
@@ -513,7 +513,8 @@ function d3_selection(groups) {
// type can be namespaced, e.g., "click.foo"
// listener can be null for removal
- groups.on = function(type, listener) {
+ groups.on = function(type, listener, capture) {
+ if (arguments.length < 3) capture = false;
// parse the type specifier
var i = type.indexOf("."),
@@ -522,8 +523,8 @@ function d3_selection(groups) {
// remove the old event listener, and add the new event listener
return groups.each(function(d, i) {
- if (this[name]) this.removeEventListener(typo, this[name], false);
- if (listener) this.addEventListener(typo, this[name] = l, false);
+ if (this[name]) this.removeEventListener(typo, this[name], capture);
+ if (listener) this.addEventListener(typo, this[name] = l, capture);
// wrapped event listener that preserves d, i
function l(e) {
View
@@ -5,9 +5,9 @@ d3.layout.force = function() {
size = [1, 1],
alpha,
drag = .9,
- distance = 30,
- charge = -60,
- gravity = .001,
+ distance = 20,
+ charge = -30,
+ gravity = .1,
theta = .8,
interval,
nodes,
@@ -105,7 +105,7 @@ d3.layout.force = function() {
o = nodes[i];
s = x - o.x;
t = y - o.y;
- l = kg * Math.sqrt(s * s + t * t);
+ l = kg * Math.pow(s * s + t * t, .01);
s *= l;
t *= l;
o.fx += s;
@@ -193,19 +193,16 @@ d3.layout.force = function() {
force.start = function() {
var i,
+ j,
n = nodes.length,
m = links.length,
w = size[0],
h = size[1],
+ neighbors,
o;
- // TODO initialize positions of new nodes using constraints (links)
for (i = 0; i < n; ++i) {
- o = nodes[i];
- if (isNaN(o.x)) o.x = Math.random() * w;
- if (isNaN(o.y)) o.y = Math.random() * h;
- if (isNaN(o.px)) o.px = o.x;
- if (isNaN(o.py)) o.py = o.y;
+ (o = nodes[i]).index = i;
}
for (i = 0; i < m; ++i) {
@@ -214,6 +211,40 @@ d3.layout.force = function() {
if (typeof o.target == "number") o.target = nodes[o.target];
}
+ for (i = 0; i < n; ++i) {
+ o = nodes[i];
+ if (isNaN(o.x)) o.x = position("x", w);
+ if (isNaN(o.y)) o.y = position("y", h);
+ if (isNaN(o.px)) o.px = o.x;
+ if (isNaN(o.py)) o.py = o.y;
+ }
+
+ // initialize node position based on first neighbor
+ function position(dimension, size) {
+ var neighbors = neighbor(i),
+ j = -1,
+ m = neighbors.length,
+ x;
+ while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x;
+ return Math.random() * size;
+ }
+
+ // initialize neighbors lazily
+ function neighbor() {
+ if (!neighbors) {
+ neighbors = [];
+ for (j = 0; j < n; ++j) {
+ neighbors[j] = [];
+ }
+ for (j = 0; j < m; ++j) {
+ var o = links[j];
+ neighbors[o.source.index].push(o.target);
+ neighbors[o.target.index].push(o.source);
+ }
+ }
+ return neighbors[i];
+ }
+
return force.resume();
};
@@ -232,27 +263,43 @@ d3.layout.force = function() {
force.drag = function() {
this
- .on("mouseover", d3_layout_forceDragOver)
- .on("mouseout", d3_layout_forceDragOut)
- .on("mousedown", d3_layout_forceDragDown);
+ .on("mouseover.force", d3_layout_forceDragOver)
+ .on("mouseout.force", d3_layout_forceDragOut)
+ .on("mousedown.force", d3_layout_forceDragDown);
d3.select(window)
- .on("mousemove", dragmove)
- .on("mouseup", dragup);
+ .on("mousemove.force", dragmove)
+ .on("mouseup.force", dragup, true);
return force;
};
function dragmove() {
if (!d3_layout_forceDragNode) return;
+
+ // O NOES! The drag element was removed from the DOM.
+ if (!d3_layout_forceDragElement.parentNode) {
+ d3_layout_forceDragNode.fixed = false;
+ d3_layout_forceDragNode = d3_layout_forceDragElement = null;
+ return;
+ }
+
var m = d3.svg.mouse(d3_layout_forceDragElement);
+ d3_layout_forceDragMoved = true;
d3_layout_forceDragNode.px = m[0];
d3_layout_forceDragNode.py = m[1];
force.resume(); // restart annealing
}
function dragup() {
if (!d3_layout_forceDragNode) return;
+
+ // If the node was moved, prevent the mouseup from propagating.
+ if (d3_layout_forceDragMoved) {
+ d3.event.stopPropagation();
+ d3.event.preventDefault();
+ }
+
dragmove();
d3_layout_forceDragNode.fixed = false;
d3_layout_forceDragNode = d3_layout_forceDragElement = null;
@@ -262,6 +309,7 @@ d3.layout.force = function() {
};
var d3_layout_forceDragNode,
+ d3_layout_forceDragMoved,
d3_layout_forceDragElement;
function d3_layout_forceDragOver(d) {
@@ -274,8 +322,9 @@ function d3_layout_forceDragOut(d) {
}
}
-function d3_layout_forceDragDown(d) {
+function d3_layout_forceDragDown(d, i) {
(d3_layout_forceDragNode = d).fixed = true;
+ d3_layout_forceDragMoved = false;
d3_layout_forceDragElement = this;
d3.event.stopPropagation();
d3.event.preventDefault();

0 comments on commit 9d80ac2

Please sign in to comment.