Placement of SVG circle that lacks center coordinates - svg

djvanderlaan's d3.js Planetarium first defines a "sun" circle at the center of the SVG area:
svg.append("circle").attr("r", 20).attr("cx", w/2)
.attr("cy", h/2).attr("class", "sun")
and then defines two orbits around the sun (with code slightly rearranged for clarity here):
var planets = [
{ R: 300, r: 5, speed: 5, phi0: 90},
{ R: 150, r: 10, speed: 2, phi0: 190}
];
var container = svg.append("g")
.attr("transform", "translate(" + w/2 + "," + h/2 + ")")
container.selectAll("g.planet").data(planets).enter().append("g")
.attr("class", "planet")
.each(function(d, i) {
d3.select(this).append("circle").attr("class", "orbit")
.attr("r", d.R)
d3.select(this).append("circle").attr("r", d.r).attr("cx",d.R)
.attr("cy", 0).attr("class", "planet");
});
The first circle in each group--the "orbit" circle--is never given center coordinates cx and cy. That's not just in the source code; I looked at the "orbit" circles in the inspectors in three browsers, and there is no cx or cy for the orbit circles. However, these circles are centered on the center of the SVG area, i.e. on x=w/2, y=h/2. How does the browser know where to place these circles? Does it inherit cx and cy from the enclosing g element? From the "sun"?

Yes, all svg elements inherit the transform and scale of their parent svg:g elements. You can use this to set a local center, as done here, or to play with scale and rotate with fine precision (since setting these all with the transform attribute can sometimes lead to unexpected results).
Often, people place their circle elements inside a parent g and position the g without ever setting cx/cy because a circle defaults to centering on the center of its parent. This isn't the case with svg:rect elements, which have to be offset to "center" them.

Related

Generating arrays for lithography with variable dimensions

