d3 connect arc and point with a ribbon - javascript

Given a set of arcs that make up a circle and random points generated inside of said circle, what's the best way to generate an area or chord that connects a slice of the array to one of the points and not just the exact center?
I was thinking that a ribbon or chord layout would be helpful here but the chord layout seems specific to connecting arcs (though admittedly I've only spent about two days researching it and am struggling with actual usage)
Right now I have a simple arbitrary arc and circle as such -
var width = 1000;
var height = 600;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
let arc = svg.append("path")
.datum({
id: 1,
startAngle: 0,
endAngle: .50 * (2 * Math.PI)
})
.style("fill", "blue")
.attr("d", d3.arc()
.innerRadius(180)
.outerRadius(200))
let circle = svg.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("gradientUnits", "objectBoundingBox")
.attr("r", 20);
Simple fiddle - http://jsfiddle.net/968o4s9m/

Seems the best way is to draw a path manually by entering all points using lines and arcs. Ribbons do not appear to be able to connect arbitrary points and arcs from what I can tell.

Related

drawing between two points on a spiral

I have drawn a spiral using this code:
var width = 1000,
height = 1000,
start = 0,
end = 2.25,
numSpirals = 78,
margin = {top:50,bottom:50,left:50,right:50};
// Constructing the spiral:
// theta for the spiral
var theta = function(r) {
return numSpirals * Math.PI * r;
};
// the r works out the space within which the spiral can take shape - the width and height is set above
var r = d3.min([width, height]) / 2 - 40 ;
// The radius of the spiral
var radius = d3.scaleLinear()
.domain([start, end])
.range([40, r]);
// inserts svg into the DOM
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.left + margin.right)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// The path to draw the spiral needs data to inform it, points generates this, and is used in .datum(points) below
var points = d3.range(start, end + 0.02, (end - start) / 2000);
// this is the spiral, utilising the theta and radius generated above
var spiral = d3.radialLine()
.curve(d3.curveCardinal)
.angle(theta)
.radius(radius);
var path = svg.append("path")
.datum(points)
.attr("id", "spiral")
.attr("d", spiral)
.style("fill", "none")
.style("stroke", "grey")
.style("stroke", ("6, 5"))
.style("opacity",0.5);
But, now I want to draw some lines on top of this spiral, following the existing path of the spiral. These lines will need to connect two different dates across two different columns within the array. I have tried a few approaches, such as d3.line but I can't get the lines to follow the spiral. I imagine I somehow need to reference the initial spiral? I am unsure how to proceed with this though.

D3.js pattern not working with arc or donut graph

I'm trying to have an image fill my donut chart, then rotate the image 60 degrees from its center.
I've had success filling a simple shape as a pattern with this method, but the pattern image gets all screwy and repeats itself when applied to a donut chart. The image is 300px x 300px - same size as the svg. The final result should look like this.
Here's my fiddle.
imgPath = "http://www.mikeespo.com/statDonkey/inner.png";
w = 300;
h = 300;
passingPercent = 60;
rotateStartPosition = 50;
var myScale = d3.scale.linear().domain([0, 100]).range([0, 2 * Math.PI]);
// MAKES SVG
var svg = d3.select("body")
.append("svg")
.attr("id", "svg_donut")
.attr("width", w)
.attr("height", h);
// MAKE DEFS
var defs = d3.select("#svg_donut")
.append("defs");
// MAKES PATTERN
defs.append('pattern')
.attr('id', 'pic1')
.attr('width', 300)
.attr('height', 300)
.attr('patternUnits', 'userSpaceOnUse')
.append('svg:image')
.attr('xlink:href', imgPath)
.attr("width", 300)
.attr("height", 300)
.attr("transform", "rotate(60 150 150)")
.attr("x", 0)
.attr("y", 0);
// CREATES VARIABLE *VIS* TO SVG
var vis = d3.select("#svg_donut");
// DEFINES DONUT GRAPH
var arc = d3.svg.arc()
.innerRadius(95)
.outerRadius(140)
.startAngle((myScale(0 + rotateStartPosition)))
.endAngle((myScale(passingPercent + rotateStartPosition)));
// APPENDS *VIS* TO SVG
vis.append("path")
.attr("id", "passing")
.attr("d", arc)
.attr("fill", "white")
.attr("transform", "translate(150,150)")
.attr("fill", "url(#pic1)");
I'm not exactly sure why this works to be honest but when I changed the width and height of the pattern element and removed the patternUnits attribute, I was able to achieve the desired look:
defs.append('pattern')
.attr('id', 'pic1')
.attr('width', 1)
.attr('height', 1)
.append('svg:image')
.attr('xlink:href', imgPath)
.attr("width", 300)
.attr("height", 300)
.attr("transform", "rotate(00 150 150)")
.attr("x", 0)
.attr("y", 0);
I don't understand it completely but it has something to do with the coordinate system and the way in which the pattern scales to the object you're applying it to. The width and height aren't defining the size of the image as you might initially think, but the way in which the pattern will map to the new coordinate system of the donut. A width and height of 1 indicates that the pattern will just scale to the width and height of the donut.
Getting my info from here and admittedly not fully grasping it all yet but hopefully this will help: https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Patterns

