generating vertices for voronoi diagram in d3js - d3.js

In the two examples in d3js website:
Easy Version
More complicated
I find some of their code hard to understand. The D3 reference API did not offer in depth explanation. In the easy version, the vertices were generated using Math.random() like so:
var vertices = d3.range(100).map(function(d) {
return [Math.random() * width, Math.random() * height];
});
Width and height are the document size. This ensure all vertices are within the scope of the SVG element ( svg tag has width and height attribute set to these values too).
However in the more complicated version, it uses an algorithm to generate the vertices:
var vertices = d3.range(numVertices).map(function(d) { return {x: d.x, y: d.y}; })
voronoiVertices = vertices.map(function(o){return [o.x, o.y, o]})
path = path.data(d3.geom.voronoi(voronoiVertices))
When examining the variable vertices in console. I found out that it is a two dimensional array. Each sub-array has a length of 3, first and second element are numbers, and the last element is an object which contains a lot more properties like x, y and index and so on.
The resulting Voronoi diagram are very different. Why is that? They are both generated using d3.geom.voronoi() and (from what I can hopefully understand) the only differences lies in the object that is passed into it. In the easy example it was simply a two dimensional array with 2 numbers in each sub-array. I cannot seem to see how the two lines in the complicated example works to create such a complex object and append it to the end of each sub-array.
How is it ensured that the veritices are within the bound of the parent SVG element too?
I am really stuck, thank you so much for any help.

Related

get point coordination using path.getPointAtLength after rotation in d3 and svg

i want get some points from path after rotating it
i use below code to rotate path
let path= svg.append("path")
.attr("class","offset control")
.attr("d", lineFunction(offsetLineData))
.style("stroke-width", 0.5)
.style("stroke", "red")
.style("fill", "none")
.attr("transform","rotate(90,"+p.x+","+p.y+")")
.attr('transform-origin',"center")
then i want get end point and start point of path
path.node().getPointAtLength(0)
but it return coordination where it don't rotated
how can i get x and y of point after rotation
Think of a SVG as a tree of nested coordinate systems. Every element has its own coordinates, and is fitted into its parent with a rule how to re-calculate them. These rules can be explicit transform attributes, but also implicit combinations of width, height and viewBox ("fit box A in size B").
A transform attribute is considered to be the link to the parent element. That means, if you ask for a geometric property of an element, most of the times you get the value before the transform is applied. After it is applied would be asking for geometric values in the coordinate system of the parent.
Because of this complexity there are several SVG API functions to find out how these coordinate systems fit together. getPointAtLength() gets you coordinates before applying the transform attribute.
var localPoint = path.node().getPointAtLength(0)
First, you have to find out what the transform attribute does. This looks a bit complicated, partly because the attribute can be animated and can contain a list of functions, partly because the API is...well...:
// construct a neutral matrix
var localMatrix = path.node().viewportElement.createSVGMatrix()
var localTransformList = path.node().transform.baseVal
// is there at least one entry?
if (localTransformList.length) {
// consolidate multiple entries into one
localMatrix = localTransformList.consolidate().matrix
}
You can then apply the found transformation to the point with
var transformedPoint = localPoint.matrixTransform(localMatrix)
There are several functions hat will return a SVGMatrix to transform data from the coordinate system after application of the transform attribute (i. e. after the step above):
to ask for the transformation to the nearest viewport (in most cases the nearest parent <svg>) element: element.getCTM()
to ask for the transformation to screen pixels: element.getScreenCTM()
to ask for the transformation to an arbitrary element: element.getTransformToElement(...)

mapping values to d3.voronoi in p5.js

