I would like to determine the proportion of a grid cell occupied by one (or more) circles. So, for example, the top left grid cell below would have a small value (~0.1) and the center grid cell (7,7) would have a value of 1, as it is entirely occupied by the circle.
At present I am doing this with canvas.context2d.getImageData, by sampling the cell's content to determine what is present. This works but is way too slow. This is this method:
var boxRadius = 6;
var boxSize = boxRadius * 2 + 1;
var cellWidth = gridWidth / boxSize;
var cellHeight = gridHeight / boxSize;
var scanInterval = 10;
var scanCount = 10;
for (var x = viewcenterpoint.x - (gridWidth / 2); x <= viewcenterpoint.x + (gridWidth / 2) -1; x += cellWidth) {
for (var y = viewcenterpoint.y - (gridHeight / 2) ; y <= viewcenterpoint.y + (gridHeight / 2) -1; y += cellHeight) {
var cellthreatlevel = 0.0;
for (var cellx = x; cellx < x + cellWidth; cellx += scanInterval){
for (var celly = y; celly < y + cellHeight; celly += scanInterval){
var pixeldata = context.getImageData(cellx, celly, 1, 1).data;
cellthreatlevel += ((pixeldata[0] + pixeldata[1] + pixeldata[2])/765 * -1) + 1;//255; //grey tone
scancount += 1;
}
}
cellthreatlevel = cellthreatlevel / scanCount; //mean
}
}
The getImageData call is the source of the problem - it is way too slow.
Given that I have an array of circles, each with their x, y and radius how can I calculate this? If possible I would like each value to be a decimal fraction (between 0 and 1).
The grid is static, but the circles may move within it. I would be happy to get a rough estimate for the value, it doesnt need to be 100% accurate.
You can use the Monte Carlo Method to get an approximate solution. It is a probability based method, in which you generate random samples in order to estimate some value. In this case, given the coordinates of the circle center, the circle radius and the boundaries of the grid cell, you can estimate the proportion of the grid cell occupied by the circle by generating K random samples (all contained inside the grid cell), and verify the proportion of the samples that are also inside the circle. The more samples you generate, the more accurate your result will be.
Remember: to verify if a given sample P is inside a circle with center C and radius R, all you have to do is check if the equation sqrt((Px-Cx)^2 + (Py-Cy)^2) <= R is true
You only need to call getImageData once, to obtain the entire canvas.
Once you have the image data you can access the bytes at offset 4 * (celly * width + cellx) to get the RGB(A) data.
This should be massively faster since it only makes one call to the graphics hardware instead of 10s of thousands.
Related
I'm trying to pixelate an animation.. I have a .png image where I cut in frames. I have a scrollbar who gives a number, which is the new size of every pixel. (rasterSize)
I already did it for an image and it's working.
http://bht-homework.com/RMA/PIX_PRES1/
But for animation it looks like doesn't calculate the first pixels.
http://bht-homework.com/RMA/PIX_PRES2/
var imgData=context.getImageData(0,0,img.width,img.height);// width is 190,height 240
for (var x = 0; x < spriteSizeWidth; x++) {
for (var y = 0; y < spriteSizeHeight; y++) {
var rasterX = ((x / rasterSize) | 0) * rasterSize;
var rasterY = ((y / rasterSize) | 0) * rasterSize;
var rasterValIndex = (rasterX + rasterY * imgData.width) * 4;
r=imgData.data[rasterValIndex];
g=imgData.data[rasterValIndex + 1];
b=imgData.data[rasterValIndex + 2];
a=imgData.data[rasterValIndex + 3];
context.fillStyle="rgba(" +r+","+g+","+b+","+a+")";
context.fillRect(x,y,rasterSize,rasterSize);
}
}
Does someone has an idea how to fix it?
Thanks!
Though it might look as it would skip the first few pixels of your image, it actually just draws the blocks at the wrong position. It's offset by rasterSize.
You need to shift back the pixels to the correct position.
So simply replace
context.fillRect(x, y, rasterSize, rasterSize);
by
context.fillRect(x - rasterSize, y - rasterSize, rasterSize, rasterSize);
I am trying to figure out a way to have a fixed scale for the:
https://en.wikipedia.org/wiki/Diamond-square_algorithm
I see that the algorithm requires a power of 2 (+1) size of the array.
The problem I am having is that I would like to have the same heightmap produced regardless of the resolution. So if I have a resolution of 512 it would look the same as with the resolution 256 but just have less detail. I just can't figure out how to do this with.
My initial thought was to always create the heightmap in a certain dimension e.g. 1024 and downsample to the res I would like. Problem is I would like the upper resolution to be quite high (say 4096) and this severely reduces the performance at lower resolutions as we have to run the algo at the highest possible resolution.
Currently the algorithm is in javascript here is a snippet:
function Advanced() {
var adv = {},
res, max, heightmap, roughness;
adv.heightmap = function() {
// heightmap has one extra pixel this is ot remove it.
var hm = create2DArray(res-1, res-1);
for(var x = 0;x< res-1;x++) {
for(var y = 0;y< res-1;y++) {
hm[x][y] = heightmap[x][y];
}
}
return hm;
}
adv.get = function(x,y) {
if (x < 0 || x > max || y < 0 || y > max) return -1;
return heightmap[x][y];
}
adv.set = function(x,y,val) {
if(val < 0) {
val = 0;
}
heightmap[x][y] = val;
}
adv.divide = function(size) {
var x, y, half = size / 2;
var scale = roughness * size;
if (half < 1) return;
for (y = half; y < max; y += size) {
for (x = half; x < max; x += size) {
adv.square(x, y, half, Math.random() * scale * 2 - scale);
}
}
for (y = 0; y <= max; y += half) {
for (x = (y + half) % size; x <= max; x += size) {
adv.diamond(x, y, half, Math.random() * scale * 2 - scale);
}
}
adv.divide(size / 2);
}
adv.average = function(values) {
var valid = values.filter(function(val) {
return val !== -1;
});
var total = valid.reduce(function(sum, val) {
return sum + val;
}, 0);
return total / valid.length;
}
adv.square = function(x, y, size, offset) {
var ave = adv.average([
adv.get(x - size, y - size), // upper left
adv.get(x + size, y - size), // upper right
adv.get(x + size, y + size), // lower right
adv.get(x - size, y + size) // lower left
]);
adv.set(x, y, ave + offset);
}
adv.diamond = function(x, y, size, offset) {
var ave = adv.average([
adv.get(x, y - size), // top
adv.get(x + size, y), // right
adv.get(x, y + size), // bottom
adv.get(x - size, y) // left
]);
adv.set(x, y, Math.abs(ave + offset));
}
adv.generate = function(properties, resolution) {
Math.seedrandom(properties.seed);
res = resolution + 1;
max = res - 1;
heightmap = create2DArray(res, res);
roughness = properties.roughness;
adv.set(0, 0, max);
adv.set(max, 0, max / 2);
adv.set(max, max, 0);
adv.set(0, max, max / 2);
adv.divide(max);
}
function create2DArray(d1, d2) {
var x = new Array(d1),
i = 0,
j = 0;
for (i = 0; i < d1; i += 1) {
x[i] = new Array(d2);
}
for (i=0; i < d1; i += 1) {
for (j = 0; j < d2; j += 1) {
x[i][j] = 0;
}
}
return x;
}
return adv;
}
Anyone ever done this before ?
Not quite sure if I understand your question yet but I'll provide further clarification if I can.
You've described a case where you want a diamond-square heightmap with a resolution of 256 to be used at a size of 512 without scaling it up. I'll go through an example using a 2x2 heightmap to a "size" of 4x4.
A diamond-square heightmap is really a set of vertices rather than tiles or squares, so a heightmap with a size of 2x2 is really a set of 3x3 vertices as shown:
You could either render this using the heights of the corners, or you might turn it into a 2x2 set of squares by taking the average of the four surrounding points - really this is just the "square" step of the algorithm without the displacement step.
So in this case the "height" of the top-left square would be the average of the (0, 0), (0, 1), (1, 1) and (1, 0) points.
If you wanted to draw this at a higher resolution, you could split each square up into a smaller set of 4 squares, adjusting the average based on how close it is to each point.
So now the value of the top-left-most square would be a sample of the 4 sub-points around it or a sample of its position relative to the points around it. But really this is just the diamond square algorithm applied again without any displacement (no roughness) so you may as well apply the algorithm again and go to the larger size.
You've said that going to the size you wish to go to would be too much for the processor to handle, so you may want to go with this sampling approach on the smaller size. An efficient way would be to render the heightmap to a texture and sample from it and the position required.
Properly implemented diamond & square algorithm has the same first N steps regardless of map resolution so the only thing to ensure the same look is use of some specified seed for pseudo random generator.
To make this work you need:
set seed
allocate arrays and set base randomness magnitude
Diamond
Square
lower base randomness magnitude
loop #3 until lowest resolution hit
If you are not lowering the randomness magnitude properly then the lower recursion/iteration layers can override the shape of the result of the upper layers making this not work.
Here see how I do it just add the seed:
Diamond-square algorithm not working
see the line:
r=(r*220)>>8; if (r<2) r=2;
The r is the base randomness magnitude. The way you are lowering it will determine the shape of the result as you can see I am not dividing it by two but multiplying by 220/256 instead so the lower resolution has bigger bumps which suite my needs.
Now if you want to use non 2^x+1 resolutions then choose the closer bigger resolution and then scale down to make this work for them too. The scaling down should be done carefully to preserve them main grid points of the first few recursion/iteration steps or use bi-cubic ...
If you're interested take a look on more up to date generator based on the linked one:
Diamond&Square Island generator
As part of a word cloud rendering algorithm (inspired by this question), I created a Javascript / Processing.js function that moves a rectangle of a word along an ever increasing spiral, until there is no collision anymore with previously placed words. It works, yet I'm uncomfortable with the code quality.
So my question is: How can I restructure this code to be:
readable + understandable
fast (not doing useless calculations)
elegant (using few lines of code)
I would also appreciate any hints to best practices for programming with a lot of calculations.
Rectangle moveWordRect(wordRect){
// Perform a spiral movement from center
// using the archimedean spiral and polar coordinates
// equation: r = a + b * phi
// Calculate mid of rect
var midX = wordRect.x1 + (wordRect.x2 - wordRect.x1)/2.0;
var midY = wordRect.y1 + (wordRect.y2 - wordRect.y1)/2.0;
// Calculate radius from center
var r = sqrt(sq(midX - width/2.0) + sq(midY - height/2.0));
// Set a fixed spiral width: Distance between successive turns
var b = 15;
// Determine current angle on spiral
var phi = r / b * 2.0 * PI;
// Increase that angle and calculate new radius
phi += 0.2;
r = (b * phi) / (2.0 * PI);
// Convert back to cartesian coordinates
var newMidX = r * cos(phi);
var newMidY = r * sin(phi);
// Shift back respective to mid
newMidX += width/2;
newMidY += height/2;
// Calculate movement
var moveX = newMidX - midX;
var moveY = newMidY - midY;
// Apply movement
wordRect.x1 += moveX;
wordRect.x2 += moveX;
wordRect.y1 += moveY;
wordRect.y2 += moveY;
return wordRect;
}
The quality of the underlying geometric algorithm is outside my area of expertise. However, on the quality of the code, I would say you could extract a lot of functions from it. Many of the lines that you have commented could be turned into separate functions, for example:
Calculate Midpoint of Rectangle
Calculate Radius
Determine Current Angle
Convert Polar to Cartesian Coodinates
You could consider using more descriptive variable names too. 'b' and 'r' require looking back up the code to see what they are for, but 'spiralWidth' and 'radius' do not.
In addition to Stephen's answer,
simplify these two lines:
var midX = wordRect.x1 + (wordRect.x2 - wordRect.x1)/2.0;
var midY = wordRect.y1 + (wordRect.y2 - wordRect.y1)/2.0;
The better statements:
var midX = (wordRect.x1 + wordRect.x2)/2.0;
var midY = (wordRect.y1 + wordRect.y2)/2.0;
I am creating a new "whack-a-mole" style game where the children have to hit the correct numbers in accordance to the question. So far it is going really well, I have a timer, count the right and wrong answers and when the game is started I have a number of divs called "characters" that appear in the container randomly at set times.
The problem I am having is that because it is completely random, sometimes the "characters" appear overlapped with one another. Is there a way to organize them so that they appear in set places in the container and don't overlap when they appear.
Here I have the code that maps the divs to the container..
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function scramble() {
var children = $('#container').children();
var randomId = randomFromTo(1, children.length);
moveRandom('char' + randomId);
}
function moveRandom(id) {
var cPos = $('#container').offset();
var cHeight = $('#container').height();
var cWidth = $('#container').width();
var pad = parseInt($('#container').css('padding-top').replace('px', ''));
var bHeight = $('#' + id).height();
var bWidth = $('#' + id).width();
maxY = cPos.top + cHeight - bHeight - pad;
maxX = cPos.left + cWidth - bWidth - pad;
minY = cPos.top + pad;
minX = cPos.left + pad;
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX);
$('#' + id).css({
top: newY,
left: newX
}).fadeIn(100, function () {
setTimeout(function () {
$('#' + id).fadeOut(100);
window.cont++;
}, 1000);
});
I have a fiddle if it helps.. http://jsfiddle.net/pUwKb/8/
As #aug suggests, you should know where you cannot place things at draw-time, and only place them at valid positions. The easiest way to do this is to keep currently-occupied positions handy to check them against proposed locations.
I suggest something like
// locations of current divs; elements like {x: 10, y: 40}
var boxes = [];
// p point; b box top-left corner; w and h width and height
function inside(p, w, h, b) {
return (p.x >= b.x) && (p.y >= b.y) && (p.x < b.x + w) && (p.y < b.y + h);
}
// a and b box top-left corners; w and h width and height; m is margin
function overlaps(a, b, w, h, m) {
var corners = [a, {x:a.x+w, y:a.y}, {x:a.x, y:a.y+h}, {x:a.x+w, y:a.y+h}];
var bWithMargins = {x:b.x-m, y:b.y-m};
for (var i=0; i<corners.length; i++) {
if (inside(corners[i], bWithMargins, w+2*m, h+2*m) return true;
}
return false;
}
// when placing a new piece
var box;
while (box === undefined) {
box = createRandomPosition(); // returns something like {x: 15, y: 92}
for (var i=0; i<boxes.length; i++) {
if (overlaps(box, boxes[i], boxwidth, boxheight, margin)) {
box = undefined;
break;
}
}
}
boxes.push(box);
Warning: untested code, beware the typos.
The basic idea you will have to implement is that when a random coordinate is chosen, theoretically you SHOULD know the boundaries of what is not permissible and your program should know not to choose those places (whether you find an algorithm or way of simply disregarding those ranges or your program constantly checks to make sure that the number chosen isn't within the boundary is up to you. the latter is easier to implement but is a bad way of going about it simply because you are entirely relying on chance).
Let's say for example coordinate 50, 70 is selected. If the picture is 50x50 in size, the range of what is allowed would exclude not only the dimensions of the picture, but also 50px in all directions of the picture so that no overlap may occur.
Hope this helps. If I have time, I might try to code an example but I hope this answers the conceptual aspect of the question if that is what you were having trouble with.
Oh and btw forgot to say really great job on this program. It looks awesome :)
You can approach this problem in at least two ways (these two are popped up in my head).
How about to create a 2 dimensional grid segmentation based on the number of questions, the sizes of the question panel and an array holding the position of each question coordinates and then on each time frame to position randomly these panels on one of the allowed coordinates.
Note: read this article for further information: http://eloquentjavascript.net/chapter8.html
The second approach follow the same principle, but this time to check if the panel overlap the existing panel before you place it on the canvas.
var _grids;
var GRID_SIZE = 20 //a constant holding the panel size;
function createGrids() {
_grids = new Array();
for (var i = 0; i< stage.stageWidth / GRID_SIZE; i++) {
_grids[i] = new Array();
for (var j = 0; j< stage.stageHeight / GRID_SIZE; j++) {
_grids[i][j] = new Array();
}
}
}
Then on a separate function to create the collision check. I've created a gist for collision check in Actionscript, but you can use the same principle in Javascript too. I've created this gist for inspirational purposes.
Just use a random number which is based on the width of your board and then modulo with the height...
You get a cell which is where you can put the mole.
For the positions the x and y should never change as you have 9 spots lets say where the mole could pop up.
x x x
x x x
x x x
Each cell would be sized based on % rather then pixels and would allow re sizing the screen
1%3 = 1 (x)
3%3 = 0 (y)
Then no overlap is possible.
Once the mole is positioned it can be show or hidden or moved etc based on some extended logic if required.
If want to keep things your way and you just need a quick re-position algorithm... just set the NE to the SW if the X + width >= x of the character you want to check by setting the x = y+height of the item which overlaps. You could also enforce that logic in the drawing routine by caching the last x and ensuring the random number was not < last + width of the item.
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX); if(newX > lastX + characterWidth){ /*needful*/}
There could still however be overlap...
If you wanted to totally eliminate it you would need to keep track of state such as where each x was and then iterate that list to find a new position or position them first and then all them to move about randomly without intersecting which would would be able to control with just padding from that point.
Overall I think it would be easier to just keep X starting at 0 and then and then increment until you are at a X + character width > greater then the width of the board. Then just increase Y by character height and Set X = 0 or character width or some other offset.
newX = 0; newX += characterWidth; if(newX + chracterWidth > boardWidth) newX=0; newY+= characterHeight;
That results in no overlap and having nothing to iterate or keep track of additional to what you do now, the only downside is the pattern of the displayed characters being 'checker board style' or right next to each other (with possible random spacing in between horizontal and vertical placement e.g. you could adjust the padding randomly if you wanted too)
It's the whole random thing in the first place that adds the complexity.
AND I updated your fiddle to prove I eliminated the random and stopped the overlap :)
http://jsfiddle.net/pUwKb/51/
I'm trying to find the row, column in a 2d isometric grid of a screen space point (x, y)
Now I pretty much know what I need to do which is find the length of the vectors in red in the pictures above and then compare it to the length of the vector that represent the bounds of the grid (which is represented by the black vectors)
Now I asked for help over at mathematics stack exchange to get the equation for figuring out what the parallel vectors are of a point x,y compared to the black boundary vectors. Link here Length of Perpendicular/Parallel Vectors
but im having trouble converting this to a function
Ideally i need enough of a function to get the length of both red vectors from three sets of points, the x,y of the end of the 2 black vectors and the point at the end of the red vectors.
Any language is fine but ideally javascript
What you need is a base transformation:
Suppose the coordinates of the first black vector are (x1, x2) and the coordinates of the second vector are (y1, y2).
Therefore, finding the red vectors that get at a point (z1, z2) is equivalent to solving the following linear system:
x1*r1 + y1*r2 = z1
x2*r1 + y2*r2 = z2
or in matrix form:
A x = b
/x1 y1\ |r1| = |z1|
\x2 y2/ |r2| |z2|
x = inverse(A)*b
For example, lets have the black vector be (2, 1) and (2, -1). The corresponding matrix A will be
2 2
1 -1
and its inverse will be
1/4 1/2
1/4 -1/2
So a point (x, y) in the original coordinates will be able to be represened in the alternate base, bia the following formula:
(x, y) = (1/4 * x + 1/2 * y)*(2,1) + (1/4 * x -1/2 * y)*(2, -1)
What exactly is the point of doing it like this? Any isometric grid you display usually contains cells of equal size, so you can skip all the vector math and simply do something like:
var xStep = 50,
yStep = 30, // roughly matches your image
pointX = 2*xStep,
pointY = 0;
Basically the points on any isometric grid fall onto the intersections of a non-isometric grid. Isometric grid controller:
screenPositionToIsoXY : function(o, w, h){
var sX = ((((o.x - this.canvas.xPosition) - this.screenOffsetX) / this.unitWidth ) * 2) >> 0,
sY = ((((o.y - this.canvas.yPosition) - this.screenOffsetY) / this.unitHeight) * 2) >> 0,
isoX = ((sX + sY - this.cols) / 2) >> 0,
isoY = (((-1 + this.cols) - (sX - sY)) / 2) >> 0;
// isoX = ((sX + sY) / isoGrid.width) - 1
// isoY = ((-2 + isoGrid.width) - sX - sY) / 2
return $.extend(o, {
isoX : Math.constrain(isoX, 0, this.cols - (w||0)),
isoY : Math.constrain(isoY, 0, this.rows - (h||0))
});
},
// ...
isoToUnitGrid : function(isoX, isoY){
var offset = this.grid.offset(),
isoX = $.uD(isoX) ? this.isoX : isoX,
isoY = $.uD(isoY) ? this.isoY : isoY;
return {
x : (offset.x + (this.grid.unitWidth / 2) * (this.grid.rows - this.isoWidth + isoX - isoY)) >> 0,
y : (offset.y + (this.grid.unitHeight / 2) * (isoX + isoY)) >> 0
};
},
Okay so with the help of other answers (sorry guys neither quite provided the answer i was after)
I present my function for finding the grid position on an iso 2d grid using a world x,y coordinate where the world x,y is an offset screen space coord.
WorldPosToGridPos: function(iPosX, iPosY){
var d = (this.mcBoundaryVectors.upper.x * this.mcBoundaryVectors.lower.y) - (this.mcBoundaryVectors.upper.y * this.mcBoundaryVectors.lower.x);
var a = ((iPosX * this.mcBoundaryVectors.lower.y) - (this.mcBoundaryVectors.lower.x * iPosY)) / d;
var b = ((this.mcBoundaryVectors.upper.x * iPosY) - (iPosX * this.mcBoundaryVectors.upper.y)) / d;
var cParaUpperVec = new Vector2(a * this.mcBoundaryVectors.upper.x, a * this.mcBoundaryVectors.upper.y);
var cParaLowerVec = new Vector2(b * this.mcBoundaryVectors.lower.x, b * this.mcBoundaryVectors.lower.y);
var iGridWidth = 40;
var iGridHeight = 40;
var iGridX = Math.floor((cParaLowerVec.length() / this.mcBoundaryVectors.lower.length()) * iGridWidth);
var iGridY = Math.floor((cParaUpperVec.length() / this.mcBoundaryVectors.upper.length()) * iGridHeight);
return {gridX: iGridX, gridY: iGridY};
},
The first line is best done once in an init function or similar to save doing the same calculation over and over, I just included it for completeness.
The mcBoundaryVectors are two vectors defining the outer limits of the x and y axis of the isometric grid (The black vectors shown in the picture above).
Hope this helps anyone else in the future