I'm working on a visualization with pixel data and I have been able to successfully load data onto the canvas. Data is about the type of clouds on the globe. There are about 12 types. Every type has a color on the map. Right now I'm basically plotting every coordinate of the pixel where the color is of a specific cloud and then creating svg for it and loading it too the canvas. But when I plot the pixel data, it creates too many svg's.
However, some of the pixels are like next to each other and I was thinking that is there any way I can join those two pixels that are next to each other. I have a list of coordinates:
Cloud1 = [(1, 1), (1, 2), (2, 1), (2, 2), .... ]
In this case I would combine the four coordinates into one svg, since they are next to each other.
Also, one more thing, How many svg's can you load into a browser(max) at once without any lag?
Image URL: http://i.stack.imgur.com/beB8f.png
To answer the specific question of joining pixels that are adjacent to each other, use a bin method to accumulate touching/adjacent pixels.
Sort all the cloud's pixels by their pixel position (x,y). Iterate through each pixels, check against all existing bins(arrays of pixels), and if none are touching any points in any of the bins, create a new bin with that pixel as the first item. If the current pixel touches an item in the first bin found, add that pixel to that bin and go to the next pixel in the cloud.
After all the pixels in cloud have been divided into separate bins, get the outer x,y values of each bin to draw a polyline representing the subcloud. So essentially you have 1 svg representing each bin.
This should reduce your number of svg elements greatly and you have the added bonus of colouring the area of the polyline cloud with an opaque color which may overlay other cloud type very nicely.
Related
I have 2 bmp images. ImageA is a screenshot (example) ImageB is a subset of that. Say for example, an icon.
I want to find the X,Y coordinates of ImageB within ImageA (if it exists).
Any idea how I would do that?
This is called optical-recognition. It may seem complicated (it is) but can be very simple in implementation, so don't shy away from it!
Let Image A be the image we're looking for, and Image B be the larger image with Image A in it.
Method 1
If Image A's scale in Image B hasn't been altered, and the colors are all preserved, you can place Image B on an HTML 5 canvas and iterate over the pixel data. You would load the first line of pixels from Image A and then iterate over every pixel in Image B. If a pixel was the same, you would store that pixels column in a variable and check if the next matched too. If the first row was a full match, then hop to the next row and compare those. You'd repeat that until you either got a match or hit an (or enough) pixels that didn't match. In that case, you would reset all variables and start all over again looking for a match to row 1.
Method 2
If Image A isn't perfectly identical in Image B, new complications arise and things become a lot more complicated. If only the scale changes, we can make a few tweaks to Method 1 to get something that works. Instead of grabbing any pixel and seeing if 80% or so matches, we additionally need to track the images sheer/compression.
In each row, go over pixel incrementally. For example, we'll check every tenth pixel. If we find a match for pixel 1, we then check 10 pixels away and see if that pixel exists anywhere in our row. If we find it, the distance from 0 to that pixel divided by 10 (our increment) is how many times larger the original image is.
If we found a pixel 20 slots from 0 in Image A, and it was only 10 pixels apart in Image B (remember, 10 is our increment), then our original image was 2 times larger. In other words, the new image is half the size of the original.
1) compression = target_width / original_width
2) compression = 20 / 10
3) compression = 2
This is a much more complex but robust way to detect a match. Enough matching rows mean you've got a matching image, but what about vertical stretching?
Similar logic. If you find a row that matches, start at 0 and go down by 10, then find that pixel's match in Image A.
Edit
The methods I provided are generic methods to work with looking for any image inside any other image. As you can imagine this is performance intensive. I don't know what image you're trying to detect but if there are common shapes, sometimes you can do alternative algorithms. If you have a circle, for example, you can just check that there are pixels that match outside a radius and pixels that are the same within.
The methods I presented also don't compensate for warping. Method 2 should be fine if the image is stretched but keeps a rectangular ratio. If the image has for example been warped into a circle shape, things get infinitely more complicated. For that case, the only hint I could give would be to check pixels within a radius of the original for matches.
I have a tile-based isometric world and I can calculate which tile is underneath specific (mouse) coordinates by using the following calculations:
function isoTo2D(pt:Point):Point{
var tempPt:Point = new Point(0, 0);
tempPt.x = (2 * pt.y + pt.x) / 2;
tempPt.y = (2 * pt.y - pt.x) / 2;
return(tempPt);
}
function getTileCoordinates(pt:Point, tileHeight:Number):Point{
var tempPt:Point = new Point(0, 0);
tempPt.x = Math.floor(pt.x / tileHeight);
tempPt.y = Math.floor(pt.y / tileHeight);
return(tempPt);
}
(Taken from http://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-a-primer-for-game-developers--gamedev-6511, this is a flash implementation but the maths is the same)
However, my problem comes in when I have tiles that have different elevation levels:
In these scenarios, due to the height of some tiles which have a higher elevation, the tiles (or portions of tiles) behind are covered up and shouldn't be able to be selected by the mouse, instead selecting the tile which is in front of it.
How can I calculate the tile by mouse coordinates taking into account the tiles' elevation?
I'm using a javascript and canvas implementation.
There is a technique of capturing object under the mouse on a canvas without needing to recalculate mouse coordinates into your "world" coordinates. This is not perfect, has some drawbacks and restrictions, yet it does it's job in some simple cases.
1) Position another canvas atop of your main canvas and set it's opacity to 0. Make sure your second canvas has the same size and overlaps your main one.
2) Whenever you draw your interactive objects to the main canvas, draw and fill the same objects on the second canvas, but using one unique color per object (from #000000 to #ffffff)
3) Set mouse event handling to the second canvas.
4) Use getPixel on the second canvas at mouse position to get the "id" of the object clicked/hovered over.
Main advantage is WYSIWYG principle, so (if everything is done properly) you can be sure, that objects on the main canvas are in the same place as on the second canvas, so you don't need to worry about canvas resizing or object depth (like in your case) calculations to get the right object.
Main drawback is need to "double-render" the whole scene, yet it can be optimized by not drawing on the second canvas when it's not necessary, like:
in "idling" scene state, when interactive objects are staying on their places and wait for user action.
in "locked" scene state, when some stuff is animated or smth. and user is not allowed to interact with objects.
Main restriction is a maximum number of interactive objects on the scene (up to #ffffff or 16777215 objects).
So... Not reccomended for:
Games with big amount of interactive objects on a scene. (bad performance)
Fast-paced games, where interactive objects are constantly moved/created/destroyed.(bad performance, issues with re-using id's)
Good for:
GUI's handling
Turn-based games / slow-paced puzzle games.
Your hit test function will need to have access to all your tiles in order to determine which one is hit. It will then perform test hits starting with the tallest elevation.
Assuming that you only have discreet (integer) tile heights, the general algorithm would be like this (pseudo code, assuming that tiles is a two-dimensional array of object with an elevation property):
function getTile(mousePt, tiles) {
var maxElevation = getMaxElevation(tiles);
var minElevation = getMinElevation(tiles);
var elevation;
for (elevation = maxElevation; elevation >= minElevation; elevation--) {
var pt = getTileCoordinates(mousePt, elevation);
if (tiles[pt.x][pt.y].elevation === elevation) {
return pt;
}
}
return null; // not tile hit
}
This code would need to be adjusted for arbitrary elevations and could be optimized to skip elevation that don't contain any tiles.
Note that my pseudocode ignores vertical sides of a tile and clicks on them will select the (lower elevation) tile obscured by the vertical side. If vertical tiles need to be accounted for, then a more generic surface hit detection approach will be needed. You could visit every tile (from closest to farthest away) and test whether the mouse coordinates are in the "roof" or in one of the viewer facing "wall" polygons.
If map is not rotatable and exatly same as picture you posted here,
When you are drawing polygons, save each tile's polygon(s) in a polygon array. Then sort the array only once using distance of them(their tile) to you(closest first, farthest last) while keeping them grouped by tile index.
When click event happens, get x,y coordinates of mouse, and do point in polygon test starting from first element array until last element. When hit, stop at that element.
No matter how high a tile is, will not hide any tile that is closer to you(or even same distance to you).
Point in polygon test is already solved:
Point in Polygon Algorithm
How can I determine whether a 2D Point is within a Polygon?
Point in polygon
You can even check every pixel of canvas once with this function and save results into an 2d array of points, vect2[x][y] which gives i,j indexes of tiles from x,y coordinates of mouse, then use this as a very fast index finder.
Pros:
fast and parallelizable using webworkers(if there are millions of tiles)
scalable to multiple isometric maps using arrays of arrays of polygons sorted by distance to you.
Elevation doesnt decrease performance because of only 3 per tile maximum.
Doesn't need any conversion to isometric to 2d. Just the coordinates of corners of polygons on canvas and coordinates of mouse on the same canvas.
Cons:
You need coordinates of each corner if you haven't already.
Clicking a corner will pick closest tile to you while it is on four tiles at the same time.
The answer, oddly, is written up in the Wikipedia page, in the section titled "Mapping Screen to World Coordinates". Rather than try to describe the graphics, just read the section three times.
You will need to determine exactly which isomorphic projection you are using, often by measuring the tile size on the screen with a ruler.
I have a cylinder that is split into many height segments (the amount depends on the data). For each height segment I have a value for which I want the entire circle at that height to be extruded.
So essentially I end up with a cylinder that has very spiky edges.
I was intending to do this by manually moving the vertices or faces but I cannot seem to access the vertices/faces for a given segment.
So basically I need to scale the segment at N height.
Any suggestions on which direction I take? Have had a few failed attempts now and am running out of ideas.
Have a look at the source code for CylinderGeometry.js on GitHub.
You could copy this entire method and call it something different, e.g.
THREE.CylinderGeometry2 = function (...
Then change the generation of the vertices based on the number of height segments you have.
So basically I have an application which zooms in by scaling every layer and adjusting the position of the layers. However I have a bunch of circles (and images) on some layers which I need to keep the size the same regardless of the zoom level (ie regardless of the scale of the layer).
Is there a way to set the scale of every shape/image in a layer without iteration? I've looked at using a Group but there is no universal way to set a universal scale.
If not, would there be an efficient way to do this without iteration?
Put the non-scaling objects in a separate group and then rescale (and optionally reposition) that group based on the scaling factor of the groups layer.
Demo: http://jsfiddle.net/m1erickson/2X7eK/
var scaleFactor=2;
// scale the entire layer by 2X
layer.setScale(scaleFactor);
// unscale just the noGroup back to original size
noGroup.setScale(1/scaleFactor);
// reposition just the noGroup back to it's original position
// before the layer was resized
noGroup.setPosition(noGroup.getX()/scaleFactor,noGroup.getY()/scaleFactor);
I'm using canvas of HTML5 to create a "preview" image which mainly consists of some rectangles and simple lines. Works fine so far, but there's one problem I cannot fix somehow. Presume the following situation:
context.fillStyle = "rgba(0,0,0,0.75)";
context.fillRect(100.64646,100,50.94967,20);
context.fillRect(100.64646+50.94967,100,100,20);
So I'm drawing 2 rectangles with some opacity. The x-starting coordinate plus the x-length of the first rect is equal to the x-starting coordinate of the second rect, so in theory they should collide without any margin between. Sadly, the result is different:
(see http://files.clemensfreitag.de/thin_spacing.jpg)
There's a very tiny spacing between the boxes, and the background color is visible. But:
This problem doesn't occur if the coordinates and lengths are integer values.
Is there any way to get it done by using float values? Converting them to integers before drawing might be acceptable in my application, but I'm just wondering why this should not work with floats.
Best,
Clemens
What you're seeing is the result of overlaying two opaque colors. When the first rectangle ends at 151.59613, the rectangle is automatically antialiased, filling in the rightmost column with rgba(0,0,0,0.4470975). When the second rectangle starts at the same x coordinate, it is also antialiased, filling in the leftmost column (the same as the first rectangle's rightmost) with rgba(0,0,0,0.3029025). The two values do add up to rgba(0,0,0,0.75), but that's not how they are blended. Instead, the second color (rgba(0,0,0,.3029025)) is drawn on top of the first, resulting in rgba(0,0,0,0.4470975+(1-0.4470975)*0.3029025) = rgba(0,0,0,0.61457305). So there isn't actually a gap between the two rectangles, but rather a 1px column that is a slightly lighter shade of grey.
Similarly, if you were using solid colors then the second rectangle's antialiased column would overwrite the first's, resulting in an even lighter shade of grey in the "gap".
The issue does not show up with integer values because no antialiasing is required - each rectangle ends at the edge of a pixel.
It looks like none of the globalCompositeOperation settings fix this, and turning off antialiasing would sometimes result in a 1px gap, so I think your simplest solution is to force integer values (alternatively, you could clear that column then fill it in with the desired color).
This problem is related to the way objects are drawn on a float based grid (especially vertical and horizontal lines and thus rects).
See there for an explanation and a schema : http://canop.org/blog/?p=220
Depending on the size of your objects, you need to use integer or mid-integer coordinates and sizes for your shapes, the goal being to fill complete pixels in both dimensions.
For example :
use a mid-integer for a thin line (one pixel width)
use an integer coordinate for a 2 pixels wide line
(and extend the logic for rects)