Cannot add Image to SVG Circle in d3 - javascript

I'm working with d3, but can't seem to add an image to my SVG circle? I've tried different ways but won't give the output I want. Can someone help me?
The image loaded correctly so that shouldn't be the problem. I also tried the fill attribute but it didn't work.
import React, { Component } from "react";
import "../styles/style.css";
import $ from "jquery";
import * as d3 from "d3";
import btcImage from "../images/btc.png";
const crypto = require("../data/cryptoData.json");
const coins = [];
for (let i = 0; i < crypto.data.length; i++) {
var coin = crypto.data[i];
coins.push(coin);
}
var data = [];
coins.forEach((coin) => {
var text = coin.symbol;
var r = coin.quote.USD.market_cap / 4000000000;
data.push({
text: text,
category: coin.quote.USD.percent_change_24h,
image: btcImage,
r: r,
r_change_1: coin.quote.USD.market_cap / 4000000000,
r_change_2: coin.quote.USD.market_cap / 4000000000,
});
});
function collide(alpha) {
var quadtree = d3.geom.quadtree(data);
return function (d) {
var r = d.r + 10,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function (quad, x1, y1, x2, y2) {
if (quad.point && quad.point !== d) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.r + quad.point.r;
if (l < r) {
l = ((l - r) / l) * (1 + alpha);
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
const bubbleCloud = (element) => {
var container = d3.select(".bubble-cloud");
var $container = $(".bubble-cloud");
var containerWidth = $container.width();
var containerHeight = $container.height();
var svgContainer = container
.append("svg")
.attr("width", containerWidth)
.attr("height", containerHeight);
// prepare layout
var force = d3.layout
.force()
.size([containerWidth, containerHeight])
.gravity(0)
.charge(0)
.friction(1);
// load data
force.nodes(data).start();
// create item groups
var node = svgContainer
.selectAll(".node")
.data(data)
.enter()
.append("g")
.attr("class", "node")
.call(force.drag);
// create circles
node
.append("defs")
.append("pattern")
.attr("height", 60)
.attr("width", 60)
.attr("x", 0)
.attr("y", 0)
.append("image")
.attr("xlink:href", function (d) {
return d.image;
})
.attr("height", 60)
.attr("width", 60)
.attr("x", 0)
.attr("y", 0);
node
.append("circle")
.attr("r", 1e-6)
.style("fill", function (d) {
return d.image;
});
// create labels
node
.append("text")
.text(function (d) {
return d.text;
})
.classed("text", true)
.style({
fill: "#ffffff",
"text-anchor": "middle",
"font-size": "12px",
"font-weight": "bold",
"text-transform": "uppercase",
"font-family": "Tahoma, Arial, sans-serif",
});
node
.append("text")
.text(function (d) {
return d.category;
})
.classed("category", true)
.style({
color: "white",
"font-family": "Tahoma, Arial, sans-serif",
"text-anchor": "middle",
"font-size": "9px",
});
node
//.append("line")
//.classed("line", true)
//.attr({
// x1: 0,
// y1: 0,
// x2: 0,
// y2: 0,
//})
.attr("stroke-width", 1)
.attr("stroke", function (d) {
return d.stroke;
});
// put circle into movement
force.on("tick", function (e) {
d3.selectAll("circle")
.each(collide(0.1))
.attr("r", function (d) {
return d.r;
})
.attr("cx", function (d) {
// boundaries
if (d.x <= d.r) {
d.x = d.r + 1;
}
if (d.x >= containerWidth - d.r) {
d.x = containerWidth - d.r - 1;
}
return d.x;
})
.attr("cy", function (d) {
// boundaries
if (d.y <= d.r) {
d.y = d.r + 1;
}
if (d.y >= containerHeight - d.r) {
d.y = containerHeight - d.r - 1;
}
return d.y;
});
d3.selectAll("line").attr({
x1: function (d) {
return d.x - d.r + 10;
},
y1: function (d) {
return d.y;
},
x2: function (d) {
return d.x + d.r - 10;
},
y2: function (d) {
return d.y;
},
});
d3.selectAll(".text")
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y - 10;
});
d3.selectAll(".category")
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y + 20;
});
force.alpha(0.1);
});
};
class Bubbles extends Component {
render() {
return <div className="bubble-cloud" ref={bubbleCloud}></div>;
}
}
export default Bubbles;
I think I'm overlooking something
Output:

