Array algorithm too slow - javascript

i have written an algorithm that finds each line of a hexagon in a huge object-array structure.
the array consits of about 80.000 - 100.000 elements (line coordinates from start to end).
A hexagon cosists of 6 lines points. So the array has the information of about 15.000 hexagons.
The structure of the object (UNSORTED!!!) looks like this:
const stamps = [
{
vertices: [
{x: 114.5116411118, y: 342.9815785601},
{x: 115.6663416502, y: 344.9815785601}
]
},
{
vertices: [
{x: 115.6663416502, y: 340.9815785601},
{x: 114.5116411118, y: 342.9815785601}
]
},
{
vertices: [
{x: 122.6663416502, y: 364.9815785601},
{x: 147.9757427269, y: 314.9815785601},
]
},
{
vertices: [
{x: 117.9757427269, y: 340.9815785601},
{x: 115.6663416502, y: 340.9815785601},
]
},
{
vertices: [
{x: 119.1304432653, y: 342.9815785601},
{x: 117.9757427269, y: 340.9815785601},
]
},
{
vertices: [
{x: 117.9757427269, y: 344.9815785601},
{x: 119.1304432653, y: 342.9815785601},
]
},
{
vertices: [
{x: 115.6663416502, y: 344.9815785601},
{x: 117.9757427269, y: 344.9815785601},
]
},
];
To find each line hexagon, my idea was that there has to be 2 elements that have to same coordinate. If this is the case, i'm jumping to the index of this element and repeat that process untill i have all 6 lines of the hexagon.
It works like this, but its really, really slow. For an array with 80.000 elements its about 3 minutes.
The algorithm:
function findHexPolyPoints() {
const hexCoordinates = [];
let activeArrayPos = 0;
let position = 0;
while (1) {
let foundPair = false;
if (stamps.length < 6) break;
for (let k = 0; k < stamps.length; ++k) {
if (k === position) continue;
if (stamps[position].vertices[0].x === stamps[k].vertices[1].x && stamps[position].vertices[0].y === stamps[k].vertices[1].y) {
if (hexCoordinates[activeArrayPos]) {
hexCoordinates[activeArrayPos].push(stamps[k].vertices[0].x, stamps[k].vertices[0].y);
} else {
hexCoordinates.push([stamps[position].vertices[0].x, stamps[position].vertices[0].y, stamps[k].vertices[0].x, stamps[k].vertices[0].y]);
}
foundPair = true;
} else if (stamps[position].vertices[1].x === stamps[k].vertices[0].x && stamps[position].vertices[1].y === stamps[k].vertices[0].y) {
if (hexCoordinates[activeArrayPos]) {
hexCoordinates[activeArrayPos].push(stamps[k].vertices[1].x, stamps[k].vertices[1].y);
} else {
hexCoordinates.push([stamps[position].vertices[1].x, stamps[position].vertices[1].y, stamps[k].vertices[1].x, stamps[k].vertices[1].y]);
}
foundPair = true;
}
if (foundPair) {
stamps.splice(position, 1);
if (k > position) {
position = k - 1;
} else {
position = k;
}
if (hexCoordinates[activeArrayPos].length < 12) break;
}
if (hexCoordinates[activeArrayPos] && hexCoordinates[activeArrayPos].length === 12) {
if (k > position) stamps.splice(k - 1, 1);
else stamps.splice(k, 1);
activeArrayPos += 1;
position = 0;
break;
}
if (k === stamps.length - 1) {
stamps.splice(position, 1);
break;
}
}
}
sortHexagons(hexCoordinates);
}
Is there any way to speed up my algorithm? I have read that a simple for loop is still faster that some js sort functions like .map .filter or similar.