How to draw gradient arc using d3.js?

I have started using d3.js. I have following requirement
Requirement:
What I have tried?
fiddle
Question?
How to achieve gradient as same as above image.
Any suggestion or idea will be grateful.
Note
I am just started d3.js.
Edit - changed data structure and fiddle link to represent unfilled chunk at the beginning.
I would use the pie function in d3 to create a pie chart.
The image above is basically a pie with two different gradient styles applied to the pie chunks.
A red linear gradient and a black/white radial gradient.
I created a fiddle linked below to show you an example.
The key here is that you need to structure your data to also include the percentage that should not have the red-gradient applied.
Using the example above, we have three chunks with red and the rest as unfilled.
Imagine the data set like so:
var data = [{
percent: 10,
pie: 0
}, {
percent: 13,
pie: 1
}, {
percent: 13,
pie: 1
}, {
percent: 6,
pie: 1
}, {
percent: 56,
pie: 0
}];
So we have the percent and we also flag which chunks should be red and which chunk should be the unfilled section using the pie attribute.
You can use whatever data set you wish but I'm just using this as an example.
So next thing is to create your SVG element:
var width = 400;
var height = 400;
var radius = Math.min(width, height) / 2;
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(((radius - 10) / 5) * 4);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) { return d.percent });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
and after this we will create the two gradients to style the pie chunks.
So first one is the linear red gradient:
// append a defs tag to SVG, This holds all our gradients and can be used
//by any element within the SVG we append it to
var defs = svg.append("svg:defs")
//next we append a linear gradient
var red_gradient = defs.append("svg:linearGradient")
.attr("id", "gradient")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "0%")
.attr("y2", "100%")
.attr("spreadMethod", "pad");
//first dark red color
red_gradient.append("svg:stop")
.attr("offset", "0%")
.attr("stop-color", "rgb(221,48,2)")
.attr("stop-opacity", 1);
//second light red color
red_gradient.append("svg:stop")
.attr("offset", "100%")
.attr("stop-color", "rgb(247, 78, 1)")
.attr("stop-opacity", 1);
Then we append the radial gradient for the unfilled part. This one is a little tricker because we need to move the gradient with a transform to get the right radial center. If you translate it half the width and height I think it should work out.
var radial_gradient = defs.append("radialGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", '50%')
.attr("cy", '50%')
.attr("r", "75%")
.attr("fx", '50%')
.attr("fy", '50%')
.attr('gradientTransform', "translate(-200,-200)")
.attr("id", 'gradient2');
radial_gradient.append("stop").attr("offset", "0%").style("stop-color", "black");
radial_gradient.append("stop").attr("offset", "55%").style("stop-color", "white");
radial_gradient.append("stop").attr("offset", "95%").style("stop-color", "black");
Once we have set up the gradients, we can add the pie:
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
// we create a function to append the different chucks of the pie.
// we check the pie attribute from the data and apply the correct gradient.
g.append("path")
.attr("d", arc)
.style("fill", function (d) {
if (d.data.pie === 1) {
console.log('true' + d.data.pie);
return "url(#gradient)"
}
else {
console.log('false' + d.data.pie);
return "url(#gradient2)"
}
})
JSFiddle: http://jsfiddle.net/staceyburnsy/afo292ty/2/

