force.alpha(a/0.99*(1 - x))
xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
/*margin: 200px 500px 100px 500px;*/
}
#inputs {
display: inline-block;
margin: 0 0 0 0.5em;
}
#panel {
display: inline-block;
margin: 0 0 0 100px;
border: none;
box-sizing: border-box;
background-color: black;
}
#metrics {
display: inline-block;
}
label, input {
text-align: left;
width: 3.5em;
color: orange;
/*padding-left: 1em;*/
background-color: black;
outline: none;
border: none;
}
circle {
stroke: black;
}
svg {
display: block;
overflow: visible;
border: none;
background: black;
margin: 0 100px 0 100px;
}
text {
text-anchor: middle;
}
rect {
stroke: #ccc;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<!--<script src="d3 CB.js"></script>-->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/tinycolor/1.1.2/tinycolor.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/repo/cool-blue/d3-lib/filters/shadow.js"></script>
<script
src="https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/elapsedTime/elapsed-time-1.0.js"></script>
<script>
var inputs = d3.select("body").append("div")
.attr("id", "metrics")
.attr("id", "panel")
.append("div").attr({id: "inputs"}),
nodeCount = inputs.append("label")
.attr("for", "nodeCount")
.text("nodes: ")
.append("input")
.attr({id : "nodeCount",
class : "numIn",
type : "number",
min : "100",
max : "5,000",
step : "100",
inputmode: "numeric"
});
var elapsedTime = ElapsedTime("#panel", {
border : 0, margin: 0, "box-sizing": "border-box",
padding: "0 0 0 3px", background: "black", "color": "orange"
})
.message(function(value) {
var this_lap = this.lap().lastLap, aveLap = this.aveLap(this_lap)
return 'alpha:' + d3.format(" >7,.3f")(value)
+ '\ttick time:' + d3.format(" >8,.4f")(this_lap)
+ ' (' + d3.format(" >4,.3f")(this.aveLap(this_lap)) + ')'
+ '\tframe rate:' + d3.format(" >4,.1f")(1 / aveLap) + " fps"
}),
width = 960 - 200,
height = 500 - elapsedTime.selection.node().clientHeight,
padding = 4, // separation between nodes
maxRadius = 7;
var n = 500, // total number of nodes
m = 1, // number of distinct layers
c = 10,
g = 0.2, g2 = 0.1,
f1 = 0.5, f2 = 0.01,
q2 = -40;
var tick = (function() {
var phase = -1, stage1 = true;
function tick(e) {
viz.circle.each(viz.collide(e.alpha * 40));
if(e.alpha < 0.02 || !(phase = ++phase % 4)) {
elapsedTime.mark(e.alpha);
viz.circle.attr({
cx: function(d) {
return d.x;
},
cy: function(d) {
return d.y;
}
});
}
if(stage1 && e.alpha < 0.03) {
console.log("stage2")
force.friction(f2)
.charge(q2)
.gravity(g2)
.start().alpha(e.alpha);
stage1 = false;
}
force.alpha(e.alpha / 0.99 * 0.998)
}
tick.reset = function() {
stage1 = true;
};
return tick;
})(),
force = d3.layout.force()
.size([width, height])
.gravity(g)
.charge(0)
.friction(f1)
.on("tick", tick)
.on("start", function() {
elapsedTime.start(1000);
force
.gravity(g)
.charge(0)
.friction(f1)
tick.reset();
});
force.drag().on("dragend", function(){force.alpha(0.05)})
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g"),
bubble = Bubble(svg);
var viz = update(force, n, padding);
nodeCount
.property("value", n)
.on("change", function() {
viz = update(force, this.value, padding);
this.blur();
});
elapsedTime.selection.style({
width: (width
- parseFloat(window.getComputedStyle(d3.select("#inputs").node()).getPropertyValue("width"))
- parseFloat(window.getComputedStyle(d3.select("#inputs").node()).getPropertyValue("margin-left")))
+ "px"
});
function Collide(nodes, padding) {
// Resolve collisions between nodes.
var maxRadius = d3.max(nodes, function(d) {
return d.radius
});
return function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var r = d.radius + maxRadius + padding,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1);
if(quad.point && (quad.point !== d) && possible) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + padding,
m = Math.pow(quad.point.radius, 4),
mq = Math.pow(d.radius, 4),
mT = m + mq;
if(l < r) {
for(; Math.abs(l) == 0;) {
x = Math.round(Math.random() * r);
y = Math.round(Math.random() * r);
l = Math.sqrt(x * x + y * y);
}
//move the nodes away from each other along the radial (normal) vector
//taking relative mass into consideration, the sign is already established
//in calculating x and y and the nodes are modelled as spheres for calculating mass
l = (r - l) / l * (1 + alpha);
d.x += (x *= l) * m / mT;
d.y += (y *= l) * m / mT;
quad.point.x -= x * mq / mT;
quad.point.y -= y * mq / mT;
}
}
return !possible;
});
};
}
}
function initNodes(force, n, padding) {
var rMax = Math.pow(500 / n * 50, 0.5);
force.stop()
.nodes(d3.range(n).map(function() {
var u = Math.random(),
v = -Math.log(u);
return {
radius : Math.pow(v, 0.8) * rMax,
color : Math.floor(u * c),
x : width / 2,
y : height / 2,
get v() {
var d = this;
return {x: d.x - d.px || d.x || 0, y: d.y - d.py || d.y || 0}
},
frustration: (function() {
//if they can't get home, they get angry, but, as soon as they're home, they're fine
var anger = 1;
return function() {
var d = this, anxious = (Math.abs(d.cy - d.y) > w.rangeBand()
/ 2);
return anger = anxious ? anger + windUp.value() : 1;
}
})()
}
}))
.start();
return Collide(force.nodes(), padding);
}
function update(force, n, padding) {
return {
collide: initNodes(force, n, padding),
circle : (function() {
var update = svg.selectAll("circle")
.data(force.nodes());
update.enter().append("circle");
update.exit().remove();
update.attr("r", function(d) {
return d.radius;
})
.call(bubble.call)
.call(force.drag)
return update;
})()
};
}
function Bubble(svg) {
var colors = d3.range(20).map(d3.scale.category10()).map(function(d) {
return filters.sphere(svg, d, 1)
});
return {
call: function(selection) {
selection.style("fill", function(d) {
return colors[d.color]
})
},
map : function(d, i, data) {
d.fill = colors[~~(Math.random() * 20)];
},
fill: function(d) {
return d.fill
}
}
}
;
</script>
</body>
Updated missing url https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/filters/shadow.js to https://cdn.jsdelivr.net/gh/repo/cool-blue/d3-lib/filters/shadow.js
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js
https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/filters/shadow.js