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 })
}
}
Related
I have a Goldberg polyhedron that I have procedurally generated. I would like to draw an outline effect around a group of “faces” (let's call them tiles) similar to the image below, preferably without generating two meshes, doing the scaling in the vertex shader. Can anyone help?
My assumption is to use a scaled version of the tiles to write into a stencil buffer, then redraw those tiles comparing the stencil to draw the outline (as usual for this kind of effect), but I can't come up with an elegant solution to scale the tiles.
My best idea so far is to get the center point of the neighbouring tiles (green below) for each edge vertex (blue) and move the vertex towards them weighted by how many there are, which would leave the interior ones unmodified and the exterior ones moved inward. I think this works in principle, but I would need to generate two meshes as I couldn't do scaling this way in the vertex shader (as far as I know).
If it’s relevant this is how the polyhedron is constructed. Each tile is a separate object, the surface is triangulated with a central point and there is another point at the polyhedron’s origin (also the tile object’s origin). This is just so the tiles can be scaled uniformly and protrude from the polyhedron without creating gaps or overlaps.
Thanks in advance for any help!
EDIT:
jsb's answer was a simple and elegant solution to this problem. I just wanted to add some extra information in case someone else has the same problem.
First, here is the C# code I used to calculate these UVs:
// Use duplicate vertex count (over 4)
var vertices = mesh.vertices;
var uvs = new Vector2[vertices.Length];
for(int i = 0; i < vertices.Length; i++)
{
var duplicateCount = vertices.Count(s => s == vertices[i]);
var isInterior = duplicateCount > 4;
uvs[i] = isInterior ? Vector2.zero : Vector2.one;
}
Note that this works because I have not welded any vertices in my original mesh so I can count the adjoining triangles by just looking for duplicate vertices.
You can also do it by counting triangles like this (this would work with merged vertices, at least with how Unity's mesh data is laid out):
// Use triangle count using this vertex (over 4)
var triangles = mesh.triangles;
var uvs = new Vector2[mesh.vertices.Length];
for(int i = 0; i < triangles.Length; i++)
{
var triCount = triangles.Count(s => mesh.vertices[s] == mesh.vertices[triangles[i]]);
var isInterior = triCount > 4;
uvs[i] = isInterior ? Vector2.zero : Vector2.one;
}
Now on to the following problem. In my use case I also need to generate outlines for irregular tile patterns like this:
I neglected to mention this in the original post. Jsb's answer is still valid but the above code will not work as is for this. As you can see, when we have a tile that is only connected by one edge, the connecting vertices only "share" 2 interior triangles so we get an "exterior" edge. As a solution to this I created extra vertices along the the exterior edges of the tiles like so:
I did this by calculating the half way point along the vector between the original exterior tile vertices (a + (b - a) * 0.5) and inserting a point there. But, as you can see, the simple "duplicate vertices > 4" no longer works for determining which tiles are on the exterior.
My solution was to wind the vertices in a specific order so I know that every 3rd vertex is one I inserted along the edge like this:
Vector3 a = vertex;
Vector3 b = nextVertex;
Vector3 c = (vertex + (nextVertex - vertex) * 0.5f);
Vector3 d = tileCenter;
CreateTriangle(c, d, a);
CreateTriangle(c, b, d);
Then modify the UV code to test duplicates > 2 for these vertices (every third vertex starting at 0):
// Use duplicate vertex count
var vertices = mesh.vertices;
var uvs = new Vector2[vertices.Length];
for(int i = 0; i < vertices.Length; i++)
{
var duplicateCount = vertices.Count(s => s == vertices[i]);
var isMidPoint = i % 3 == 0;
var isInterior = duplicateCount > (isMidPoint ? 2 : 4);
uvs[i] = isInterior ? Vector2.zero : Vector2.one;
}
And here is the final result:
Thanks jsb!
One option that avoids a second mesh would be texturing:
Let's say you define 1D texture coordinates on the triangle vertices like this:
When rendering the mesh, use these coordinates to look up in a 1D texture which defines the interior and border color:
Of course, instead of using a texture, you can just as well implement this behavior in a fragment shader by thresholding the texture coordinate, conceptually:
if (u > 0.9)
fragColor = white;
else
fragColor = gray;
To update the outline, you would only need upload a new set of tex coords, which are just 1 for vertices on the outline and 0 everywhere else.
Depending on whether you want the outlines to extend only into the interior of the selected region or symmetrically to both sides of the boundary, you would need to specify the tex coords either per-corner or per-vertex, respectively.
So, I'm using snap.svg and I'd like to dynamically rotate an object over time. Something like this:
function rotateObject()
{
myObject.rotation += value;
}
The problem is I don't know how to access the rotation values for my display objects (or if they even exist!) So given something simple, let's say a circle declared like this:
snap = Snap(800,600);
circle = snap.circle(400,300,50);
I know I can access the x and y values like so:
circle.attr("cx");
circle.attr("cy");
What I need help with is:
Is there a rotation property of some sort that I can use to rotate this object?
If not, how do I rotate an object with snap.svg?
Better rotate objects using Snap.Matrix()
The way suggested by Ian, works for me perfectly while I used Chrome version < 36.0
When I updated Chrome to 36.0.1985.125 I saw bug with text rotation.
So, the soulution was using
var matrix = new Snap.Matrix();
matrix.rotate(-90, x, y);
Paper.text(x, y, 'Text').attr({
fontWeight: 'bold',
fill: '#434343',
transform: matrix
});
instead of
Paper.text(x, y, 'Text').attr({
fontWeight: 'bold',
fill: '#434343',
transform: 'r-90'
});
Maybe it will be useful for somebody.
Ideally you will control the rotation yourself, (rather than figuring it out from the attributes which is possible, but fiddlier). Animation can be easier, depending on what you need. Here is an example showing some basic animation with a rect (as circle rotation is just itself if around the centre)...
s = Snap(400, 620);
var myRect = s.rect(100, 100, 100, 200).attr({
fill : 'white',
stroke : 'black'
});
var myRotate = 45;
// if you wanted to rotate manually using your own adjust variable then this
// myRect.transform("r" + myRotate);
// but simpler in most cases to use the animate method
myRect.animate( { transform: "r" + myRotate + ",150,200" }, 1000 ); //rotate around centre of 150,200
Fiddle at http://jsfiddle.net/XG7ks/6/
Really it would probably be best to get a basic grounding on transformations with SVG (and translate, rotate, scale) just for it to make a bit more sense. You can 'see' the resultant transform with myRect.attr('transform') but I would probably leave that just at first.
For a project we are trying to make a circle into a line (and back again) while it is rotating along a linear path, much like a tire rotates and translates when rolling on a road, or a curled fore finger is extended and recurled into the palm.
In this Fiddle, I have a static SVG (the top circle) that rotates along the linear black path (which is above the circle, to mimic a finger extending) that is defined in the HTML.
I also use d3 to generate a "circle" that is made up of connected points (and can unfurl if you click on/in the circle thanks to #ChrisJamesC here ), and is translated and rotated
in the function moveAlongLine when you click on the purple Line:
function moveAlongLine() {
circle.data([lineData])
.attr("transform", "translate(78.5,0) rotate(-90, 257.08 70) ")
.duration(1000)
circle.on("click", transitionToCircle)
}
The first problem is that the .duration(1000) is not recognized and throws a Uncaught TypeError: Object [object Array] has no method 'duration' in the console, so there is a difference between the static definition of dur in SVG and dynamically setting it in JS/D3, but this is minor.
The other is should the transform attributes be abstracted from one another like in the static circle? in the static circle, the translate is one animation, and the rotation is another, they just have the same star and duration, so they animate together. How would you apply both in d3?
The challenge that I can not get, is how to let it unroll upwards(and also re-roll back), with the static point being the top center of the circle also being the same as the leftmost point on the line.
these seem better:
I should try to get the unfurl animation to occur while also rotating? This seems like it would need to be stepwise/sequential based...
Or Consider an octogon (defined as a path), and if it were to rotate 7 of the sides, then 6, then 5.... Do this for a rather large number of points on a polyhedron? (the circle only needs to be around 50 or so pixels, so 100 points would be more than enough) This is the middle example in the fiddle. Maybe doing this programmatically?
Or This makes me think of a different way: (in the case of the octogon), I could have 8 line paths (with no Z, just an additional closing point), and transition between them? Like this fiddle
Or anything todo with keyframes? I have made an animation in Synfig, but am unsure ho get it to SVG. The synfig file is at http://specialorange.org/filedrop/unroll.sifz if you can convert to SVG, but the xsl file here doesn't correctly convert it for me using xsltproc.
this seems really complicated but potential:
Define a path (likely a bézier curve with the same number of reference points) that the points follow, and have the reference points dynamically translate as well... see this for an concept example
this seems complicated and clunky:
Make a real circle roll by, with a growing mask in front of it, all while a line grows in length
A couple of notes:
The number of points in the d3 circle can be adjusted in the JS, it is currently set low so that you can see a bit of a point in the rendering to verify the rotation has occurred (much like the gradient is in the top circle).
this is to help students learn what is conserved between a number line and a circle, specifically to help learn fractions. For concept application, take a look at compthink.cs.vt.edu:3000 to see our prototype, and this will help with switching representations, to help you get a better idea...
I ended up using the same function that generates the circle as in the question, and did a bit of thinking, and it seemed like I wanted an animation that looked like a finger unrolling like this fiddle. This lead me to the math and idea needed to make it happen in this fiddle.
The answer is an array of arrays, with each nested array being a line in the different state, and then animate by interpolating between the points.
var circleStates = [];
for (i=0; i<totalPoints; i++){
//circle portion
var circleState = $.map(Array(numberOfPoints), function (d, j) {
var x = marginleft + radius + lineDivision*i + radius * Math.sin(2 * j * Math.PI / (numberOfPoints - 1));
var y = margintop + radius - radius * Math.cos(2 * j * Math.PI / (numberOfPoints - 1));
return { x: x, y: y};
})
circleState.splice(numberOfPoints-i);
//line portion
var lineState = $.map(Array(numberOfPoints), function (d, j) {
var x = marginleft + radius + lineDivision*j;
var y = margintop;
return { x: x, y: y};
})
lineState.splice(i);
//together
var individualState = lineState.concat(circleState);
circleStates.push(individualState);
}
and the animation(s)
function all() {
for(i=0; i<numberOfPoints; i++){
circle.data([circleStates[i]])
.transition()
.delay(dur*i)
.duration(dur)
.ease("linear")
.attr('d', pathFunction)
}
}
function reverse() {
for(i=0; i<numberOfPoints; i++){
circle.data([circleStates[numberOfPoints-1-i]])
.transition()
.delay(dur*i)
.duration(dur)
.ease("linear")
.attr('d', pathFunction)
}
}
(Note: This should be in comments but not enough spacing)
Circle Animation
Try the radial wipe from SO. Need to tweak it so angle starts at 180 and ends back at same place (line#4-6,19) and move along the X-axis (line#11) on each interation. Change the <path... attribute to suit your taste.
Line Animation Grow a line from single point to the length (perimeter) of the circle.
Sync both animation so that it appears good on all browsers (major headache!).
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 })
I think I may have stumbled onto a limitation of Flot, but I'm not sure. I'm trying to represent a single data series over time. The items' "State" is represented on the Y-Axis (there are 5 of them), and time is on the X-Axis (items can change states over time). I want the graph to have points and lines connecting those points for each data series.
In addition to tracking an item's State over time, I'd also like to represent it's "Status" at any of the particular points. This I would like to do by changing the color of the points. What this means is a single item may have different Statuses at different times, meaning for a single data series I need a line that connects different points (dots) of different colors.
The only thing I've seen so far is the ability to specify the color for all points in a given dataseries. Does anyone know if there's a way to specify colors individually?
Thanks.
There you go mate. You need to use a draw hook.
$(function () {
var d2 = [[0, 3], [4, 8], [8, 5], [9, 13]];
var colors = ["#cc4444", "#ff0000", "#0000ff", "#00ff00"];
var radius = [10, 20, 30, 40];
function raw(plot, ctx) {
var data = plot.getData();
var axes = plot.getAxes();
var offset = plot.getPlotOffset();
for (var i = 0; i < data.length; i++) {
var series = data[i];
for (var j = 0; j < series.data.length; j++) {
var color = colors[j];
var d = (series.data[j]);
var x = offset.left + axes.xaxis.p2c(d[0]);
var y = offset.top + axes.yaxis.p2c(d[1]);
var r = radius[j];
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(x,y,r,0,Math.PI*2,true);
ctx.closePath();
ctx.fillStyle = color;
ctx.fill();
}
}
};
var plot = $.plot(
$("#placeholder"),
[{ data: d2, points: { show: true } }],
{ hooks: { draw : [raw] } }
);
});
With 3 views, it may not be worth answering my own question, but here's the solution:
My original problem was how to plot a dataseries of points and a line, but with each point being a color that I specify.
Flot only allows specifying colors of the dots at the dataseries level, meaning each color must be its own dataseries. With this in mind, the solution is to make a single dataseries for each color, and draw that dataseries with only points, and no lines. Then I must make a separate dataseries that is all of the dots I want connected by the line, and draw that one with no points, and only a line.
So if I want to show a line going through 5 points with five different colors, I need 6 dataseries: 5 for each point, and 1 for the line that connects them. Flot will simply draw everything on top of each other, and I believe there's a way to specify what gets shown on top (to make sure the dots are shown above the line).
Actually, it's not very difficult to add a feature to flot that would call back into your code to get the color for each point. It took me about an hour, and I'm not a javascript expert by any measure.
If you look at drawSeriesPoints(), all you have to do is pass a callback parameter to plotPoints() which will be used to set ctx.strokeStyle. I added an option called series.points.colorCallback, and drawSeriesPoints() either uses that, or a simple function that always returns the series.color.
One tricky point: the index you should pass to your callback probably isn't the i in plotPoints(), but rather i/ps.
Hope this helps!