i've been working with p5.js for a while, but i just picked up d3/p5 to make some voronoi diagrams. I have an existing program where an array of 512 points are mapped to the screen. I want to use those points to make a voronoi diagram.
the only relevant reference i can find is http://codepen.io/sepans/pen/Qbgaby
however, this program operates solely within function setup(), as it is a static program. this is the beginning:
function setup() {
var width = 1500,
height = 1000;
// randomly generate vertices in [[123,57],[43,67], ..] format
var vertices = d3.range(100).map(function(d) {
return [Math.random() * width, Math.random() * height];
});
// using d3.js voronoi layout to calculate voronoi polygons
var voronoi = d3.geom.voronoi()
.clipExtent([
[0, 0],
[width, height]
]);
I unsure how to move these elements into function draw(), so that my continuously dynamic variables can feed into the diagram.
this is probably not 100% clear so if anyone has any advice as to how i can explain my problem more effectively that would be great. Thanks!
The key to this program is this line:
var vertices = d3.range(100).map(function(d) {
return [Math.random() * width, Math.random() * height];
});
Please try to understand what this code is doing. It's filling the vertices array with 100 random points.
You say you want to fill your diagram with points you gather from somewhere else. You need to get rid of this line and populate vertices with your points. The best advice I can give you is to break your problem down into smaller pieces and take those pieces on one at a time.
For example, get this working with just a couple hard-coded points. Then try to add points when you click the mouse. Don't worry about your final end goal yet, just get these simple examples working. Then if you get stuck, you can post a MCVE along with a more specific question. Good luck.

D3.js breaking up overlapping shapes

I'm trying to create a chart, where the input is a list of circles (position and radius) (or better ellipses) and the overlaps of the circles become shapes and a mouseover event can be applied. I also wish for the circles to move to the front, and have a mouseover effect, almost exactly like this
http://benfred.github.io/venn.js/examples/intersection_tooltip.html
The size of the overlap does not need to be known.
I've tried using D3.js Venn diagrams by Ben Frederickson. Although I can't understand some of the chart(selection) function, I've made it so that the circles can be inputted, and are drawn fine, including the overlaps, but this still relies on having the 'data' as an input as well and all of the sets (seen in the jsonp file) are still require. I realise that I can just make a script to list all of the possible sets, but this is ideal.
http://www.benfrederickson.com/venn-diagrams-with-d3.js/
I'm struggling to understand how the code creates these overlaps and then assigns them to the set.
Cheers, Ryan
Each intersection area has an SVG path computed for it by the 'venn.intersectionAreaPath' function. It takes a list of circles and returns a path element for the intersection area.
If you already have positions for the circles, you can override the 'layoutFunction' attribute on the venn diagram object like:
var circles = [{'x' : 0, 'y': 100, 'radius' : 80},
{'x' : 0, 'y': 0, 'radius' : 90 },];
var chart = venn.VennDiagram().layoutFunction(function() { return circles; });
d3.select("#venn").datum([{sets: [0]}, {sets:[1]}, {sets:[0,1]}]).call(chart);
This still requires having a list of all possible regions that you wish to draw (like "[{sets: [0]}, {sets:[1]}, {sets:[0,1]}]"), but this way you don't need to specify sizes for the regions.

How to pass different arrays to d3.svg.line() function?

I have seen an example of d3.svg.line() being as follows:
var altitude = some_array() // I read this from a .json file
y = d3.scale.linear().domain([0, d3.max(altitude)]).range([0 + margin, h - margin]),
x = d3.scale.linear().domain([0, altitude]).range([0 + margin, w - margin])
var line = d3.svg.line()
.x(function(d,i) { return x(i); })
.y(function(d) { return -1 * y(d); })
some_svg_group.append("svg:path").attr("d", line(altitude));
although I didn't quite understood what is actually happening, I get that this is a sort of generator function, where line().x and line().y are sort of generator functions, and the idiom shown is a way to have an equally spaced x array versus the actual values in the y array.
What I would like to do, though, is to pass TWO arrays to d3.svg.line(), say distance and altitude, more or less like this:
var line = d3.svg.line()
.x(function_that_gives_distance)
.y(function_that_fives_altitude)
some_svg_group.append("svg:path").attr("something", line(some_other_thing));
Any suggestion on what to replace those placeholders with? Or, if they are wrong, how to achieve that at all?
line.x and line.y are actually accessors. The array you pass into the line generator represents a set of data points and the accessor functions are used to map each data point onto appropriate x and y pixel coordinates.
So with that in mind, it probably doesn't make sense represent the same data points using 2 separate arrays. You should try to transform distance and altitude into a single array of data points ahead of time. Then, you can defined your x and y accessors to map the properties on your data point to the correct pixel locations. Using your placeholder terminology, I would think that function_that_gives_distance and function_that_gives_altitude would actually be invoked as part of the construction of the data points, not in the line generator's accessors.
To summarize, break apart the workflow into 2 steps:
Transform the data into a set of data points you want to base the line on.
Use the line generator accessor functions to do a projection of these data points onto the drawing system's coordinates (i.e. pixel positions).

D3 force layout by node size

I'm trying to create a visualization with D3 such that nodes are differently sized by a particular attribute and bigger nodes go to the center and smaller nodes go to the outside. I have sizing and clustering and collision detection working, but I can't figure out how to tell the bigger nodes to go to the center.
I've tried messing with the charge, but couldn't convince that to work. I got linkDistance to move the bigger ones to the center, but (a) getting there was VERY jittery and (b) the smaller ones are way outside rather than tightly packed. The linkDistance is still in the code, just commented out.
It's up at http://pokedex.mrh.is/stats/index.html:
The relevant code (I assume) is also below. The nodes are sized per their attr attribute. Oh, and the nodes are Pokémon.
force = d3.layout.force()
// .gravity(0.05)
// .charge(function(d, i) { return d.attr; })
// .linkDistance(function(d) {
// return 50000/Math.pow(d.source.attr+d.target.attr,1);
// })
.nodes(pokemon)
// .links(links)
.size([$(window).width(), $(window).height()]);
The following gave me a less jittery version of what you have now.
force = d3.layout.force()
.gravity(0.1)
.charge(function(d, i) { return -d[selectedAttr]})
.friction(0.9)
.nodes(pokemon)
.size([$(window).width(), $(window).height()]);
To answer your actual question, each node's coordinates are currently being placed in your graph at random. I quote from the D3 documentation:
When nodes are added to the force layout, if they do not have x and y attributes already set, then these attributes are initialized using a uniform random distribution in the range [0, x] and [0, y], respectively.
From my experience, there's no magic force method that gets the nodes you want to the center of the map. The way that I've accomplished your desired result in the past has been by replacing the randomized coordinates of each node with coordinates that place the nodes in a the desired order, expanding from the center of the map.

Resources