The following O(n) algorithm assumes
two different hexagons do not have overlapping vertices
there are no more than two same vertices in the array (meaning, there could be an orphan that doesn't belong to any hexagon, but its coordinates should not equal any of the hexagons vertices)
there is no floating point inaccuracy in coordinates (meaning two vertices that are supposed to be equal, are === exactly equal)
6 connected vertices are assumed to form an hexagon... (there is no barycenter calculation and checks to ensure it's actually an hexagon)
(if the points 1. and 2. are not true, the algo would need to be worked more, to try all possibilities (in overt[x_y] array, see below) in order to avoid non-hexagon or overlapping coords, depending on the expectancy to find overlapping hexagons, or orphans, the complexity could go beyond O(n))
Using the concept of map (get an object value from a key), which is considered O(1).
In order to conveniently use the vertices coordinates, we can concatenate x and y into one string
x:123.456, y:789.321
gives
x_y = "123.456_789.321"
Let's create 3 variables avert = [], overt = {}, hexas = []
avert is an array of all vertices, avert[index] is an array(2) of the x_y vertices coordinates
overt is an object that, for each x_y coordinates gives an array of indexes in avert (the size should not become more than 2, as assumed above (and there is no >2 check))
hexas is an array of array(6), the list of hexagons found (each coordinate is formatted x_y)
In the first forEach, avert and overt are created.
The next forEach processes all avert vertices [x_y1, x_y2]
starting from the first vertex, tries to find the 6 points to form an hexagon
adds each vertex to one hexagon array hexa, starting from the next (after the first)
assume coordinates are not sorted, thus ensure we're not going back to previous vertice
skips the used vertices (in hexagons)
ensure the last vertex found has the same coordinates as the origin (first)
Initialization
let avert = [], overt = {}, hexas = [];
stamps.forEach(function(e, i){
let xy1 = e['vertices'][0]['x']+'_'+e['vertices'][0]['y'];
let xy2 = e['vertices'][1]['x']+'_'+e['vertices'][1]['y'];
// overt[XY] (array) should have two elements at most (no overlapping),
// one if orphan
if ( ! overt[xy1]) overt[xy1] = [];
overt[xy1].push( i );
if ( ! overt[xy2]) overt[xy2] = [];
overt[xy2].push( i );
avert.push([ xy1, xy2 ]);
});
Processing
avert.forEach(function (e){
let j,coord = e[0]; // first coords x_y
let origin = coord;
let hexa = [];
let lastindex = -1; // avoid going back!
// try to find 5 connected vertices + origin
for(j=0 ; j<6 ; j++) {
let o = overt[coord];
if ( o === undefined || o.length < 2 ) {
break; // not found(already processed), or orphan!
}
let index = o[0] == lastindex ? o[1] : o[0]; // no turn back!
lastindex = index;
coord = avert[index][0] === coord ? avert[index][1] : avert[index][0];
hexa.push(coord);
}
if (j >= 6) { // found all vertices
// check that the last coord is the starting point
if (hexa[5] === origin) { // got it
hexas.push( hexa );
hexa.forEach(function(h){ // delete used vertices
delete overt[h];
});
}
}
});
All hexagons should be in hexas

You can avoid the nested loop over all data by using a hash map. Key the individual vertices with a hash, for instance their JSON representation, and store the corresponding x, y coordinate together with a list of neigbor objects.
Once you have that, it is easy to walk through that graph and identify the hexagons.
Runnable snippet using the sample data you provided:
function findHexPolyPoints(stamps) {
// Create graph
let map = new Map;
for (let {vertices} of stamps) {
// Get unique identifier for each vertex (its JSON notation)
let keys = vertices.map(JSON.stringify);
// Create "nodes" for each vertex, keyed by their key
let nodes = keys.map(function (key, i) {
let {x, y} = vertices[i];
let node = map.get(key);
if (!node) map.set(key, node = { key, x, y, neighbors: [] });
return node;
});
// Link these two nodes in both directions
nodes[0].neighbors.push(nodes[1]);
nodes[1].neighbors.push(nodes[0]);
}
// Walk through the graph to detect and collect hexagons
let hexagons = [];
for (let [key, vertex] of map) {
let hexagon = [];
while (vertex && hexagon.push(vertex) < 6) {
vertex = vertex.neighbors.find(neighbor => !hexagon.includes(neighbor));
}
// Remove collected nodes so they don't get visited a second time
for (let {key} of hexagon) map.delete(key);
// Verify that they form indeed a hexagon:
if (vertex && vertex.neighbors.includes(hexagon[0])) {
// Simplify the hexagon to only coordinates (12 coordinates)
hexagons.push(hexagon.flatMap(({x, y}) => [x, y]));
}
}
return hexagons;
}
// Demo. Just replace `stamps` with your actual data.
const stamps = [{vertices: [{x: 114.5116411118, y: 342.9815785601},{x: 115.6663416502, y: 344.9815785601}]},{vertices: [{x: 115.6663416502, y: 340.9815785601},{x: 114.5116411118, y: 342.9815785601}]},{vertices: [{x: 122.6663416502, y: 364.9815785601},{x: 147.9757427269, y: 314.9815785601},]},{vertices: [{x: 117.9757427269, y: 340.9815785601},{x: 115.6663416502, y: 340.9815785601},]},{vertices: [{x: 119.1304432653, y: 342.9815785601},{x: 117.9757427269, y: 340.9815785601},]},{vertices: [{x: 117.9757427269, y: 344.9815785601},{x: 119.1304432653, y: 342.9815785601},]},{vertices: [{x: 115.6663416502, y: 344.9815785601},{x: 117.9757427269, y: 344.9815785601},]},];
let hexagons = findHexPolyPoints(stamps);
console.log(hexagons);
It is true that plain old for loops are somewhat faster than .map, .forEach, .reduce, .find and the likes, but here I kept using them, as the main speed up is really coming from using a hash map.

Related

How to get the shortest distance from a point on 2d plane from an array of location objects

I have an array containing objects of location and want the shortest point to a different point ie
//location array
let locations=[
{ x_axis:900, y_axis:900, },
{ x_axis:800, y_axis:800, },
{ x_axis:10, y_axis:40, },
{ x_axis:700, y_axis:700, },
];
let startPoint={ x_axis:0, y_axis:0, }
function closest(locations,startPoint){
//get closest point which is { x_axis:10, y_axis:40 }
}
What I tried using
const index_0 = locations.findIndex(
// Trash code but my aim was to get the closest locations object
// to mainPoint within 100 difference
item => item.x_axis - person[w].x_axis > -100,
item => item.x_axis > person[w].x_axis < 100,
item => item.y_axis - person[w].y_axis > -100,
item => item.y_axis > person[w].y_axis < 100,
);
console.log(locations[index_0])
If you want to get the closest point on the Euclidean plane then it's a matter of applying Pythagorean theorem, and finding the point that gives the smallest distance.
Iterate through all locations
Calculate the distance of each entry in locations against the start point: this can be done by simply calculating the Euclidean distance using Pythagorean theorum
Find the index of the smallest distance
Retrieve the entry of the point by the given index
To calculate the hypothenuse between two points, you can either use Math.hypot(x1-x2, y1-y2) or the good old way of Math.sqrt(Math.pow(x1-x2, 2), Math.pow(y1-y2,2))
See proof-of-concept below:
const locations = [{
x_axis: 900,
y_axis: 900,
},
{
x_axis: 800,
y_axis: 800,
},
{
x_axis: 10,
y_axis: 40,
},
{
x_axis: 700,
y_axis: 700,
},
];
const startPoint = {
x_axis: 0,
y_axis: 0,
}
function closest(locations, startPoint) {
const distances = locations.map(location => {
return Math.hypot(startPoint.x_axis - location.x_axis, startPoint.y_axis - location.x_axis);
});
const minimumDistance = Math.min(...distances);
const indexOfMinimumDistance = distances.indexOf(minimumDistance);
return locations[indexOfMinimumDistance];
}
console.log(closest(locations, startPoint));

How does the function work without indexing or accepting a pre-formated object? [duplicate]

This question already has answers here:
What is 'Currying'?
(23 answers)
Closed 1 year ago.
so I'm struggling to understand this code. Can someone please explain how does function move works? Like I watched a lecture about it, but I still don't understand it.
const parse = (point) => {
return typeof point === "object" ? point : JSON.parse(point);
};
const move = (offset) => (point) =>{
point.x += offset.x;
point.y += offset.y;
return point
};
const polyline = [
{ x: 0, y: 0 },
{ x: 10, y: 10 },
'{ "x": 20, "y": 20 }',
{ x: 30, y: 30 },
];
const offset = move({ x: 10, y: -5 });
const path = polyline.map(parse).map(offset);
console.log({ path });
Here's how it was done previously, and this code is an optimised version of that code:
const shift = (offset, points) => {
let modifiedPoints = [];
points.forEach((point) => {
point = parse(point);
point.x += offset.x;
point.y += offset.y;
modifiedPoints.push(point);
});
return modifiedPoints;
};
It is called currying
The passed created function will in your case add 10 to x and subtract 5 from y from each of the polylines in the array
If you call it with
move({ x: 5, y: -15 }); it will offset each line by 5,-15
const parse = (point) => {
return typeof point === "object" ? point : JSON.parse(point); // did we get an object or a string? if the latter parse it
};
// move takes an offset and returns a function that uses that offset (closure)
const move = (offset) => (point) =>{
point.x += offset.x;
point.y += offset.y;
return point
};
// an array of objects or valid JSON strings
const polyline = [
{ x: 0, y: 0 },
{ x: 10, y: 10 },
'{ "x": 20, "y": 20 }', // this will be parsed
{ x: 30, y: 30 },
];
const offset = move({ x: 10, y: -5 }); // offset is returning a function to be used in the map
const path = polyline.map(parse).map(offset); // call the function for each parsed entry in the polyline
console.log({ path });

By modifying arr[1][0] multiple cells are modified

I'm trying to make a match-3 game (candy crush like). I have an object level which has tiles property which is a 2d array. After I do some manipulations I want to change the type of a specific element to -1 using this simple line (I'll be using for, but for now I've made it simple for demonstrative purposes)
level.tiles[1][0].type = -1;
Here is the code
var level = {
x: 250, // X position
y: 113, // Y position
columns: 8, // Number of tile columns
rows: 8, // Number of tile rows
tilewidth: 40, // Visual width of a tile
tileheight: 40, // Visual height of a tile
tiles: [], // The two-dimensional tile array
selectedtile: {selected: false, column: 0, row: 0}
};
var tileTypes = [
{
type: "red",
colors: [255, 128, 128]
},
{
type: "green",
colors: [128, 255, 128]
},
{
type: "darkBlue",
colors: [128, 128, 255]
},
{
type: "yellow",
colors: [255, 255, 128]
}
];
function createLevel() {
for (var i = 0; i < level.columns; i++) {
level.tiles[i] = [];
}
for (var i = 0; i < level.columns; i++) {
for (var j = 0; j < level.rows; j++) {
level.tiles[i][j] = getRandomTile();
}
}
}
function getRandomTile() {
return tileTypes[Math.floor(Math.random() * tileTypes.length)];
}
createLevel();
level.tiles[1][0].type = -1;
Unfortunately not only tiles[1][0] is modified, but multiple cells. The interesting part is that every time random cells are affected
This occurs because getRandomTile() returns a reference to a tile type, not a copy of it.
I.e. to simplify this case:
var a = {x: 1};
var b = [a, a, a, a];
b[0].x = 2;
console.log(a, b);
will output
{x: 2} [{x: 2}, {x: 2}, {x: 2}, {x: 2}]
If you want the tiles to be modifiable, have getRandomTile return a copy – a shallow copy in this case, so colors is still a reference, not a copy – of the randomly chosen tile type.
function getRandomTile() {
const tileType = tileTypes[Math.floor(Math.random() * tileTypes.length)];
// Idiom for shallow copy, i.e. assign all properties of tileType
// into a new, unique object.
return Object.assign({}, tileType);
}
The problem is you modify the type object, instead of linking to another type. A solution would be to clone it when creating the tiles:
function getRandomTile() {
var srcType = tileTypes[Math.floor(Math.random() * tileTypes.length)];
return {type:srcType.type, colors:srcType.color};
}
Another one (depending on your goal) would be to have Tile objects, each one having a reference to a Type object (not just an integer). At this point some classes might be helpful:
class TileType {
constructor(colors){
this.colors = colors;
}
}
let tileTypes = [...]
class Tile {
constructor(){
this.type = tileTypes[Math.random()*tileTypes.length|0];
}
setNewType(type){
this.type = type;
}
}
etc.
This is caused by getRandomTile which returns the same reference of the object defined in tileTypes if the index passed in is the same. You can print tileTypes to help your understand what happens.

JointJS create multiple links between elements

I'm trying create a diagram like this using JointJS.
However, when I'm adding multiple links between elements, I'm only seeing 1 link show up. How do I go about adding multiple links with automatically adjusted space between them?
This is the code to add the boxes and links. Note that right now I'm just trying to add 3 links between all of the blocks, but I'm only seeing 1 link appear between each.
var steps = [{title: "Step 1"}, {title: "Step 2"}, {title: "Step 3"}];
steps.forEach(function(step, i){
var title = step.title;
var yOffset = i*150 + 50; //offsets first block by 50 in y and all others 150
var xOffset = 60; //offsets all blocks by 60
createBlock(title, xOffset, yOffset, i);
});
var blocks = [];
function createBlock(title, x, y, loc) {
var x = (typeof x !== 'undefined') ? x : 0;
var y = (typeof y !== 'undefined') ? y : 0;
var newBlock = new joint.shapes.html.Element({
position: { x: x, y: y },
size: { width: 170, height: 100 },
label: title,
attrs: {
'.label': {
text: title,
'ref-x': .5,
'ref-y': .4,
fill: '#FFFFFF'
},
}
});
blocks.push(newBlock.id);
graph.addCell(newBlock);
if(blocks.length > 1) {
var link = new joint.shapes.devs.Link({
source: {
id: blocks[loc-1],
},
target: {
id: blocks[loc],
},
});
graph.addCell(link);
var link2 = new joint.shapes.devs.Link({
source: {
id: blocks[loc-1],
},
target: {
id: blocks[loc],
},
});
graph.addCell(link2);
var link3 = new joint.shapes.devs.Link({
source: {
id: blocks[loc-1],
},
target: {
id: blocks[loc],
},
});
graph.addCell(link3);
}
}
all the links are lying on top of each other so you see it as a single one. There is code in the demo of jointjs to see each link in different paths. you could add the below code and see that the links show up in different paths. You will need to change the graph to your graph name in the below three lines
// displaying multiple links between two elements in different paths
function adjustVertices(graph, cell) {
// If the cell is a view, find its model.
cell = cell.model || cell;
if (cell instanceof joint.dia.Element) {
_.chain(graph.getConnectedLinks(cell)).groupBy(function(link) {
// the key of the group is the model id of the link's source or target, but not our cell id.
return _.omit([link.get('source').id, link.get('target').id], cell.id)[0];
}).each(function(group, key) {
// If the member of the group has both source and target model adjust vertices.
if (key !== 'undefined') adjustVertices(graph, _.first(group));
});
return;
}
// The cell is a link. Let's find its source and target models.
var srcId = cell.get('source').id || cell.previous('source').id;
var trgId = cell.get('target').id || cell.previous('target').id;
// If one of the ends is not a model, the link has no siblings.
if (!srcId || !trgId) return;
var siblings = _.filter(graph.getLinks(), function(sibling) {
var _srcId = sibling.get('source').id;
var _trgId = sibling.get('target').id;
return (_srcId === srcId && _trgId === trgId) || (_srcId === trgId && _trgId === srcId);
});
switch (siblings.length) {
case 0:
// The link was removed and had no siblings.
break;
case 1:
// There is only one link between the source and target. No vertices needed.
cell.unset('vertices');
break;
default:
// There is more than one siblings. We need to create vertices.
// First of all we'll find the middle point of the link.
var srcCenter = graph.getCell(srcId).getBBox().center();
var trgCenter = graph.getCell(trgId).getBBox().center();
var midPoint = joint.g.line(srcCenter, trgCenter).midpoint();
// Then find the angle it forms.
var theta = srcCenter.theta(trgCenter);
// This is the maximum distance between links
var gap = 20;
_.each(siblings, function(sibling, index) {
// We want the offset values to be calculated as follows 0, 20, 20, 40, 40, 60, 60 ..
var offset = gap * Math.ceil(index / 2);
// Now we need the vertices to be placed at points which are 'offset' pixels distant
// from the first link and forms a perpendicular angle to it. And as index goes up
// alternate left and right.
//
// ^ odd indexes
// |
// |----> index 0 line (straight line between a source center and a target center.
// |
// v even indexes
var sign = index % 2 ? 1 : -1;
var angle = joint.g.toRad(theta + sign * 90);
// We found the vertex.
var vertex = joint.g.point.fromPolar(offset, angle, midPoint);
sibling.set('vertices', [{ x: vertex.x, y: vertex.y }]);
});
}
};
var myAdjustVertices = _.partial(adjustVertices, graph);
// adjust vertices when a cell is removed or its source/target was changed
graph.on('add remove change:source change:target', myAdjustVertices);
// also when an user stops interacting with an element.
graph.on('cell:pointerup', myAdjustVertices);
The core of the solution lies in the adjustVertices function presented below. It accepts a graph and a cell (link or element). For added convenience, the function accepts cell views as well as models.
If cell is a link, it will find all links with the same source and target and then set vertices on them; we will be calling those related links 'siblings'.
If cell is an element, we execute our function for each distinct (different source and target) link connected to the element.
function adjustVertices(graph, cell) {
// if `cell` is a view, find its model
cell = cell.model || cell;
if (cell instanceof joint.dia.Element) {
// `cell` is an element
_.chain(graph.getConnectedLinks(cell))
.groupBy(function(link) {
// the key of the group is the model id of the link's source or target
// cell id is omitted
return _.omit([link.source().id, link.target().id], cell.id)[0];
})
.each(function(group, key) {
// if the member of the group has both source and target model
// then adjust vertices
if (key !== 'undefined') adjustVertices(graph, _.first(group));
})
.value();
return;
}
// `cell` is a link
// get its source and target model IDs
var sourceId = cell.get('source').id || cell.previous('source').id;
var targetId = cell.get('target').id || cell.previous('target').id;
// if one of the ends is not a model
// (if the link is pinned to paper at a point)
// the link is interpreted as having no siblings
if (!sourceId || !targetId) return;
// identify link siblings
var siblings = _.filter(graph.getLinks(), function(sibling) {
var siblingSourceId = sibling.source().id;
var siblingTargetId = sibling.target().id;
// if source and target are the same
// or if source and target are reversed
return ((siblingSourceId === sourceId) && (siblingTargetId === targetId))
|| ((siblingSourceId === targetId) && (siblingTargetId === sourceId));
});
var numSiblings = siblings.length;
switch (numSiblings) {
case 0: {
// the link has no siblings
break;
} case 1: {
// there is only one link
// no vertices needed
cell.unset('vertices');
break;
} default: {
// there are multiple siblings
// we need to create vertices
// find the middle point of the link
var sourceCenter = graph.getCell(sourceId).getBBox().center();
var targetCenter = graph.getCell(targetId).getBBox().center();
var midPoint = g.Line(sourceCenter, targetCenter).midpoint();
// find the angle of the link
var theta = sourceCenter.theta(targetCenter);
// constant
// the maximum distance between two sibling links
var GAP = 20;
_.each(siblings, function(sibling, index) {
// we want offset values to be calculated as 0, 20, 20, 40, 40, 60, 60 ...
var offset = GAP * Math.ceil(index / 2);
// place the vertices at points which are `offset` pixels perpendicularly away
// from the first link
//
// as index goes up, alternate left and right
//
// ^ odd indices
// |
// |----> index 0 sibling - centerline (between source and target centers)
// |
// v even indices
var sign = ((index % 2) ? 1 : -1);
// to assure symmetry, if there is an even number of siblings
// shift all vertices leftward perpendicularly away from the centerline
if ((numSiblings % 2) === 0) {
offset -= ((GAP / 2) * sign);
}
// make reverse links count the same as non-reverse
var reverse = ((theta < 180) ? 1 : -1);
// we found the vertex
var angle = g.toRad(theta + (sign * reverse * 90));
var vertex = g.Point.fromPolar(offset, angle, midPoint);
// replace vertices array with `vertex`
sibling.vertices([vertex]);
});
}
}
}
We then attach the necessary event listeners (function bindInteractionEvents). The vertices are recalculated anytime the user translates an element - as well as anytime a link is added/removed or has its source or target changed.
function bindInteractionEvents(adjustVertices, graph, paper) {
// bind `graph` to the `adjustVertices` function
var adjustGraphVertices = _.partial(adjustVertices, graph);
// adjust vertices when a cell is removed or its source/target was changed
graph.on('add remove change:source change:target', adjustGraphVertices);
// adjust vertices when the user stops interacting with an element
paper.on('cell:pointerup', adjustGraphVertices);
}

Creating a grid array of co ordinates

I'm a little confused with my array creation. I have a list of items with x:y co ordinates and also their dimensions (in grid sizes) ... for example:
x: 1
y: 7
width: 2 tiles
height: 2 tiles
So the idea im trying to do is create an array of x:y grids that are "occupied". But looping such data so the occupied tiles would there for be:
x: 1
y: 7
//
x: 2
y: 7
//
x: 1
y: 8
//
x: 2
y: 8
Because in the above example the item is 2 by 2 tiles ( a square ).
My object is structured like this (shown in console.log(sdata);)
7: Array[4]
0: "1"
1: "7"
2: "2"
3: "2"
So yeah im trying to make an array to store this kind of grid reference..Hope I explained what I trying to get at, but i can't work out how to structure a loop to create the array.
http://jsfiddle.net/rlemon/JeeV2/ Here is an example of how you could produce this type of 'grid' occupied data..
var chords = [ // instead of arrays we'll use objects.. they're nicer.
{
x: 1,
y: 7,
h: 2, // height
w: 2}, // width
{ // ohh look a second plot point.
x: 4,
y: 1,
h: 3,
w: 2},
];
var occupied = { // will be your cells points occupied
x: [],
y: []
};
chords.forEach(function(chord) { // now lets loop the chords and produce the occupied array
occupied.x.push( chord.x );
occupied.x.push( chord.x + (chord.w-1) ); // expand the width of the cell - initial point
occupied.y.push( chord.y );
occupied.y.push( chord.y + (chord.h-1) ); // expand the height of the cell - initial point
});
console.log( occupied );
// outputs
​Object
x: Array[4]
0: 1
1: 2
2: 4
3: 5
y: Array[4]
0: 7
1: 8
2: 1
3: 3
The resulting outputArray array is a collection of objects in the form { x: value, y: value}.
var inputArray = [ [1,7,2,2], ... ];
var outputArray = [];
for(var i = 0; i < inputArray.length; i++) {
var item = {
x: inputArray[i][0],
y: inputArray[i][1],
width: inputArray[i][2],
height: inputArray[i][3]
};
for(var xx = 0; xx < item.width; xx++) {
for(var yy = 0; yy < item.height; yy++) {
outputArray.push({
x: item.x + xx,
y: item.y + yy
});
}
}
}
​I added the x, y, width and height attributes to make it more understandable.
function range(a, b) {
/*
range(5) -> [0,1,2,3,4]
range(1,4) -> [1,2,3]
*/
if (b===undefined)
var start=0, stop=a;
else
var start=a, stop=b;
var R = [];
for(var i=start; i<stop; i++)
R.push(i);
return R;
}
Then it's a two-liner:
range(x, x+width).map(function(x) {
return range(y, y+height).map(function(y) {
return {x:x, y:y}; // anything you'd like
})
})
Result (obtained via JSON.stringify):
[
[ {"x":1,"y":7}, {"x":1,"y":8} ],
[ {"x":2,"y":7}, {"x":2,"y":8} ]
]

Categories