I've got a 2x3 table that I'm adding to EaselJS...currently I'm building it like this:
for (var i = 0; i < 6; i++) {
if(i == 1 || i == 3 || i == 5) {
var xPos = playersBoxW;
} else {
var xPos = 0;
}
if(i == 2 || i == 3) {
var yPos = playersBoxH;
} else if (i == 4|| i == 5) {
var yPos = playersBoxH*2;
} else {
var yPos = 0;
}
playerBox[i] = new createjs.Container().set({x: xPos, y: yPos});
}
It just seems a very inefficient way of doing it and not useful if the table grows. Anyone else have an idea to simplify this?
If you are just trying to do row/column math, there is an easier way.
Here is your original example (with some code to make it work) http://jsfiddle.net/u3ds24y5/
You can just derive the column and row with a simple equation. This lets you change the number of columns and total count easily.
var column = i % num_columns;
var row = Math.floor(i / num_columns);
var x = column * column_width;
var y = row * row_height;
Here is an updated fiddle: http://jsfiddle.net/u3ds24y5/1/
Simplified code:
var cols = 2, total = 6; // Change these
for (var i = 0; i < total; i++) {
var xPos = i % cols * playersBoxW,
yPos = Math.floor(i/cols) * playersBoxH;
// Create container, etc
}
Looking at your code I think this algorithm is essentially what it boils down to:
xPos seems to be equal to the integer division of i (by table width) times playersBoxW. e.g if i = 3 and width is 2, then xPos is equal to playersBoxW times int division of 3/2 which is 1.
yPos seems to be equal to the integer division of i (by table height) times playersBoxH. e.g if i = 4 and height = 3, then yPos is equal to playersBoxH times int division of 4/3 which is 1.
function integerDivision(a, b) {
return Math.floor(a / b);
}
function makeTable(width, height, player, arr) {
var xPos, yPos, size = width*height;
for (var i = 0; i < size; i++) {
xPos = player.boxW * integerDivision(i, width);
yPos = player.boxH * integerDivision(i, height);
arr[i] = new createjs.Container().set({x: xPos, y: yPos});
}
return arr;
}
Integer division is like regular division but you throw the remainder away. So in this case we round the number down:
3/2 = 1.5 => floor the result (round down) => 1
Side node: EaslJS containers can be expensive sometimes so be careful with them.
Containers have some overhead, so you generally shouldn't create a Container to hold a single child. [easljs doc]
Related
I want to write a program that draws a surface (X * Y) evenly.I already have an approach for this at the moment, but it doesn't quite work yet and is also very slow. Since this approach is far too slow, I do not want to pursue it much further.
At the beginning there is always the first point and the last one - so with an area of 10 x 10 the pixel at position 0 and the pixel at position 99.
Then the next best pixel must be found, i.e. the one with the largest distance. This is relatively easy with only two points - (99 - 0 / 2) so 49 or 48.
Now you have to look for the next best one again. So (49 - 0) / 2 or if 48 was taken before (99 - 48) / 2 so 24/25 or 74/75.
This process must be repeated until the correct sequence is found.
0,99,49,74,24,36,61,86,12,42,67,92,6,18,30,55,80,45,70,95,3,9,15,21,27,33,39,52,58,64,77,83,89,47,72,97,1,4,7,10,13,16,19,22,25,28,31,34,37,40,43,50,53,56,59,62,65,68,75,78,81,84,87,90,93,2,5,8,11,14,17,20,23,26,29,32,35,38,41,44,46,48,51,54,57,60,63,66,69,71,73,76,79,82,85,88,91,94,96,98
I also added a small example here, which shows how it should work. The function getElementOrder should be replaced by a mathematical expression to get the fastest possible solution.
// define variables
const width = 20; // this will be > 2100
const height = 20; // this will be > 1600
const size = 20;
let elements = {};
// create all cells
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
let id = x + y * height;
let div = document.createElement("div");
div.style.border = "solid 1px black";
div.style.width = size + "px";
div.style.height = size + "px";
div.style.position = "absolute";
div.style.left = x * size + "px";
div.style.top = y * size + "px";
div.style.backgroundColor = "#F0F0F0";
let textDiv = document.createElement("div");
textDiv.innerHTML = id;
textDiv.style.position = "absolute";
textDiv.style.fontSize = "6pt";
textDiv.style.top = "1px";
textDiv.style.right = "1px";
div.appendChild(textDiv);
document.body.appendChild(div);
elements[id] = div;
}
}
function getElementOrder(width, height) {
/* BAD SLOW CODE START - This sould be better: */
const length = width * height;
const order = [0, length -1];
const result = [0, length -1];
while (order.length !== length) {
let index = 0;
let diff = 0;
for (let i = 0, m = order.length - 1; i < m; i++) {
let localDiff = order[i+1] - order[i];
if (localDiff > diff) {
index = i;
diff = localDiff;
}
}
let offset = Math.floor(diff/2);
let value = order[index] + offset;
order.splice(index + 1, 0, value);
result.push(value);
}
return result;
/* BAD SLOW CODE END */
}
// get the draw order
let order = getElementOrder(width, height);
// change color of each pixel in draw order
let interval = setInterval(() => {
if (order.length === 0) {
clearInterval(interval);
return;
}
const value = order.shift();
elements[value].style.backgroundColor = "#00abab";
}, 10);
Are there any mathematical approaches to solve this problem?
You are welcome to post better solutions, approaches or links to mathematical formulas for this problem here.
I think I get what you're trying to accomplish, and what the underlying routine is. The way I see it, you're probably overcomplicating the question of "finding the biggest distance", since from what I can see, what you're basically doing is halving increasingly fine intervals.
So, here's my version:
function getElementOrder(width, height) {
const length = width * height;
const order = [ 0 ];
for (let denominator = 2; order.length < length; denominator *= 2) {
for (let enumerator = 1; enumerator < denominator; enumerator += 2) {
order.push(Math.round(length * enumerator / denominator));
}
}
return order;
}
I'm using very long and clunky variable names to make the principle behind it clearer: if you project the entire interval of [0, width*height] to the interval of [0, 1] then what you're doing is adding 1/2, then 1/4 and 3/4, then 1/8 and 3/8 and 5/8 and 7/8, and so on; each time you multiply the denominator by 2, and take all the odd-numbered multiples.
(Addendum: you can probably squeeze even better performance out of it by using a fixed-length TypedArray for the results, and adding elements by index instead of using .push(). I just didn't want to obscure the gist of the solution with the additional loop variable and such.)
I got a bunch of nodes which are stored in an array arr.
Each node has a x and y value which repesents the position on the screen.
Now, i created the middle element of arr and save it in middle.
Now, my goal is, to find out the distance between middle and all other nodes and also find out the one with the maximum distance. For the distance I use the Pythagorean theorem a^2 + b^2 = c^2, that means sqrt(a^2 + b^2) = c or in my case sqrt(x^2 + y^2) = distance between 2 nodes.
For example to create the distance between (10,10) and (20,30) I create the difference of the x-scale and the y-scale, that means x = 20-10 = 10 and y = 30-10 = 20. The result is, that the distance between those nodes is sqrt( 10^2 + 20^2) = 22,3. In my code, I check with the if-loop, which x-value and y-value is bigger to avoid negative values. But something I made is wrong. Maybe someone can help?
var middle = arr[Math.floor(arr.length / 2)];
var arrayForDistance = [];
var distance = [];
for(i = 0; i != arr[middle] & i< arr.length; i++ ) {
if(arr[i].x > arr[middle].x) {
var newX = arr[i].x - arr[middle].x;
var newY = arr[i].y - arr[middle].y;
}
else if ( arr[i].x < arr[middle].x)
{
var newX = arr[middle].x - arr[i].x;
var newY = arr[middle].y - arr[i].y;
}}
distance = sqrt( newX^2 + newY^2)
arrayForDistance.push(distance[i]);
}
var maxDistance = Math.max.apply(null, arrayForDistance)
First of all you dont need to worry about negatives since you are squareing them, they will cancel out.
secondly your for loop is wrong it should be
var middle = arr[Math.floor(arr.length / 2)];
var arrayForDistance = [];
var distance ;
for(i = 0; i< arr.length; i++ ) {
if (i != Math.floor(arr.length / 2)){
var newX = arr[i].x - arr[middle].x;
var newY = arr[i].y - arr[middle].y;
distance = sqrt( newX^2 + newY^2)
arrayForDistance.push(distance);
}
}
var maxDistance = Math.max.apply(null, arrayForDistance)
I am trying to write a script to place 100 circles of varying sizes onto a stage. I've outlined the concise requirements below.
Given the following:
var stage; // contains a "width" and "height" property.
var circle; // the circle class. contains x, y, radius & a unique id property.
var circleArray; // contains 100 circle instances
requirements:
write a function to place 100 circles of varying radius onto the stage.
placements must be random but evenly distributed (no clumping).
placement must be performant - this will be executing on a mobile web browser.
circles must not intersect/overlap other circles.
circle.x >= 0 must be true.
circle.y >= 0 && circle.y <= stage.height must be true.
circles may have any of the following radius sizes (assigned at creation):
150
120
90
80
65
My current attempt is a brute-force method, which does not operate efficiently. If I attempt to insert any more than ~10 circles, the browser hangs. Below is my current implementation, which I am completely OK with throwing away in favor of a more performant / better one.
Here is a live demo (NOTE: there is no actual drawing code, just the logic, but it will still lock up the browser so be warned!!) http://jsbin.com/muhiziduxu/2/edit?js,console
function adjustForOverlap (circleArray) {
// a reference to the circle that is invoking this function.
var _this = this;
// remove this circle from the array we are iterating over.
var arr = circleArray.filter(function (circle){
return circle.id !== _this.id;
});
// while repeat == true, the circle may be overlapping something.
var repeat = true;
while(repeat) {
var hasOverlap = false;
for (var i=0; i<arr.length; i++) {
var other = arr[i];
var dx = _self.x - other.x;
var dy = _self.y - other.y;
var rr = _self.radius + other.radius;
if (dx * dx + dy * dy < rr * rr) {
// if here, then an overlap was detected.
hit = true;
break;
}
}
// if hit is false, the circle didn't overlap anything, so break.
if (hit === false) {
repeat = false;
break;
} else {
// an overlap was detected, so randomize position.
_self.x = Math.random() * (stage.width*2);
_self.y = Math.random() * stage.height;
}
}
}
There are lots of efficient collision detection algorithms. Many of them work by dividing up the space into cells and maintaining a separate data structure with efficient lookup of other objects in the cell. The basic steps are:
Identify a random spot for your new circle
Determine which cells it's in
Look in each of those cells for a collision
If there's a collision, goto 1.
Else, add the new circle to each of the cells it overlaps.
You can use a simple square grid (i.e. a 2-d array) for the cell data structure, or something else like a quadtree. You can also in some cases get a bit of extra speed by trying a cheap-but-coarse collision check first (do the bounding boxes overlap), and if that returns true try the slightly more expensive and exact check.
Update
For quadtrees, check out d3-quadtree, which ought to give you a pretty good implementation, with examples.
For a (very quick, untested) 2-d array implementation:
function Grid(radius, width, height) {
// I'm not sure offhand how to find the optimum grid size.
// Let's use a radius as a starting point
this.gridX = Math.ceil(width / radius);
this.gridY = Math.ceil(height / radius);
// Determine cell size
this.cellWidth = width / this.gridX;
this.cellHeight = height / this.gridY;
// Create the grid structure
this.grid = [];
for (var i = 0; i < gridY; i++) {
// grid row
this.grid[i] = [];
for (var j = 0; j < gridX; j++) {
// Grid cell, holds refs to all circles
this.grid[i][j] = [];
}
}
}
Grid.prototype = {
// Return all cells the circle intersects. Each cell is an array
getCells: function(circle) {
var cells = [];
var grid = this.grid;
// For simplicity, just intersect the bounding boxes
var gridX1Index = Math.floor(
(circle.x - circle.radius) / this.cellWidth
);
var gridX2Index = Math.ceil(
(circle.x + circle.radius) / this.cellWidth
);
var gridY1Index = Math.floor(
(circle.y - circle.radius) / this.cellHeight
);
var gridY2Index = Math.ceil(
(circle.y + circle.radius) / this.cellHeight
);
for (var i = gridY1Index; i < gridY2Index; i++) {
for (var j = gridX1Index; j < gridX2Index; j++) {
// Add cell to list
cells.push(grid[i][j]);
}
}
return cells;
},
add: function(circle) {
this.getCells(circle).forEach(function(cell) {
cell.push(circle);
});
},
hasCollisions: function(circle) {
return this.getCells(circle).some(function(cell) {
return cell.some(function(other) {
return this.collides(circle, other);
}, this);
}, this);
},
collides: function (circle, other) {
if (circle === other) {
return false;
}
var dx = circle.x - other.x;
var dy = circle.y - other.y;
var rr = circle.radius + other.radius;
return (dx * dx + dy * dy < rr * rr);
}
};
var g = new Grid(150, 1000, 800);
g.add({x: 100, y: 100, radius: 50});
g.hasCollisions({x: 100, y:80, radius: 100});
Here's a fully-functional example: http://jsbin.com/cojoxoxufu/1/edit?js,output
Note that this only shows 30 circles. It looks like the problem is often unsolvable with your current radii, width, and height. This is set up to look for up to 500 locations for each circle before giving up and accepting a collision.
So I have 2 functions that I was hoping would work together to bounce an object around a div. I'm using a graphics library, so there will be some unfamiliar pieces of code below. I think you'll still understand the gist of what I'm trying to do.
function homepage_screensaver()
{
/*
Create Raphael object in the space of the div with id "homeandidiv"
*/
var pappos = $("#homeanidiv").position();
var papx = pappos.left;
var papy = pappos.top;
var papheight = $("#homeanidiv").height();
var papwidth = $("#homeanidiv").width();
var paper = Raphael(papx, papy, papwidth, papheight);
/*
paper: space in which the circle will be bound
*/
var circx = Math.floor(Math.random() * Number.MAX_VALUE) % papwidth;
var circy = Math.floor(Math.random() * Number.MAX_VALUE) % papheight;
/*
circx, circy: initial positions of circle on the paper
*/
var mycirc = paper.circle(circx, circy, 10);
mycirc.attr("fill","#F9C624");
var theta = Math.floor(Math.random() * Number.MAX_VALUE) % 4 + 1;
/*
theta = 1 <---> object moving at a 45-degree angle
theta = 2 <---> object moving at a 135-degree angle
theta = 3 <---> object moving at a 225-degree angle
theta = 4 <---> object moving at a 315 degree angle
*/
var circwrapper = new Array(mycirc, theta);
window.setInterval(function() { move_obj(circwrapper, papwidth, papheight);}, 100);
}
function move_obj(obwrap, backwidth, backheight)
{
var ob = obwrap[0]; // object
var th = obwrap[1]; // theta, the current direction of the object
var BB = ob.getBBox(); // bounding box for object
var dx = 0;
var dy = 0;
if (BB.x >= backwidth && (th == 1 || th == 2))
dx = -1;
else if (BB.x <= 0 && (th == 3 || th == 4))
dx = 1;
if (BB.y >= backheight && (th == 2 || th == 3))
dy = -1;
else if (BB.y <= 0 && (th == 1 || th == 4))
dy = 1;
ob.transform("T " + dx + ", " + dy);
if (dx == 1 && dy == -1)
th = 1;
else if (dx == 1 && dy == 1)
th = 2;
else if (dx == -1 && dy == 1)
th = 3;
else // (dx == -1 && dy == -1)
th = 4;
obwrap[0] = ob;
obwrap[1] = th;
}
Here's the problem that I've realized after testing my page: my function move_obj(...) is not actually affecting the first parameter I'm passing to it. You can see at the end of my function that I have
obwrap[0] = ob;
obwrap[1] = th;
indicating that I'm trying to actually modify values of the array that is passed in as the first parameter.
Is there any "quick fix" to my problem? I would prefer not to go back and try to make things global variables.
Just so you know, I have researched the issue of passing by reference in JS and here it says that arrays are passed by reference: http://orizens.com/wp/topics/javascript-arrays-passing-by-reference-or-by-value/. So I don't see what's going wrong here.
You have to do the reassignment after the "move" function returns.
window.setInterval(function() {
var wrapper = [mycirc, theta];
move_obj(wrapper, papwidth, papheight);
mycirc = wrapper[0];
theta = wrapper[1];
}, 100);
Assigning new values to the array works but it only affects the array. When you build the array, you're making copies of the values of the two variables. There's no subsequent implicit relationship between the array slots and the variables, so changes to the array have no effect on the values of the independent variables.
This is a code review question more then anything.
I have the following problem:
Given a list of relative widths (no unit whatsoever, just all relative to each other), generate a list of pixel widths so that these pixel widths have the same proportions as the original list.
input: list of proportions, total pixel width.
output: list of pixel widths, where each width is an int, and the sum of these equals the total width.
Code:
var sizes = "1,2,3,5,7,10".split(","); //initial proportions
var totalWidth = 1024; // total pixel width
var sizesTotal = 0;
for (var i = 0; i < sizes.length; i++) {
sizesTotal += parseInt(sizes[i], 10);
}
if(sizesTotal != 100){
var totalLeft = 100;;
for (var i = 0; i < sizes.length; i++) {
sizes[i] = Math.floor(parseInt(sizes[i], 10) / sizesTotal * 100);
totalLeft -= sizes[i];
}
sizes[sizes.lengh - 1] = totalLeft;
}
totalLeft = totalWidth;
for (var i = 0; i < sizes.length; i++) {
widths[i] = Math.floor(totalWidth / 100 * sizes[i])
totalLeft -= widths[i];
}
widths[sizes.lenght - 1] = totalLeft;
//return widths which contains a list of INT pixel sizes
Might be worth abstracting it to a function... I cleaned it up a bit. And I wasn't sure what the sizesTotal != 100... stuff was all about so I life it out.
function pixelWidths(proportions, totalPx) {
var pLen = proportions.length,
pTotal = 0,
ratio, i;
for ( i = -1; ++i < pLen; )
pTotal += proportions[i];
ratio = totalPx / pTotal;
pTotal = 0;
for ( i = -1; ++i < pLen; )
pTotal += proportions[i] = ~~(proportions[i] * ratio);
proportions[pLen-1] += totalPx - pTotal;
return proportions;
}
pixelWidths([1,2,3,5,7,10], 1024); // => [36, 73, 109, 182, 256, 368]
FYI, ~~ (double-bitwise-not) has the effect of getting the number representation of any type (using the internal toInt32 operation) and then flooring it. E.g:
~~'2'; // => 2
~~'2.333'; // => 2
~~null; // => 0
If sizes starts off declared as a list of numbers, why do you have to call parseInt()?
You misspelled "length" in the last line
Where is widths declared?
How does this account for rounding issues? Oh I see; it's that last line; well don't you need to add totalLeft and not just override whatever's there?