Related

d3js v4 bubble chart - getting force/gravity effects back

I am working on a d3 application - which features a bubble chart. I have a version which is displaying - but the old force code from version 3 - but I am unsure how to incorporate version 4 force effects. I want to give the bubbles a bit of animation - charge/gravity type effects so there is always some movement.
//old code with no force effects
http://jsfiddle.net/xzd9eamt/2/
var $this = $('.bubblechart');
var data = [{
"label": "Chinese",
"value": 20
}, {
"label": "American",
"value": 10
}, {
"label": "Indian",
"value": 50
}];
var width = $this.data('width'),
height = $this.data('height');
var color = d3.scaleOrdinal()
.range(["#ff5200", "red", "green"]);
var margin = {
top: 20,
right: 15,
bottom: 30,
left: 20
},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var svg = d3.select($this[0])
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('class', 'bubblechart')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var bubbles = svg.append('g').attr('class', 'bubbles');
var force = d3.forceSimulation()
.force("collide", d3.forceCollide(12))
.force("center", d3.forceCenter(width / 2, height / 2))
.nodes(data)
//.on("tick", tick);
var bubbles = svg.append("g")
.attr("class", "bubbles")
data = funnelData(data, width, height);
var padding = 4;
var maxRadius = d3.max(data, function(d) {
return parseInt(d.radius)
});
var scale = (width / 6) / 100;
var nodes = bubbles.selectAll("circle")
.data(data);
// Enter
nodes.enter()
.append("circle")
.attr("class", "node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 10)
.style("fill", function(d, i) {
return color(i);
})
//.call(d3.drag());
// Update
nodes
.transition()
.delay(300)
.duration(1000)
.attr("r", function(d) {
return d.radius * scale;
})
// Exit
nodes.exit()
.transition()
.duration(250)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 1)
.remove();
draw('all');
function funnelData(data, width, height) {
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var max_amount = d3.max(data, function(d) {
return parseInt(d.value)
})
var radius_scale = d3.scalePow().exponent(0.5).domain([0, max_amount]).range([2, 85])
$.each(data, function(index, elem) {
elem.radius = radius_scale(elem.value) * .8;
elem.all = 'all';
elem.x = getRandom(0, width);
elem.y = getRandom(0, height);
});
return data;
}
function draw(varname) {
var foci = {
"all": {
name: "All",
x: width / 2,
y: height / 2
}
};
//force.on("tick", tick(foci, varname, .55));
//force.start();
}
function tick(foci, varname, k) {
return function(e) {
data.forEach(function(o, i) {
var f = foci[o[varname]];
o.y += (f.y - o.y) * k * e.alpha;
o.x += (f.x - o.x) * k * e.alpha;
});
nodes
.each(collide(.1))
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
}
function collide(alpha) {
var quadtree = d3.geom.quadtree(data);
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) {
if (quad.point && (quad.point !== d)) {
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;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
body {
background: #eeeeee;
}
.line {
fill: none;
stroke-width: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<h1>BubbleChart I</h1>
<div class="bubblechart" data-width="300" data-height="300" />
There are a couple of things you need to know with d3 versus d4.
Firstly, nodes was empty, because the new nodes were not added to the selection automatically. Since d3 v4, you need to call .merge() to combine the update and enter parts of a selection. This article is a really good summary.
Secondly, there is no need to do all this data wrangling yourself. d3-force updates d.x and d.y automatically with the correct values, so you can remove the collide function and the part where you calculate d.x and d.y. All you need to do is just draw the circles at the correct place.
var $this = $('.bubblechart');
var data = [{
"label": "Chinese",
"value": 20
}, {
"label": "American",
"value": 10
}, {
"label": "Indian",
"value": 50
}];
var width = $this.data('width'),
height = $this.data('height');
var color = d3.scaleOrdinal()
.range(["#ff5200", "red", "green"]);
var margin = {
top: 20,
right: 15,
bottom: 30,
left: 20
},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var svg = d3.select($this[0])
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('class', 'bubblechart')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var bubbles = svg.append('g').attr('class', 'bubbles');
var force = d3.forceSimulation()
.force("collide", d3.forceCollide(12))
.force("center", d3.forceCenter(width / 2, height / 2))
.nodes(data);
var bubbles = svg.append("g")
.attr("class", "bubbles")
data = funnelData(data, width, height);
var padding = 4;
var maxRadius = d3.max(data, function(d) {
return parseInt(d.radius)
});
var scale = (width / 6) / 100;
var nodes = bubbles.selectAll("circle")
.data(data);
// Enter
nodes.enter()
.append("circle")
.attr("class", "node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 10)
.style("fill", function(d, i) {
return color(i);
})
.call(d3.drag());
// Update
nodes
.transition()
.delay(300)
.duration(1000)
.attr("r", function(d) {
return d.radius * scale;
})
// Exit
nodes.exit()
.transition()
.duration(250)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 1)
.remove();
draw('all');
function funnelData(data, width, height) {
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var max_amount = d3.max(data, function(d) {
return parseInt(d.value)
})
var radius_scale = d3.scalePow().exponent(0.5).domain([0, max_amount]).range([2, 85])
$.each(data, function(index, elem) {
elem.radius = radius_scale(elem.value) * .8;
elem.all = 'all';
elem.x = width / 2;
elem.y = height / 2;
});
return data;
}
function draw(varname) {
var foci = {
"all": {
name: "All",
x: width / 2,
y: height / 2
}
};
force.on("tick", tick(foci, varname, .55));
}
function tick(foci, varname, k) {
return function(e) {
bubbles.selectAll("circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
}
body {
background: #eeeeee;
}
.line {
fill: none;
stroke-width: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<h1>BubbleChart I</h1>
<div class="bubblechart" data-width="300" data-height="300" />

d3.js - Collision in force layout

I'm trying to implement the following script in v4:
This script is written in v3 and is not valid in v4.
(I'm also new to d3 and started to learn v4)
var width = 400,
height = 400;
var nodes = d3.range(200).map(function() { return {radius: Math.random() * 12 + 4}; }),
root = nodes[0],
color = d3.scale.category10();
root.radius = 0;
root.fixed = true;
var force = d3.layout.force()
.gravity(0.05)
.charge(function(d, i) { return i ? 0 : -2000; })
.nodes(nodes)
.size([width, height]);
force.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.selectAll("circle")
.data(nodes.slice(1))
.enter().append("circle")
.attr("r", function(d) { return d.radius; })
.style("fill", function(d, i) { return color(i % 3); });
force.on("tick", function(e) {
var q = d3.geom.quadtree(nodes),
i = 0,
n = nodes.length;
while (++i < n) q.visit(collide(nodes[i]));
svg.selectAll("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
svg.on("mousemove", function() {
var p1 = d3.mouse(this);
root.px = p1[0];
root.py = p1[1];
force.resume();
});
function collide(node) {
var r = node.radius + 16,
nx1 = node.x - r,
nx2 = node.x + r,
ny1 = node.y - r,
ny2 = node.y + r;
return function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== node)) {
var x = node.x - quad.point.x,
y = node.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = node.radius + quad.point.radius;
if (l < r) {
l = (l - r) / l * .5;
node.x -= x *= l;
node.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
The best solution I could think of is to move around a big special circle and use collision to avoid the rest of the circles touch my big circle.
But my big circle is freezing.
In addition, think my overall solution is not so good:
What am I doing wrong?
var width = 1000,
height = 600;
d3.select("body")
.append("svg")
.attr("width", 1000)
.attr("height", 600);
const collideCircle = {
radius: 30,
fill: 'black',
center: {
x: 200,
y: 200
}
};
const radius = 10;
const moreCircles = [...Array(40).keys()].map(i => ({
radius,
fill: 'pink',
center: {
x: 200,
y: 200
}
}));
const simulation = d3.forceSimulation([collideCircle, ...moreCircles])
.force('x', d3.forceX().x(d => d.center.x))
.force('y', d3.forceY().y(d => d.center.y))
.force('collision', d3.forceCollide().radius(d => d.radius))
.on('tick', ticked);
const collideCircles = d3.select('svg')
.append('g')
.selectAll('circle')
.data([collideCircle])
.enter().append('circle')
.attr('fill', d => d.fill)
.attr('r', d => d.radius)
.attr('cx', d => d.x)
.attr('cy', d => d.y);
const moreCirclesGeom = d3.select('svg')
.append('g')
.selectAll('circle')
.data(moreCircles)
.enter().append('circle')
.attr('fill', d => d.fill)
.attr('r', d => d.radius)
.attr('cx', d => d.x)
.attr('cy', d => d.y);
function ticked() {
// stay in the SVG (box) only:
moreCirclesGeom
.attr("cx", function (d) {
return d.x = Math.max(radius, Math.min(width - radius, d.x));
})
.attr("cy", function (d) {
return d.y = Math.max(radius, Math.min(height - radius, d.y));
});
collideCircles
.attr("cx", function (d) {
return d.x = Math.max(radius, Math.min(width - radius, d.x));
})
.attr("cy", function (d) {
return d.y = Math.max(radius, Math.min(height - radius, d.y));
});
}
d3.select('svg').on("mouseover", handleMouseOver);
function handleMouseOver() {
collideCircle.fx = d3.event.x;
collideCircle.fy = d3.event.y;
simulation.alphaTarget(0.1).restart();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>

D3: Rotating labels in Bi-Level Partition

I'm using D3 and javascript to create a BiLevel Partition following this example. The labels in the left side of the diagram are upside down, and I was trying to rotate them, but I've not been successful yet.
I found numerous cases of people with the same problem, but using Sunburst. Also tried to implement those solutions, but I'm still unable to solve this problem.
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
/*var margin = {top: 350, right: 480, bottom: 350, left: 480},
radius = Math.min(margin.top, margin.right, margin.bottom, margin.left) - 10;*/
var width = 1200,
height = 1200,
radius = Math.min(width, height) / 2;
function filter_min_arc_size_text(d, i) {return (d.dx*d.depth*radius/3)>14};
var hue = d3.scale.category10();
var luminance = d3.scale.sqrt()
.domain([0, 1e6])
.clamp(true)
.range([90, 20]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ")");
var partition = d3.layout.partition()
.sort(function(a, b) { return d3.ascending(a.name, b.name); })
.size([2 * Math.PI, radius]);
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx - .01 / (d.depth + .5); })
.innerRadius(function(d) { return radius / 3 * d.depth; })
.outerRadius(function(d) { return radius / 3 * (d.depth + 1) - 1; });
//Tooltip description
var tooltip = d3.select("body")
.append("div")
.attr("id", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("opacity", 0);
function format_number(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function format_description(d) {
var description = d.description;
return /* '<b>' + d.name + '</b></br>'+*/ d.description + '<br> (' + format_number(d.value) + ')';
}
function computeTextRotation(d) {
var ang = ((d.x + d.dx / 2) - Math.PI / 2) / Math.PI * 180;
return (ang > 90) ? 180 + ang : ang;
}
function mouseOverArc(d) {
d3.select(this).attr("stroke","black")
tooltip.html(format_description(d));
return tooltip.transition()
.duration(50)
.style("opacity", 0.9);
}
function mouseOutArc(){
d3.select(this).attr("stroke","")
return tooltip.style("opacity", 0);
}
function mouseMoveArc (d) {
return tooltip
.style("top", (d3.event.pageY-10)+"px")
.style("left", (d3.event.pageX+10)+"px");
}
var root_ = null;
d3.json("flare.json", function(error, root) {
if (error) return console.warn(error);
// Compute the initial layout on the entire tree to sum sizes.
// Also compute the full name and fill color for each node,
// and stash the children so they can be restored as we descend.
partition
.value(function(d) { return d.size; })
.nodes(root)
.forEach(function(d) {
d._children = d.children;
d.sum = d.value;
d.key = key(d);
d.fill = fill(d);
});
// Now redefine the value function to use the previously-computed sum.
partition
.children(function(d, depth) { return depth < 2 ? d._children : null; })
.value(function(d) { return d.sum; });
var center = svg.append("circle")
.attr("r", radius / 3)
.on("click", zoomOut);
center.append("title")
.text("zoom out");
var partitioned_data=partition.nodes(root).slice(1)
var path = svg.selectAll("path")
.data(partitioned_data)
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) { return d.fill; })
.each(function(d) { this._current = updateArc(d); })
.on("click", zoomIn)
.on("mouseover", mouseOverArc)
.on("mousemove", mouseMoveArc)
.on("mouseout", mouseOutArc);
var texts = svg.selectAll("text")
.data(partitioned_data)
.enter().append("text")
.filter(filter_min_arc_size_text)
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) { return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d,i) {return d.name})
function zoomIn(p) {
if (p.depth > 1) p = p.parent;
if (!p.children) return;
zoom(p, p);
}
function zoomOut(p) {
if (!p.parent) return;
zoom(p.parent, p);
}
// Zoom to the specified new root.
function zoom(root, p) {
if (document.documentElement.__transition__) return;
// Rescale outside angles to match the new layout.
var enterArc,
exitArc,
outsideAngle = d3.scale.linear().domain([0, 2 * Math.PI]);
function insideArc(d) {
return p.key > d.key
? {depth: d.depth - 1, x: 0, dx: 0} : p.key < d.key
? {depth: d.depth - 1, x: 2 * Math.PI, dx: 0}
: {depth: 0, x: 0, dx: 2 * Math.PI};
}
function outsideArc(d) {
return {depth: d.depth + 1, x: outsideAngle(d.x), dx: outsideAngle(d.x + d.dx) - outsideAngle(d.x)};
}
center.datum(root);
// When zooming in, arcs enter from the outside and exit to the inside.
// Entering outside arcs start from the old layout.
if (root === p) enterArc = outsideArc, exitArc = insideArc, outsideAngle.range([p.x, p.x + p.dx]);
var new_data=partition.nodes(root).slice(1)
path = path.data(new_data, function(d) { return d.key; });
// When zooming out, arcs enter from the inside and exit to the outside.
// Exiting outside arcs transition to the new layout.
if (root !== p) enterArc = insideArc, exitArc = outsideArc, outsideAngle.range([p.x, p.x + p.dx]);
d3.transition().duration(d3.event.altKey ? 7500 : 750).each(function() {
path.exit().transition()
.style("fill-opacity", function(d) { return d.depth === 1 + (root === p) ? 1 : 0; })
.attrTween("d", function(d) { return arcTween.call(this, exitArc(d)); })
.remove();
path.enter().append("path")
.style("fill-opacity", function(d) { return d.depth === 2 - (root === p) ? 1 : 0; })
.style("fill", function(d) { return d.fill; })
.on("click", zoomIn)
.on("mouseover", mouseOverArc)
.on("mousemove", mouseMoveArc)
.on("mouseout", mouseOutArc)
.each(function(d) { this._current = enterArc(d); });
path.transition()
.style("fill-opacity", 1)
.attrTween("d", function(d) { return arcTween.call(this, updateArc(d)); });
});
texts = texts.data(new_data, function(d) { return d.key; })
texts.exit()
.remove()
texts.enter()
.append("text")
texts.style("opacity", 0)
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) { return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.filter(filter_min_arc_size_text)
.text(function(d,i) {return d.name})
.transition().delay(750).style("opacity", 1)
}
});
function key(d) {
var k = [], p = d;
while (p.depth) k.push(p.name), p = p.parent;
return k.reverse().join(".");
}
function fill(d) {
var p = d;
while (p.depth > 1) p = p.parent;
var c = d3.lab(hue(p.name));
c.l = luminance(d.sum);
return c;
}
function arcTween(b) {
var i = d3.interpolate(this._current, b);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function updateArc(d) {
return {depth: d.depth, x: d.x, dx: d.dx};
}
d3.select(self.frameElement).style("height", margin.top + margin.bottom + "px");
</script>
The problem I have is that it is only showing the right half of the Partition.

d3.js bubble chart animation

I am working on a bubble chart application. At the moment though - my chart appears broken, not sure why.
I will be looking to try and animate the bubbles when new data sets come in.
http://jsfiddle.net/NYEaX/175/
setup: function(rawData, w, h){
var format = d3.format(",d"),
color = d3.scale.category20c();
var bubble = d3.layout.pack()
.sort(null)
.size([w, h])
.padding(1.5);
var svg = d3.select(methods.el).append("svg")
.attr("width", w)
.attr("height", h)
.attr("class", "bubblechart")
.attr('viewBox', "0 0 "+parseInt(w, 10)+" "+parseInt(h, 10))
.attr('perserveAspectRatio', "xMinYMid");
var root = methods.conformData(rawData);
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("title")
.text(function(d) { return d.className + ": " + format(d.value); });
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(d.packageName); });
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.className.substring(0, d.r / 3); });
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
else classes.push({packageName: node.name, className: node.name, value: node.size});
recurse(null, root);
return {children: classes};
}
d3.select(self.frameElement).style("height", h + "px");
//this.resizeBubble();
}
Here is the latest bubble chart code. Although its not updating correctly for multiple charts
http://jsfiddle.net/pPMqQ/18/
animateBubbles: function(selector, data){
data = this.funnelData(data, methods.width, methods.height);
var padding = 4;
var maxRadius = d3.max(data, function (d) { return parseInt(d.radius)});
var year_centers = {
"2008": {name:"2008", x: 150, y: 300},
"2009": {name:"2009", x: 550, y: 300},
"2010": {name:"2010", x: 900, y: 300}
}
var all_center = { "all": {name:"All Grants", x: methods.width/2, y: methods.height/2}};
var bubbleholder = d3.select(selector + " .bubbleholder");
var bubbles = d3.select(selector + " .bubbles");
var labelbubble = d3.select(selector + " .labelbubble");
var nodes = bubbles.selectAll("circle")
.data(data);
// Enter
nodes.enter()
.append("circle")
.attr("class", "node")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("r", 1)
.style("fill", function (d) { return methods.fill(d.label); })
.call(methods.force.drag);
// Update
nodes
.transition()
.delay(300)
.duration(1000)
.attr("r", function (d) { return d.radius; })
// Exit
nodes.exit()
.transition()
.duration(250)
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("r", 1)
.remove();
var labels = labelbubble.selectAll("text")
.data(data);
// Enter
labels.enter()
.append("text")
.attr("class", "title")
.text(function(d) { return d.label; })
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; })
// Update
labels
.transition()
.delay(300)
.duration(1000)
// .attr("x", function (d) { return d.x; })
//.attr("y", function (d) { return d.y; })
// Exit
labels.exit()
.transition()
.duration(250)
.remove();
draw('all');
function draw (varname) {
var foci = varname === "all" ? all_center: year_centers;
methods.force.on("tick", tick(foci, varname, .55));
methods.force.start();
}
function tick (foci, varname, k) {
return function (e) {
data.forEach(function(o, i) {
var f = foci[o[varname]];
o.y += (f.y - o.y) * k * e.alpha;
o.x += (f.x - o.x) * k * e.alpha;
});
nodes
.each(collide(.1))
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
labels
.each(collide(.1))
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; });
}
}
function collide(alpha) {
var quadtree = d3.geom.quadtree(data);
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) {
if (quad.point && (quad.point !== d)) {
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;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
},

d3.js collision detection with quadtree

I'm trying to draw a graph with d3.js that implements collisions between nodes.
I try the following codigo.Pero not work.
If I remove the collision detection draw but overlapping nodes ...
<html>
<head>
<title>Demo</title>
<script type="text/javascript" src="../d3.v2.js"></script>
<script type="text/javascript" src="jquery-1.8.1.js"></script>
<style type="text/css">
</style>
</head>
<body>
<script type="text/javascript">
window.onload = function() {
print();
}
function print(){
var w = 960;
var h = 500;
var r = 20;
var fill = d3.scale.category20();
var force = d3.layout.force()
.gravity(.01)
.charge(-120)
.linkDistance(30)
.size([w, h]);
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
function() {
d3.json("datos.json",
function(json) {
var nodes = svg.selectAll("circle")
.data(json.nodes)
.enter()
.append("circle")
.attr("r", r - .75)
.style("fill", function(d) {return "#FF7F0E"})
.style("stroke", function(d) { return "green"; })
.style("stroke-width", function(d) { return "13px"; })
.call(force.drag);
force.nodes(json.nodes)
.on("tick", tick)
.start();
var text = svg.append("g").selectAll("g")
.data(force.nodes())
.enter()
.append("g")
.style("font-size", function(d) { return "12px"; })
.style("fill", function(d) { return "red"; })
.style("font-weight", function(d) { return "bold"; });
text.append("text")
.attr("text-anchor", "middle")
.attr("y", ".31em")
.text(function(d) { return d.name; });
function tick(e) {
nodes.each(collide(.5))
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";});
}
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var ratio = r,
nx1 = d.x - ratio,
nx2 = d.x + ratio,
ny1 = d.y - ratio,
ny2 = d.y + ratio;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
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 +
(d.color !== quad.point.color) * padding;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2
|| x2 < nx1
|| y1 > ny2
|| y2 < ny1;
});
};
}
});
})();
}
</script>
</body>
</html>
If I remove the collision detection works but overlapping nodes. I need help :(

Categories