I have a large number of rectangles, and some overlap others; each rectangle has an absolute z-order and a colour. (Each 'rectangle' is actually the axis-aligned bounding box of a particle effect, mesh or texture and may be semi-transparent. But its easier to think abstractly about coloured rectangles as long as you don't try to cull rectangles behind others, so I will use that in the problem description:)
The cost of changing the 'colour' is quite high; its much faster to draw two blue rectangles in succession than it is to draw two different-coloured rectangles.
The cost of drawing rectangles that are not even on the screen is quite high too and should be avoided.
If two rectangles do not overlap, the order they are drawn relative to one-another is not important. Its only if they overlap that the z-order is important.
For example:
1 (red) and 4 (red) can be drawn together. 2 (blue) and 5 (blue) can also be drawn together, as can 3 (green) and 7 (green). But 8 (red) must be drawn after 6 (blue). so its either we draw all three red together and draw the blue in two sets, or we draw all the blue together and draw the red in two sets.
And some of the rectangles may move occasionally. (Not all of them; some rectangles are known to be static; others are known to move.)
I will be drawing this scene in JavaScript/webGL.
How can I draw the rectangles in a reasonable order to minimize colour changes, with a good trade-off of JavaScript culling code vs letting the GPU cull?
(Just working out which rectangles overlap and which are visible is expensive. I have a basic quadtree and this sped my scene drawing up immensely (compared to just emitting the draw-ops for the whole scene); now the question is how to minimize OpenGL state changes and concatenate attribute arrays as much as possible)
UPDATE I have created a very simple test app to illustrate the problem and serve as a basis for demonstration of solutions: http://williame.github.com/opt_rects/
The source-code is on github and can easily be forked: https://github.com/williame/opt_rects
It turns out its hard to make a little test app with sufficient state change to actually recreate the problem I see in my full game. At some point you'll have to take it as a given that state changes can be sufficiently expensive. What is also important is how to speed up the spatial index (quadtree in demo) and the overall approach.
You are making the very wrong assumption that the performance you will be getting on the desktop browser will somehow determine the performance on your iPhone. You need to understand that the iPhone hardware implements tile-based deferred rendering which means that the fragment shader is used very late in the pipeline anyway. As Apple themselves say (“Do not waste CPU time sorting objects front to back”), Z-sorting your primitives will get you little performance gain.
But here’s my suggestion: if changing the colour is expensive, just don’t change the colour: pass it as a vertex attribute, and merge the behaviours into one super shader so you can draw everything in one or a few batches without even sorting. Then benchmark and determine the optimal batch size for your platform.
Choose colours, not boxes!
At any point in time, one or more boxes will be paintable, i.e. they are able to be painted next without introducing problems (though possibly introducing a cost due to having a different colour from the most recently painted box).
The question at every point is: What colour should we pick to draw next? It's not necessary to think about picking individual paintable boxes to draw, because as soon as you pick a particular box to draw next, you might as well draw all available boxes of the same colour that can be drawn at that time. That's because painting a box never adds constraints to the problem, it only removes them; and choosing not to paint a paintable box when you could do so without changing the current colour cannot make the solution less expensive than it would otherwise be, since you will later have to paint this box and that may require a colour change. This also means it doesn't matter in which order we paint paintable boxes of the same colour, since we will paint all of them at once in a single "block" of box painting operations.
The dependency graph
Start by building a "lies underneath" dependency graph, where each coloured rectangle is represented by a vertex and there is an arc (arrow) from v to u if rectangle v overlaps rectangle u and lies underneath it. My first thought was to use this to build a "must be drawn before" dependency graph by finding the transitive closure, but actually we don't need to do this, since all the algorithms below care about is whether a vertex is paintable or not. Paintable vertices are the vertices that have no predecessors (in-arcs), and taking the transitive closure does not alter whether a vertex has 0 in-arcs or not.
In addition, whenever a box of a given colour has only boxes of the same colour as its ancestors, it will be painted in the same "block" -- since all those ancestors can be painted before it without changing colours.
A speedup
To cut down on computation, notice that whenever all paintable boxes of some particular colour have no different-coloured descendants, painting this colour won't open up any new opportunities for other boxes to become paintable, so we don't need to consider this colour when considering which colour to paint next -- we can always leave it till later with no risk of increasing the cost. In fact it's better to leave painting this colour till later, since by that time other boxes of this colour may have become paintable. Call a colour helpful if there is at least one paintable box of that colour that has a different-coloured descendant. When we get to the point when there are no helpful colours remaining (i.e. when all remaining boxes overlap only boxes of the same colour, or no boxes at all) then we are done: just paint the boxes of each remaining colour, picking colours in any order.
Algorithms
These observations suggest two possible algorithms:
A fast but possibly suboptimal greedy algorithm: Choose to paint next the colour that produces the most new paintable vertices. (This will automatically consider only helpful colours.)
A slower, exact DP or recursive algorithm: For each possible helpful colour c, consider the dependency graph produced by painting all paintable c-coloured boxes next:
Let f(g) be the minimum number of colour-changes required to paint all boxes in the dependency graph g. Then
f(g) = 1 + min(f(p(c, g)))
for all helpful colours c, where p(c, g) is the dependency graph produced by painting all paintable boxes of colour c. If G is the dependency graph for the original problem, then f(G) will be the minimum number of changes. The colour choices themselves can be reconstructed by tracing backwards through the DP cost matrix.
f(g) can be memoised to create a dynamic programming algorithm that saves time whenever 2 different permutations of colour choices produce the same graph, which will happen often. But it might be that even after DP, this algorithm could take an amount of time (and therefore space) that is exponential in the number of boxes... I will have a think about whether a nicer bound can be found.
Here's a possibility. You'll have to benchmark it to see if it's actually an improvement.
For all rectangles, back to front:
If this rectangle has been marked as drawn, skip to the next one
Set a screen-sized unseen surface to all black
Call this rectangle's color "the color"
For rectangles starting with this one and proceeding toward the front
If (this rectangle's color is the color and
all the pixels of this rectangle on the unseen are black) then
Add this rectangle to the to-draw list
Draw a white rectangle with this rectangle's shape on the unseen surface
If the unseen surface is more than half white, break
For all rectangles on the to-draw list:
Draw the rectangle
Mark it as drawn
It's not guaranteed to be the most optimal in terms of ordering, but I think it will come pretty close, and it's worst-case quadratic in the pre-drawing step. It does depend on readbacks from the graphics buffer being fast. One trick that might help there is to create a new one pixel surface that is a shrunken version of the area of interest. Its color will be the fraction of the original that was white.
Start by drawing in a random (but correct) order, for example in strict z order. When drawing each frame, either count the number of color changes, or possibly the actual time a complete frame takes. Each frame, try swapping the order of two rectangles. The rectangles to be swapped must not overlap, therefore they can be drawn in any order without violating correctness; aside from that they can be chosen at random, or do a linear pass through the list, or... If doing the swap reduces the number of color changes, keep the new order, if not revert it and try a different swap in the next frame. If doing the swap neither reduces nor increases the number of color changes, keep it with 50% odds. For any rectangles which did not overlap in a previous frame but which start overlapping due to a move, simply exchange them so they are in z order.
This has some relationship to sorting algorithms which swap pairs of items, except that we cannot compare items, we need to go through the whole list and count color changes. This will perform very badly at first but converge to a good order relatively quick, and will adapt to scene changes. I think it is probably not worth it to go through and calculate an optimum order every frame; this will get to, and maintain, a near-optimum order with very little extra work.
Referring to the drawing you have: Initial draw order picked at random: 1,6,2,4,5,8,3,7 (5 color changes). Swap 5,8. New order: 1,6,2,4,8,5,3,7 (4 color changes) => Keep new order.
Related
I want to find the text lines in a page of text (like from a book).
Sample image:
One of the problems is that I want to implement this in Javascript and this is the best computer vision library that I found:
http://inspirit.github.io/jsfeat/#imgproc
Therefore I am limited to the algorithms implemented in JSFeat (or another JS library).
I thought of doing feature detection on the page and then doing statistics on the plotted points to find the lines. I'm not sure that's a good idea or how this can be done.
For example this is the output of FAST when applied on that image.
It should work regardless of the font used. Also slight rotation tolerance would be even better.
Help much appreciated!
My approach would be to count the number of vertical edges on each horizontal scanline. Each letter will produce two or more edges.
First, use the sobel operator to calculate x derivative:
Now we have positive and negative edges, but we want to count them both as positive. So take the absolut value:
Now count the edges on each line. This can be done by summing the pixels up, or simply by scaling the image to a width of 1px, leaving the height unchanged. For easy viewing I've plotted the result:
Now you'll need to threshold this result somehow, or maybe find the maxima after running a blur on the 1px-width image. If the font size and the letters per line stay roughly the same, this is easy.
You may want to re-run on different rotations of the original image and then use the result with the highest contrast.
I am making some "statistical" Analysis over different Videos and Images. It´s Histogram-like over every Frame. In each Frame I take 16 prededefined Colors and rate them.
I am drawing Lines (up to down) into an Canvas where every Line can consist of that 16 colors in different intensities. For every Frame there is an new Line in the Canvas.
My Question:
1.) How can I draw Lines with different colors? So far I only came up that I draw an Part of the Line with an specified color then (if needed) draw again from the last point of that Line an new Subline with an new Color. So, in worst case I do that 16 times for one Line. How can I do that more easily?
2.) How can I apply some kind of "intensity" (brighter/darker) to that colors?
3.) Since it´s possible that I have to draw 100.000+ Lines for one Video, what can I do in order to speed up draw time and save memory? (well, at least I guess that that will take some time, I have no code so far and can only assume that...). Would it be an good approach to "precompute" every Line an save that somewhere and when it´s needed to pop it into the Canvas?
Html5 lines are drawn using path commands beginning with context.beginPath. You can draw multiple line segments with a single set of line path commands but each single set can only have one style (== one color). Your workaround (as you suspected) will be to issue a separate beginPath command for each desired colored line segment.
Html5 colors default to the RGBA color format, but you can also use the HSLA color format. This way you can change your "L" value to lighten or darken your hue. (The hue, "H", is basically your color).
Optimizing your line drawings is very dependent on the conditions in your own app. If the lines require significant processing to determine their position, color or other styling then you might (or might not) gain performance by pre-calculating those line values. On balance, canvas draws path commands (lines) very, very quickly so I would certainly start by test drawing your lines "live" to see if the canvas can keep up.
Good luck with your project!
I am working on this browser-based experiment where i am given N specific circles (let's say they have a unique picture in them) and need to position them together, leaving as little space between them as possible. It doesn't have to be arranged in a circle, but they should be "clustered" together.
The circle sizes are customizable and a user will be able to change the sizes by dragging a javascript slider, changing some circles' sizes (for example, in 10% of the slider the circle 4 will have radius of 20px, circle 2 10px, circle 5 stays the same, etc...). As you may have already guessed, i will try to "transition" the resizing-repositioning smoothly when the slider is being moved.
The approach i have tried tried so far: instead of manually trying to position them i've tried to use a physics engine-
The idea:
place some kind of gravitational pull in the center of the screen
use a physics engine to take care of the balls collision
during the "drag the time" slider event i would just set different
ball sizes and let the engine take care of the rest
For this task i have used "box2Dweb". i placed a gravitational pull to the center of the screen, however, it took a really long time until the balls were placed in the center and they floated around. Then i put a small static piece of ball in the center so they would hit it and then stop. It looked like this:
The results were a bit better, but the circles still moved for some time before they went static. Even after playing around with variables like the ball friction and different gravitational pulls, the whole thing just floated around and felt very "wobbly", while i wanted the balls move only when i drag the time slider (when they change sizes). Plus, box2d doesn't allow to change the sizes of the objects and i would have to hack my way for a workaround.
So, the box2d approach made me realize that maybe to leave a physics engine to handle this isn't the best solution for the problem. Or maybe i have to include some other force i haven't thought of. I have found this similar question to mine on StackOverflow. However, the very important difference is that it just generates some n unspecific circles "at once" and doesn't allow for additional specific ball size and position manipulation.
I am really stuck now, does anyone have any ideas how to approach this problem?
update: it's been almost a year now and i totally forgot about this thread. what i did in the end is to stick to the physics model and reset forces/stop in almost idle conditions. the result can be seen here http://stateofwealth.net/
the triangles you see are inside those circles. the remaining lines are connected via "delaunay triangulation algorithm"
I recall seeing a d3.js demo that is very similar to what you're describing. It's written by Mike Bostock himself: http://bl.ocks.org/mbostock/1747543
It uses quadtrees for fast collision detection and uses a force based graph, which are both d3.js utilities.
In the tick function, you should be able to add a .attr("r", function(d) { return d.radius; }) which will update the radius each tick for when you change the nodes data. Just for starters you can set it to return random and the circles should jitter around like crazy.
(Not a comment because it wouldn't fit)
I'm impressed that you've brought in Box2D to help with the heavy-lifting, but it's true that unfortunately it is probably not well-suited to your requirements, as Box2D is at its best when you are after simulating rigid objects and their collision dynamics.
I think if you really consider what it is that you need, it isn't quite so much a rigid body dynamics problem at all. You actually want none of the complexity of box2d as all of your geometry consists of spheres (which I assure you are vastly simpler to model than arbitrary convex polygons, which is what IMO Box2D's complexity arises from), and like you mention, Box2D's inability to smoothly change the geometric parameters isn't helping as it will bog down the browser with unnecessary geometry allocations and deallocations and fail to apply any sort of smooth animation.
What you are probably looking for is an algorithm or method to evolve the positions of a set of coordinates (each with a radius that is also potentially changing) so that they stay separated by their radii and also minimize their distance to the center position. If this has to be smooth, you can't just apply the minimal solution every time, as you may get "warping" as the optimal configuration might shift dramatically at particular points along your slider's movement. Suffice it to say there is a lot of tweaking for you to do, but not really anything scarier than what one must contend with inside of Box2D.
How important is it that your circles do not overlap? I think you should just do a simple iterative "solver" that first tries to bring the circles toward their target (center of screen?), and then tries to separate them based on radii.
I believe if you try to come up with a simplified mathematical model for the motion that you want, it will be better than trying to get Box2D to do it. Box2D is magical, but it's only good at what it's good at.
At least for me, seems like the easiest solution is to first set up the circles in a cluster. So first set the largest circle in the center, put the second circle next to the first one. For the third one you can just put it next to the first circle, and then move it along the edge until it hits the second circle.
All the other circles can follow the same method: place it next to an arbitrary circle, and move it along the edge until it is touching, but not intersecting, another circle. Note that this won't make it the most efficient clustering, but it works. After that, when you expand, say, circle 1, you'd move all the adjacent circles outward, and shift them around to re-cluster.
So I’m trying to draw a simple hue wheel showing the red, green, and blue components and how they relate. The problem is that my (simple, one-degree) arcs are taking on the strokes of later arcs, even though I’m using beginPath() and closePath().
My current progress is at http://meyerweb.com/eric/css/colors/hsl-from-rgb.html. The innermost ring is what’s intended for that ring, with the blue-fade-to-black. The next ring out should show only green-fade-to-black, and the third ring out should have only red-fade-to-black. The outermost, thickest ring is meant to show the full spectrum around the hue wheel, which you can kind of see in the thin spokes (as you can in the other rings).
If I reverse the order of the drawing blocks, then the outermost ring is fine and all the inner rings are messed up, so clearly the stroke styles are leaking forward. I just can’t figure out how or why, nor how to overcome the problem. Should I just define four separate objects (say, ctx1 through ctx4) and draw to each one separately?
I looked at different fillStyle colors for arc in canvas but the recommendations there didn’t seem to help me. Other Googling produced nothing of use.
You can't use decimals for your RGB values, you need to round them.
http://jsfiddle.net/M6KbD/
I didn't take a very long look at your code but the problem is probably not with canvas, its probably the order in which you're drawing everything.
There are 4 sets you've got, right? You are drawing 1 from the first set, 1 from the second, 1 from the third, 1 from the fourth, then another 1 from the first...
If you change the order everything will be fine:
http://jsfiddle.net/muatT/
(lazily, I just copied the entire for-loop cruft as it was. You can probably simplify that code a lot)
I'm looking for some high-level recommendations for how to implement a project I'm starting for a client.
This is a web page that will include an SVG canvas (sized 920px W x 450px H) containing 20-40 "circular" images (i.e., they may just be square images cropped with a circle). The size of the images will probably range from about 50px to about 200px diameter, each one set dynamically within that range based on data from an API. It's basically a dynamic data visualization, so pretty much every aspect of this needs to be configurable/dynamic.
I think the hardest problem to solve is how to distribute these images within the given canvas area, considering that they will vary in size, and should appear to be evenly/randomly distributed (i.e., they shouldn't line up to a grid, be clumped together, or be in groups of similar sizes). It's OK if they overlap slightly. Here's a quick sketch of how this should look, ideally (each gray circle represents an image):
https://skitch.com/troywarr/gwj14/adobe-fireworks-cs5
FWIW, I was planning to use Raphaël as an SVG library; I'll also have jQuery available and can probably use any other libraries as needed. This needs to be cross-browser compatible back to IE7.
Can anyone suggest a general approach to this problem, any specific libraries or algorithms to look into, or provide any other guidance or suggestions? Please let me know if this description isn't clear, or if you need any additional details.
Thanks in advance!
Here is how I would tackle it:
First decide on the percentage of the screen that will be filled in with circles and the number of circles that will be displayed. You can use that to determine the average radius of each circle using the area of a circle formula - e.g. given x circles what would the average radius of all the circles have to be to cover y% of my container. You can then decide how much you want the radius to vary, that is +/- 50%. Unless the number of circles is very small you should get a good result - statistically that is.
Then I would divide the screen in to a rectangular grid - I know that is not what you want just be patient :) The dimensions of the grid are calculable from the number of circles, e.g. 16 circles would fit nicely into a 4 X 4 grid. The number of circles can be less than the number of cells but not a lot less.
I would then select a random x,y co-ordinate inside each grid cell as my circle's center. I would also leave a padding of about 25% around the edges so that my circle is not centered too close to an edge.
You could then check for overlap - the ratio of (r1 + r2) / distance between the circle center points will equal 1 if the circles touch, be less than 1 if they don't and greater than 1 if they overlap. A ratio of 1.1 is a small overlap; careful of the limit here - exact same centers result in a distance of 0 and a division by 0 error.
One thing to worry about but should not happen unless you pick a very high initial coverage percentage or the number if circles is much smaller than the number of cells. If all the cells adjacent to a given cell have circles close to that cells edges, especially if they overlap the edge, there may not be enough room (even with circle overlap) for the current cell's circle. This can be checked for and handled by shrinking the radius or moving a circle away...
NOTE if the number of desired circle's does not exactly match your square (or rectangular) grid, just randomly leave some cells empty...
Here's a rough description of the approach I took after all. Sorry, pressed for time so this may be inexact.
I initially approached the problem similarly to #BigMac66's answer. I laid out a grid corresponding to the quantity of circles I had, drew a circle in each and then randomly shifted the circles from the center point of each grid cell.
I built in a variety of "fudge factors" - circle radius bounds, offset from center, maximum overlap with adjacent cells, etc. - but, no matter how I tweaked my settings, you could still always tell that a grid was behind the layout; the circles aligned just enough to look like a wonky polka dot pattern rather than a truly random collection of circles.
So, I changed to a more brute-force approach.
First, I set up configurable bounds for the radius of a circle, tweaking the upper bound until I found a maximum size that didn't make the canvas look crowded based on the total number of circles I had.
Then, I applied my math to size the circles as needed (in short, the largest circles represented a 100% value, and the smallest represented a 0% value; the rest were sized accordingly based on where they fell in the spectrum).
Then, I sorted the circles descending by size. I laid them out randomly on the canvas, using an algorithm roughly like so:
Place a circle at random coordinates.
Place the next circle at random coordinates.
If the latter circle overlaps the former by more than X pixels (configurable param), place it at a new set of random coordinates.
Continue step #3 for up to Y times (another configurable param) until you successfully place another circle.
If you can't place the circle in Y times, clear the canvas, drop the maximum circle radius by Z pixels (another configurable param) and start over.
This worked surprisingly well, and I tweaked the params until I could draw the entire canvas very quickly while requiring few maximum circle radius reductions.
You can actually see the finished product here:
http://www.eonline.com/news/2012_sag_awards/heatgauge
It's out of season, so click the "Overall View" tab at the top of the main box to see cumulative stats (and hence a full canvas of circles).