d3: svg image in zoom circle packing

UPDATE: New JSFIDDLE Scaling now working, ditched the defs and rect altogether and just appended the image. But still stuck on translate.
The translating is still not working on zoom. I can set the translate to say -100 for both x and y to get the non-zoomed placement correct. But, when zooming, it's of course still translating it -100 and not the larger value it would need to be to keep it in place.
Appears to need something in the code in the zoom section toward the bottom. Been messing with the part currently commented out, but no luck so far.
// .attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; })
// .attr("x", function(d) { return d.r * k; })
// .attr("y", function(d) { return d.r * k; })
.attr("width", function(d) { return d.r * k; })
.attr("height", function(d) { return d.r * k; })
Here's JSFIDDLE. I have a d3 circle packing with a raster image inside an svg rect within each node. How do you make the image scale when zooming? The container scales, but the image stays small and repeats when zoomed. Been trying to set the defs correctly, but no luck.
var defs = svg.append("defs")
// .data(nodes)
// .enter()
.append("pattern")
.attr("id", "bg")
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', imageWidthHeight)
.attr('height', imageWidthHeight)
// .attr("transform", "translate(40,80)")
.append("image")
// .html("xlink:href", "img/" + function(d) { return d.image; })
.attr("xlink:href", "http://www.public-domain-photos.com/free-stock-photos-4/travel/yosemite/yosemite-meadows.jpg")
.attr('width', imageWidthHeight)
.attr('height', imageWidthHeight)
// .attr("transform", "translate(40,80)");
Also, can't get the container/image to translate into the center of the circle. I've commented those bits out for now because it screws everything up.
Have tried to apply info from these discussions, but still stuck. Thanks.
http://bl.ocks.org/mbostock/950642#graph.json
https://groups.google.com/forum/#!topic/d3-js/fL8_1BLrCyo
How to fill D3 SVG with image instead of colour with fill?
Adding elements to a D3 circle pack nodes
Answer JSFIDDLE
Got it. The trick was changing this bit of horrible:
(d.x - v[0]) * k
to this even worse bit of horrible:
(((d.x - v[0]) * (k)) - ((d.r / 2) * k))
Then the same for y.
Don't get me wrong, I'm grateful for the zoom circle pack template and the genius(es) who put it together. Thank you. It's just for someone at my noob level, the code above looks like a punishment of some kind. :)

How to style SVG path to take an image?

Here is my JSFiddle.
I am simply trying to set up this image in the middle of my arc. My best intuition tells to use .attr("fill","url('somePicture')"), but for the life of me that hasn't been a viable solution.
var width = 700,
height = 600,
tau = 2 * Math.PI
var arc = d3.svg.arc()
.innerRadius(100)
.outerRadius(250)
.startAngle(0)
var arc2 = d3.svg.arc()
.innerRadius(0)
.outerRadius(100)
.startAngle(0)
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")")
//gray null background
var background = svg.append("path")
.datum({endAngle: tau})
.style("fill", "#ddd")
.attr("d", arc)
var center = svg.append("image")
.append("path")
.datum({endAngle: tau})
.attr("d", arc2)
.attr("class","record")
.attr("xlink:href", "http://lorempixel.com/g/400/400/")
You don't need to define a path. If you look into your html, the image is there but it's of size 0x0.
var center = svg.append("image")
.datum({endAngle: tau})
.attr("d", arc2)
.attr("class","record")
.attr("width",400)
.attr("height",400)
.attr("x",-200)
.attr("y",-200)
.attr("xlink:href", "http://cdn.mysitemyway.com/etc-mysitemyway/icons/legacy-previews/icons/glossy-black-icons-symbols-shapes/018712-glossy-black-icon-symbols-shapes-shapes-circle.png")
In your fiddle you attached the wrong image. If you keep your code the same aside from this it should work. Good luck.
If I understand you correctly, you mean that if you want to resize the whole thing, then the image will change with it. Correct? You can do that by making all numbers functions of others.
I start with defining
innerR = 100
for lack of a better name. Then all other non-zero functions are functions of that. There is a fiddle here. Experiment with that parameter and see what happens.

Categories