First I must apologize because I am a chemist and not a programmer, so I am venturing far into unknown territory.
I am working on developing some patterns for micro-contract printing to create templates for controlled cellular growth. The master cast for the template is printed on A4 transparency and regardless of how much space I use, the cost is more or less the same. One of my patterns is about 2 x 2 mm, so you can imagine how many I can fit on the master template.
With that in mind what I would like to do is generate a repeating array of circles and tracks. This is easily accomplished in adobe illustrator, but it has become tedious. I would like to automate the ability to vary the dimensions of the circles, the width of the tracks connecting them, and the spaces between the circles.
For example, I might want a 20 x 20 grid of 30 um circles connected with a 10 um wide track with circles that are 150 um between the edges.
I was hoping to do this in Matlab, because I'm currently learning Matlab for some image processing capabilities.
An example of what the final product looks like can be seen:
http://www.nature.com/srep/2014/140424/srep04784/full/srep04784.html
http://pubs.rsc.org/en/Content/ArticleLanding/2011/LC/c1lc20257j#!divAbstract
I would appreciate some direction in:
Is doing this in Matlab even a good idea to begin with?
Setting up a code to generate a "grid" of circles
Connecting those circles with vertical, horizontal or diagonal tracks
I feel like this something someone has done before, so even pointing me to similar code that I could study would be a lot of help
Thanks!
I'm not very used to Matlab so I can't tell you for 0).
Below is a possible answer for 1) and 3). If you think my answer can help you, I can write some code about 2).
The library d3.js might be of interest for what you're doing. It basically allows you to bind data to svg elements.
Here is an example of what you could do. Let's say your data is a list of circle properties (size, position)
JSFiddle here
data = [ {x: 20µm, y:250µm, radius: 250µm}, {....}, ... ]
//Dimensions of what will be dsplayed in your browser
width = 500px
height = 700px
//d3.js provides functions to automatically resize your data to the viewport (maps domain -> range)
xScale = d3.scale.linear()
.domain([0, a4_format_size_x])
.range([0, width])
yScale = d3.scale.linear()
.domain([0, a4_format_size_y])
.range([0, height])
rScale = d3.scale.linear()
.domain([0, max_circle_size])
.range([0, 20])
svg = d3.select(element[0])
.append("svg")
.attr("width", width)
.attr("height", height)
svg.selectAll("circle")
.data(data) // This line binds your date to SVG elements
.enter()
.append("circle")
.attr("cx", function(data) { return xScale(data.x)})
.attr("cy", function(data) { return yScale(data.y)})
.attr("r", function(data) { return rScale(data.radius)}
Note : the syntax selectAll > enter > append might seem weird at first, if you're puzzled feel free to have a look at this
Now for the generation of the data itself (a "grid of circles"), you could have something like
var numCirclesX = 500
var numCirclesY = 700
var data = []
for(var i=0; i<numCirclesX; i++){
for(var j=0; j<numCirclesY, j++){
data.push({ x: i*size_A4_x/numCirclesX,
y: j*size_A4_y/numCirclesY,
radius: 5 })
}
}

D3 pan+ zoom constraints

I'm trying to zoomable/draggle rectangle from going outside of the svg bounds when panning and zooming. I've tried to implement it based off of this example, but i cant seem to get it to work. I've created this jsfiddle with just the rectangle that is zoomable and draggable. Again, im trying to make it so that you can not drag the rectangle outside of the svg box i put the border on. I know i need to update the move function. the code below is from the first link example but it does not seem to work well so i commented part of it out.
function move() {
var t = d3.event.translate,
s = d3.event.scale;
//t[0] = Math.min(width / 2 * (s - 1), Math.max(width / 2 * (1 - s), t[0]));
//t[1] = Math.min(height / 2 * (s - 1) + 230 * s, Math.max(height / 2 * (1 - s) - 230 * s, t[1]));
//zoom.translate(t);
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
Edit: So additionally i need to be able to drag the rectangle when you are zoomed in all the way and its bigger than the svg. In the image below, the blue rectangle is the svg and green would be the rectangle and you are zoomed in all the way so that the green rectangle takes up the much more than the SVG. This is similar to the map in the constrained zoom example. You can zoom into the states and drag across the country, navigating to states outside the current svg size
You can do this by constraining the translation coordinates you set to the size of the box:
var t = d3.event.translate,
s = d3.event.scale;
t[0] = Math.max(0, Math.min(t[0], width - s*50));
t[1] = Math.max(0, Math.min(t[1], height - s*50));
svg.attr("transform", "translate(" + t + ")scale(" + d3.event.scale + ")");
This is constraining the x coordinate to be between 0 and the width minus however much space is required to show the box completely -- this depends on the zoom level and the term therefore contains s. For the y coordinate, it is exactly the same.
This is much easier if you don't use both a translation and explicit coordinate settings through x and y for the box -- to offset from the top left corner, simply set an initial translation.
Complete example here.

How to get polygon points in Fabric.js

I am drawing polygon by capturing mouse clicks on canvas and then passing these points to fabric.Polygon. So, in this manner I'm drawing multiple polygons.
What I need know is, I want to get the mouse co-ordinates (pixel points on canvas) for the polygon which is selected now?
I have tried with:
canvas.getActiveObject().get('points');
But this is giving some negative and some positive values.
So, can u please tell me a way to find out the polygon points?
Polygon points are relative to its center so you can get their "absolute" position like so:
var polygon = canvas.getActiveObject();
var polygonCenter = polygon.getCenterPoint();
var translatedPoints = polygon.get('points').map(function(p) {
return {
x: polygonCenter.x + p.x,
y: polygonCenter.y + p.y
};
});
Let's check how this looks:
translatedPoints.forEach(function(p) {
canvas.getContext().strokeRect(p.x-5, p.y-5, 10, 10);
});
I think this will only work if polygon's angle is at 0 (otherwise need to "rotate" points coordinates as well).
It looks like that from version 2.0 they changed the coordinates of the polygon. Before 2.0 points relative to the center of the polygon; after 2.0 they are absolute to the canvas;
Check out my response to the similar questions https://stackoverflow.com/a/53710375/4681279

How to create a bar chart made up of very small squares

I am trying to use d3 to create a chart which will end up being almost like a bar chart. However, I would like to accomplish this by using very small individual squares. Something like the image below but ignoring the random squares dotted around:
http://i.imgur.com/jYSyhur.jpg
Each square represents a vote (or group of votes depending on how many votes are made on a given day) and can have 3 possible values, each with a different shade of blue, i.e. light blue, blue and dark blue.
One example I have already found is http://bost.ocks.org/mike/miserables/ but I would like to convert this style in to a bar chart.
I have already attempted at doing the markup in HTML using tables and divs, but this got widely out of control and ending up massively slowing down the page loading speed -- hence the use of SVG instead.
Any ideas on how to approach this would be appreciated.
The basis for what you want to do is the stacked bar chart: http://bl.ocks.org/mbostock/3886208
However, there are a few things you will need to take into account. The y and x axes must be proportional so you will have to carefully consider:
the size of the graph
the number of data points
the max value each data point can have
and select a height, width and "value" for each tick - each square.
I've made a demonstration fiddle here: http://jsfiddle.net/sa5RK/
It assumes a few things (for simplicity!):
Each tick is one value
The height & width of the box is set
var boxheight = 6;
the height / width can be dynamic based on the size of the box and data values
var margin = {top: 20, right: 20, bottom: 80, left: 40},
width = boxheight * data.length;
var max = d3.max(data, function(d){return d.a + d.b + d.c});
var height = max * boxheight;
I hope that's enough to get you started!
The other key thing which will help you out, is learning how each data joined selection can be set to a variable, then selecting inside of it will allow you access to the outside bound data. eg. below, each data group (value in the original array) -> has types (a,b or c) -> has rectangles
var groups = svg.selectAll(".group")
.data(data)
.enter().append("g")
.attr("transform", function(d,i){return "translate(" + x(i) + ", 0)"})
.attr("class", "group")
var types = groups.selectAll(".type")
.data(function(d){return d.offsets})
.enter().append("g")
.attr("transform", function(d){ return translate(0,y(d.y1))})
.attr("class", "type")
.attr("fill", function(d){return color(d.type)})
types.selectAll("rect")
.data(function(d){return d3.range(0,d.value)})
.enter().append("rect")
.attr("height", boxheight-0.5)
.attr("width", boxheight-0.5)
.attr("y", function(d){ return boxheight * d })

d3.js - how to change x position of rect on time scale axis?

I'm drawing bar graphs on time scale x-axis.
For example,
var x = d3.time.scale()
.domain([minDate, maxDate])
.range([0, width]);
chart.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", bar_graph.x())
.attr("y", d.value)
.attr("width", 10);
The left corner of the rect starts at the assigned x-point, but I want to assign the x-point to the center of the rect.
Since it's time scale, simple substraction x-5 (assuming the width is 10px) didn't work. I tried invert(), also checked svg reference, but fail.
The problem is indeed in setting the x attribute, it appears the issue lies in the bar_graph.x() statement, try doing something like this:
.attr("x", function(d) {return x(d.dateVal)-barwidth/2;})
See a full implementation here: http://jsfiddle.net/qAHC2/4/

Resources