I'm making a bot for a project called Ogar, an Agar.io server implementation written in Node.js.
This bot has an obstacle, a green spiky cell called a virus (see illustration). I need to program this bot to avoid that obstacle, but I'm with no luck. Since there are a lot of targets like in the illustration, it's based on updates.
Here is the code that I ended up with until now.
BotPlayer.prototype.avoidObstacles = function(cell, angle) {
// Sum up all of the vector angles of obstacles to cell and react against it
var angleSum = 0;
var collided = this.collisionFromList(cell, this.obstacles);
if (collided.length == 0) return angle; // Not to return NaN
for (var i = 0; i < collided.length; i++) {
angleSum += this.angle(cell.position, collided[i].position);
}
angleSum /= collided.length; // Average out the angle sum
// TODO: Find closest available edge
angleSum += Math.PI / 2;
return angle + angleSum;
};
This DOES work in most cases, but the bot sometimes completely ignores the obstacle (this.collisionFromList(cell, this.obstacles); is perfectly fine) and ends up literally going through it (explode into a lot of cells).
BotPlayer.prototype has a lot of useful functions for this kind of calculations. See this link.
I don't need any pathfinding squabbles, just this simple avoid measure.
There is an alternative approach to what you are trying to do. The approach is to use attractors to describe entities in your system. Your "bot" is the agent, it has a position and it knows about other entities in the world and their attraction force. Say your destination has +1 attraction force and obstacles have -X attraction force, effectively repelling the "bot" (agent).
Here's a decision pseudo-code:
/**
* #param {Array.<{position:Vector2, attraction:Number}>} entities
*/
Agent.prototype.calculateDirectionVector = function(entities){
var agentPosition = this.position;
var result = new Vector2(0,0);
entities.forEach(function(entity){
//calculate separation between agent and entity
var displacement = entity.position.clone().sub(agentPosition);
//figure out distance between entities
var distance = displacement.length();
//dampen influence of attraction linearly with distance
var influence = entity.attraction/distance;
//produce force vector exerted by this entity on the agent
var force = displacement.normalize().multiplyScalar(influence);
//add up forces on the entity
result.add(force);
});
//normalize the resulting vector
result.normalize();
return result;
}
It's a big heuristic you made but if you wish to remain with this logic then consider taking into account the cartesian distance to the viruses, since you apparently have access to their position.
The function is BotPlayer.prototype.getDist or BotPlayer.prototype.getAccDist
You can use a threshold value DIST_MIN and a simple if or use a function like angle/distance (better) to reduce the impact the distant viruses have on the angle.
Related
I have a total of 1 to 64 blobs and they all move to my mouse position. But i want them to not go into each other, in other words circle collision detection. However i can't seem to make it smooth and also push new objects after they move for the first time?
Tried checking each blob for collision with other blobs. If collosion is true, then set the distance between the blobs to their accumulated radiuses.
This is how i wrote the colliding function, but this way of doing it makes the resetting of positions too fast. I want it to be a smooth, but fast transition. Like instead of now 1 frame, lets say 10 frames. And another problem is when two objects' are distanced to their radiuses, they might collide into new ones and that will cause this code to run again, and then all blobs go crazy.
this.collide = function() {
var length = this.blobs.length; // How many blobs?
this.blobs.forEach(function(item, index) {
for (var i = 0; i < length; i++) {
// Get absolute distance between two vectors
var v0 = vectorFromTo(blob.blobs[i].pos.x, blob.blobs[i].pos.y, //[x2, y2]
item.pos.x, item.pos.y); //[x1, y1]
// if colliding, set distance between to their accumulated radiuses
if (magnitude(v0) < blob.blobs[i].r + item.r) {
item.pos.add(v0.setMag(magnitude(v0) - (blob.blobs[i].r + item.r)));
}
}
});
}
I haven't tried to code another way of doing this yet because i haven't learned about vectors in school, but i do understand them quite a bit. But what i think would work is if i checked for collision, and if they collide they go opposite directions 50% of the deficit distance, and then they check if they hit new blobs. But this would require physics right? Cause then it would have to do something with the mass and speed of the blob as well to know whats gonna happen to the new blob it crashes into?
EDIT:
This is what im looking for: https://youtu.be/QvlhRGtlcsw
This is what it currently looks like: https://youtu.be/QEpHnCgomqY
in my project I have a player walk around a globe. The globe is not just a sphere, it has mountains and valleys, so I need the players z position to change. For this I'm raycasting a single ray from player's position against a single object (the globe) and I get the point they intersect and change players position accordingly. I'm only raycasting when the player moves, not on every frame.
For a complex object it takes forever. It takes ~200ms for an object with ~1m polys (faces) (1024x512 segments sphere). Does raycasting cast against every single face ?
Is there a traditional fast way to achieve this in THREE, like some acceleration structure (octree? bvh? -- tbh from my google searches I haven't seem to find such a thing included in THREE) or some other thinking-out-of-the-box (no ray casting) method?
var dir = g_Game.earthPosition.clone();
var startPoint = g_Game.cubePlayer.position.clone();
var directionVector = dir.sub(startPoint.multiplyScalar(10));
g_Game.raycaster.set(startPoint, directionVector.clone().normalize());
var t1 = new Date().getTime();
var rayIntersects = g_Game.raycaster.intersectObject(g_Game.earth, true);
if (rayIntersects[0]) {
var dist = rayIntersects[0].point.distanceTo(g_Game.earthPosition);
dist = Math.round(dist * 100 + Number.EPSILON) / 100;
g_Player.DistanceFromCenter = dist + 5;
}
var t2 = new Date().getTime();
console.log(t2-t1);
Thank you in advance
Do not use three.js Raycaster.
Consider Ray.js that offers function intersectTriangle(a, b, c, backfaceCulling, target)
Suggested optimizations:
If player starts from some known positions ⇒ you must know his initial height, − no need to raycast (or just do one time full mesh slow intersection)
if player moves with small steps ⇒ next raycast will most likely intersect the same face as before.
Optimization #1 − remember previous face, and raycast it first.
if player does not jump ⇒ next raycast will most likely intersect the adjacent face to the face where player was before.
Optimization #2 − build up a cache, so that given a face idx you could retrieve adjacent faces in O(1) time.
This cache may be loaded from the file, if your planet is not generated in real time.
So with my approach on each move you do O(1) read operation from cache and raycast 1-6 faces.
Win!
For a complex object it takes forever. It takes ~200ms for an object with ~1m polys (faces) (1024x512 segments sphere). Does raycasting cast against every single face ?
Out of the box THREE.js does check every triangle when performing a raycast against a mesh and there are no acceleration structures built into THREE.
I've worked with others on the three-mesh-bvh package (github, npm) to help address this problem, though, which may help you get up to the speeds your looking for. Here's how you might use it:
import * as THREE from 'three';
import { MeshBVH, acceleratedRaycast } from 'three-mesh-bvh';
THREE.Mesh.prototype.raycast = acceleratedRaycast;
// ... initialize the scene...
globeMesh.geometry.boundsTree = new MeshBVH(globeMesh.geometry);
// ... initialize raycaster...
// Optional. Improves the performance of the raycast
// if you only need the first collision
raycaster.firstHitOnly = true;
const intersects = raycaster.intersectObject(globeMesh, true);
// do something with the intersections
There are some caveats mentioned in the README so keep those in mind (the mesh index is modified, only nonanimated BufferGeometry is supported, etc). And there's still some memory optimization that could be done but there are some tweakable options to help tune that.
I'll be interested to hear how this works for you! Feel free to leave feedback in the issues on how to improve the package, as well. Hope that helps!
I think you should pre-render the height map of your globe into a texture, assuming your terrain is not dynamic. Read all of it into a typed array, and then whenever your player moves, you only need to back-project her coordinates into that texture, query it, offset and multiply and you should get what you need in O(1) time.
It's up to you how you generate that height map. Actually if you have a bumpy globe, then you should probably start with height map in the first place, and use that in your vertex shader to render the globe (with the input sphere being perfectly smooth). Then you can use the same height map to query the player's Z.
Edit: Danger! This may cause someone's death one day. The edge case I see here is the nearest collision will be not be seen because searchRange will not contain the nearest triangle but will contain the second nearest one returning it as the closest one. I.e. a robotic arm may stop nearby the torso instead of stopping at the arm right in front of it.
anyway
Here's a hack when raycasting not too far from the previous result i.e. during consecutive mousemove events. This will not work for completely random rays
Mesh raycast supports drawRange to limit how many triangles will be searched. Also each raycast result comes with faceIndex telling which triangle was hit. If you're continuously looking for raycasts i.e. with mousemove or there's a laser linearly scanning a mesh you can first search the area nearby* the previous hit.
triangles' distance in the data may look like they're neighbours but it's not guaranteed they are sorted in any way. Still it's very possible that the close ones in the data are close in space.
let lastFaceIndex = null
const searchRange = 2000 * 3
function raycast(mesh, raycaster) {
// limited search
if (lastFaceIndex !== null) {
const drawRange = mesh.geometry.drawRange
drawRange.start = Math.max(0, lastFaceIndex * 3 - searchRange)
drawRange.count = searchRange * 2
const intersects = raycaster.intersectObjects([mesh]);
drawRange.start = 0
drawRange.count = Infinity
if (intersects.length) {
lastFaceIndex = intersects[0].faceIndex
return intersects[0]
}
}
// regular search
const intersects = raycaster.intersectObjects([mesh]);
if (!intersects.length) {
lastFaceIndex = null
return null
}
lastFaceIndex = intersects[0].faceIndex
return intersects[0]
}
I am using Javascripts built in canvas feature to draw a graph showing home loan payments, loan balance, and equity based on user input. I am not able to use any other form of graphing package, as the code is part of an assessment.
My graph is drawn by converting data to X and Y coordinates. When a loan price is input, some home loan payment equations calculate the total amount payed, which is divided by the canvas width to get a spacing variable. This spacing variable is used to convert dollar amounts into pixels on the canvas. A similar setup is used to get the years and months spacing pixels.
The problem I am having is that the Y axis on Javascript's canvas is inverted, with 0 being the top of the canvas and 280, my canvas height, being at the bottom. So far, I have been able to work around this, simply by swapping "+" and "-" operators, however, I am currently creating the code that draws the Loan Balance line on the graph, and the inversion is causing issues that I can't seem to solve. It may be something simple that I'm just not seeing, or it may be a more complex problem that needs to be solved, but either way, I can't figure it out.
X = 0; // same as before, iterators both set back to 0 for the new line.
iterator = 0;
c.beginPath // this next line is for loan balance, it starts at 300000 and goes down with each payment made, then back up with each bit of interest accrued.
// due to the fact that the y axis begins at the top, this means that the pixels for payments is added to the pixel count, and the interest accrued is taken away.
c.moveTo(0, loanLocation) // set starting point to x=0 y= loanLocation
while (X <= 510)// loan balance loop
{
X = X + 0.001; // iterates X by .001 each time, allowing an accurate subpixel resolution loop, see above for why this is needed.
iterator = iterator + 0.001;
if (iterator >= monthSpacing)
{
loanBalance = loanBalance - monthlyPayment + (monthlyInterest * loanBalance);
//alert(loanBalance);
//interestY =
//alert(interestY);
//alert(X + " " + monthSpacing);
loanY = loanY + paymentY - (loanY * monthlyInterest);
//alert(loanY);
//loanY = loanBalance * paySpacing;
c.lineTo(X, loanY);
iterator = 0;
}
}
c.strokeStyle = "black"
c.stroke(); // there is no fill for this line, so it is just left as a stroke.
This is the set of code which draws the line, above it are a few variables which are being used here:
var X = 0;
var iterator = 0;
var monthSpacing = yearSpacing / 12;
//alert(yearSpacing);
//alert(monthSpacing);
var monthlyInterest = interest/1200; // this gives the montly interest rate, the monthly interest pixel amount is below
//alert(monthlyInterest);//debugging, comment out.
var paymentY = monthlyPayment * paySpacing;
var interestY = monthlyInterest * paySpacing; // this is inaccurate, the interestY needs to be gotten by multiplying the remaining loan balance by the
//monthly interest each month.
//var interestY; // will be used further down, must be calculated monthly so cannot be set outside of the line drawing loops.
var totalY = 280;
var equityY = 280;
var loanBalance = loan;
var loanY = loanLocation;
When run I get a strange inversion of the desired outcome, I want the loan balance line to curve down towards zero, but instead, the curve is happening in the opposite direction, I have tried two different ways to get the coordinates, the loanBalance way, which involved working with dollar values and converting that to pixels, and the loanY way, which involved working with pixel values directly.
loanBalance provided a line which was the exact inverse of the desired line, it began at the loan value, and curved upwards in the exact opposite direction to what I want, I am confident that the math I'm using for the loanBalance method is accurate, I simply cannot think of a way to convert that dollar value into pixels due to the inverted nature of the Y axis.
loanY provides a line which is headed "down", but is curving downwards at an increasingly shortened rate, this leads me to believe that while the subtraction (addition due to the inversion) of monthly repayments is accurately being calculated, the addition (subtraction) of monthly interest is being calculated incorrectly. Multiplication cannot be simply replaced with division like addition and subtraction can, so converting this value to pixels is proving difficult. The line drawn by the loanY way is definitely being affected by the inversion, but is not a perfect inverse of the desired line, the math being used for that way is clearly very wrong.
Ideally, I'd like to find a way to use the loanY way, it is consistent with the rest of the program, and can be used when not working with such obvious values as dollars. If I have to though, I will use the loanBalance way.
If you aren't entirely certain what I'm asking, or what the code being used is, I can post the program in it's entirety if that would help. I've not done that yet as I don't want to clutter the question more than I already have.
You can change to a Cartesian coordinate system like this:
// get a reference to your canvas element (eg it might have id='myCanvas')
var canvas=document.getElementById('myCanvas');
// get the context for the canvas
var context=canvas.getContext('2d');
// vertically flip the canvas so its Y origin is at the bottom
context.setTransform(1,0,0,-1,0,canvas.height);
This makes y==0 at the bottom of the canvas and increases upward.
If you're using other transformations, then put this transformation before the others.
Specifically, I'm working in canvas with javascript.
Basically, I have objects which have boundaries that I want to avoid, but still surround with a bezier curve. However, I'm not even sure where to begin to write an algorithm that would move control points to avoid colliding.
The problem is in the image below, even if you're not familiar with music notation, the problem should still be fairly clear. The points of the curve are the red dots
Also, I have access to the bounding boxes of each note, which includes the stem.
So naturally, collisions must be detected between the bounding boxes and the curves (some direction here would be good, but I've been browsing and see that there's a decent amount of info on this). But what happens after collisions have been detected? What would have to happen to calculate control points locations to make something that looked more like:
Bezier approach
Initially the question is a broad one - perhaps even to broad for SO as there are many different scenarios that needs to be taken into consideration to make a "one solution that fits them all". It's a whole project in its self. Therefor I will present a basis for a solution which you can build upon - it's not a complete solution (but close to one..). I added some suggestions for additions at the end.
The basic steps for this solutions are:
Group the notes into two groups, a left and a right part.
The control points are then based on the largest angle from the first (end) point and distance to any of the other notes in that group, and the last end point to any point in the second group.
The resulting angles from the two groups are then doubled (max 90°) and used as basis to calculate the control points (basically a point rotation). The distance can be further trimmed using a tension value.
The angle, doubling, distance, tension and padding offset will allow for fine-tuning to get the best over-all result. There might be special cases which need additional conditional checks but that is out of scope here to cover (it won't be a full key-ready solution but provide a good basis to work further upon).
A couple of snapshots from the process:
The main code in the example is split into two section, two loops that parses each half to find the maximum angle as well as the distance. This could be combined into a single loop and have a second iterator to go from right to middle in addition to the one going from left to middle, but for simplicity and better understand what goes on I split them into two loops (and introduced a bug in the second half - just be aware. I'll leave it as an exercise):
var dist1 = 0, // final distance and angles for the control points
dist2 = 0,
a1 = 0,
a2 = 0;
// get min angle from the half first points
for(i = 2; i < len * 0.5 - 2; i += 2) {
var dx = notes[i ] - notes[0], // diff between end point and
dy = notes[i+1] - notes[1], // current point.
dist = Math.sqrt(dx*dx + dy*dy), // get distance
a = Math.atan2(dy, dx); // get angle
if (a < a1) { // if less (neg) then update finals
a1 = a;
dist1 = dist;
}
}
if (a1 < -0.5 * Math.PI) a1 = -0.5 * Math.PI; // limit to 90 deg.
And the same with the second half but here we flip around the angles so they are easier to handle by comparing current point with end point instead of end point compared with current point. After the loop is done we flip it 180°:
// get min angle from the half last points
for(i = len * 0.5; i < len - 2; i += 2) {
var dx = notes[len-2] - notes[i],
dy = notes[len-1] - notes[i+1],
dist = Math.sqrt(dx*dx + dy*dy),
a = Math.atan2(dy, dx);
if (a > a2) {
a2 = a;
if (dist2 < dist) dist2 = dist; //bug here*
}
}
a2 -= Math.PI; // flip 180 deg.
if (a2 > -0.5 * Math.PI) a2 = -0.5 * Math.PI; // limit to 90 deg.
(the bug is that longest distance is used even if a shorter distance point has greater angle - I'll let it be for now as this is meant as an example. It can be fixed by reversing the iteration.).
The relationship I found works good is the angle difference between the floor and the point times two:
var da1 = Math.abs(a1); // get angle diff
var da2 = a2 < 0 ? Math.PI + a2 : Math.abs(a2);
a1 -= da1*2; // double the diff
a2 += da2*2;
Now we can simply calculate the control points and use a tension value to fine tune the result:
var t = 0.8, // tension
cp1x = notes[0] + dist1 * t * Math.cos(a1),
cp1y = notes[1] + dist1 * t * Math.sin(a1),
cp2x = notes[len-2] + dist2 * t * Math.cos(a2),
cp2y = notes[len-1] + dist2 * t * Math.sin(a2);
And voila:
ctx.moveTo(notes[0], notes[1]);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, notes[len-2], notes[len-1]);
ctx.stroke();
Adding tapering effect
To create the curve more visually pleasing a tapering can be added simply by doing the following instead:
Instead of stroking the path after the first Bezier curve has been added adjust the control points with a slight angle offset. Then continue the path by adding another Bezier curve going from right to left, and finally fill it (fill() will close the path implicit):
// first path from left to right
ctx.beginPath();
ctx.moveTo(notes[0], notes[1]); // start point
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, notes[len-2], notes[len-1]);
// taper going from right to left
var taper = 0.15; // angle offset
cp1x = notes[0] + dist1*t*Math.cos(a1-taper);
cp1y = notes[1] + dist1*t*Math.sin(a1-taper);
cp2x = notes[len-2] + dist2*t*Math.cos(a2+taper);
cp2y = notes[len-1] + dist2*t*Math.sin(a2+taper);
// note the order of the control points
ctx.bezierCurveTo(cp2x, cp2y, cp1x, cp1y, notes[0], notes[1]);
ctx.fill(); // close and fill
Final result (with pseudo notes - tension = 0.7, padding = 10)
FIDDLE
Suggested improvements:
If both groups' distances are large, or angles are steep, they could probably be used as a sum to reduce tension (distance) or increase it (angle).
A dominance/area factor could affect the distances. Dominance indicating where the most tallest parts are shifted at (does it lay more in the left or right side, and affects tension for each side accordingly). This could possibly/potentially be enough on its own but needs to be tested.
Taper angle offset should also have a relationship with the sum of distance. In some cases the lines crosses and does not look so good. Tapering could be replaced with a manual approach parsing Bezier points (manual implementation) and add a distance between the original points and the points for the returning path depending on array position.
Hope this helps!
Cardinal spline and filtering approach
If you're open to use a non-Bezier approach then the following can give an approximate curve above the note stems.
This solutions consists of 4 steps:
Collect top of notes/stems
Filter away "dips" in the path
Filter away points on same slope
Generate a cardinal spline curve
This is a prototype solution so I have not tested it against every possible combination there is. But it should give you a good starting point and basis to continue on.
The first step is easy, collect points representing the top of the note stem - for the demo I use the following point collection which slightly represents the image you have in the post. They are arranged in x, y order:
var notes = [60,40, 100,35, 140,30, 180,25, 220,45, 260,25, 300,25, 340,45];
which would be represented like this:
Then I created a simple multi-pass algorithm that filters away dips and points on the same slope. The steps in the algorithm are as follows:
While there is a anotherPass (true) it will continue, or until max number of passes set initially
The point is copied to another array as long as the skip flag isn't set
Then it will compare current point with next to see if it has a down-slope
If it does, it will compare the next point with the following and see if it has an up-slope
If it does it is considered a dip and the skip flag is set so next point (the current middle point) won't be copied
The next filter will compare slope between current and next point, and next point and the following.
If they are the same skip flag is set.
If it had to set a skip flag it will also set anotherPass flag.
If no points where filtered (or max passes is reached) the loop will end
The core function is as follows:
while(anotherPass && max) {
skip = anotherPass = false;
for(i = 0; i < notes.length - 2; i += 2) {
if (!skip) curve.push(notes[i], notes[i+1]);
skip = false;
// if this to next points goes downward
// AND the next and the following up we have a dip
if (notes[i+3] >= notes[i+1] && notes[i+5] <= notes[i+3]) {
skip = anotherPass = true;
}
// if slope from this to next point =
// slope from next and following skip
else if (notes[i+2] - notes[i] === notes[i+4] - notes[i+2] &&
notes[i+3] - notes[i+1] === notes[i+5] - notes[i+3]) {
skip = anotherPass = true;
}
}
curve.push(notes[notes.length-2], notes[notes.length-1]);
max--;
if (anotherPass && max) {
notes = curve;
curve = [];
}
}
The result of the first pass would be after offsetting all the points on the y-axis - notice that the dipping note is ignored:
After running through all necessary passes the final point array would be represented as this:
The only step left is to smoothen the curve. For this I have used my own implementation of a cardinal spline (licensed under MIT and can be found here) which takes an array with x,y points and smooths it adding interpolated points based on a tension value.
It won't generate a perfect curve but the result from this would be:
FIDDLE
There are ways to improve the visual result which I haven't addressed, but I will leave it to you to do that if you feel it's needed. Among those could be:
Find center of points and increase the offset depending on angle so it arcs more at top
The end points of the smoothed curve sometimes curls slightly - this can be fixed by adding an initial point right below the first point as well at the end. This will force the curve to have better looking start/end.
You could draw double curve to make a taper effect (thin beginning/end, thicker in the middle) by using the first point in this list on another array but with a very small offset at top of the arc, and then render it on top.
The algorithm was created ad-hook for this answer so it's obviously not properly tested. There could be special cases and combination throwing it off but I think it's a good start.
Known weaknesses:
It assumes the distance between each stem is the same for the slope detection. This needs to be replaced with a factor based comparison in case the distance varies within a group.
It compares the slope with exact values which may fail if floating point values are used. Compare with an epsilon/tolerance
I need help/advice for improving/commenting my current design please :)
This relates to collision detection in a simple game: Dynamic bodies (moving ones) might collide with static bodies (i.e. ground, walls). I'm porting my Obj-C model to Javascript and am facing memory/performance questions as to my way of implementing this.
I'm using a very basic approach: An array of arrays represents my level in terms of physic opacity.
bit set to 0: Transparent area, bodies can go through
bit set to 1: Opaque area, bodies collide
Testing the transparency/opacity of a pixel simply goes as follows:
if (grid[x][y]) {
// collide!
}
My knowledge of JS is pretty limited in termes of performance/memory and can't evaluate how good this approach is :) No idea of the efficiency of using arrays that being said.
Just imagine a 1000-pixel wide level that's 600px high. It's a small level but this already means an array containing 1000 arrays each containing up to 600 entries. Besides, I've not found a way to ensure I create a 1bit-sized element like low-level languages have.
Using the following, can I be sure an entry isn't something "else" than a bit?
grid[x][y] = true;
grid[x][y] = false;
Thanks for your time and comments/advices!
J.
If you have an 1000x600 grid, you can guarantee you have at least 601 arrays in memory (1001 if you do it the other way round).
Rather than doing this, I would consider using either 1 array, or (preferrably) one object with a mapping scheme.
var map = {};
map["1x1"] = 1;
map["1x3"] = 1;
// assume no-hits are empty and free to move through
function canGoIn(x, y) {
return map.hasOwnProperty(x + "x" + y);
};
Alternately;
var map = [];
var width = 600;
map.push(0);
map.push(1);
// etc
function canGoIn(x, y) {
return map[(x * width) + y] == 1;
}
a boolean value won't be stored as just one bit, and that is also true for any other language I know (C included).
If you are having memory issues, you should consider implementing a bitarray like this one: https://github.com/bramstein/bit-array/blob/master/lib/bit-array.js
You will have to make your 2d array into a simple vector and converting your x, y coordinates like this: offset = x + (y * width);
Browsing an array will still lead to a multiplication to evaluate the offset so using a vector is equivalent to arrays.
But I suspect that calling a function (in case your using a bit-array) and doing some evaluations inside will lead to poorer performances.
I don't think you can gain performances and save memory at the same time.