How can I find out if multiple lines overlap? - javascript

Each line segment has 2 xy coordinates given,
Input are below,
[3,4],[5,4]
[8,4],[20,4]
[10,4],[15,4]
In the above picture, if the lines are overlapping, it can be considered as a line segment. May I know the logic or mathematics behind solving it which outputs the 2 non-overlapping line segments which are [3,4],[5,4] and [8,4],[20,4]?
The input I gave is just 3 line segments that we have to filter out to non-overlapping line segments, so this gets complicated fast with more line segments if we don't have the proper mathematics. I am doing this because I faced this bug in my programming :).
I will appreciate any help that I can obtain :)
My solution is I have tried to find whether 2 lines are overlapping from by using some of the logic which is A.start <= B.end && B.start <= A.end as stated in here. The current code which I am stuck on this problem is published in codepen live demo here.

I reviewed the code and found there was nothing wrong with the logic. The problem was with the dimensions and input.
Here's the revised code.
function resolveOverlaps(lines) {
if (lines.length <= 1) return lines;
// Sort the lines ascending by start value
lines.sort((a, b) => a[0][0] - b[0][0]);
let outLines = [lines[0]];
let last = outLines[0];
// Iterate over the lines, skipping the first one
lines.slice(1).forEach((line) => {
// There's an overlap, so extend the current segment's end
if (line[0][0] <= last[1][0]) {
last[1][0] = Math.max(last[1][0], line[1][0]);
} else {
// No overlap, start a new segment
outLines.push(line);
last = outLines[outLines.length - 1];
}
});
return outLines;
}

Your drawing indicates that you're using two-dimensional data points, with x and y coordinates. Finding overlapping lines in 2D requires more sophisticated logic than just checking start and end points.
However, as your sandbox example is 1D, I'll address that.
In this solution, you iterate over each of the line segments, sorted by increasing start coordinate. As you encounter each subsequent segment, you choose to either combine the two or add a new segment, depending on whether they overlap or not.
function resolveOverlaps(lines) {
if (lines.length <= 1) return lines;
if (lines[0].length !== 2 || typeof lines[0][0] !== "number") {
throw new Error(
"Invalid input shape. resolveOverlaps requires a list of N tuples like `[[start0, end0], ..., [startN, endN]]`"
);
}
for (const line of lines) {
if (line[0] > line[1]) {
throw new Error(
`Invalid segment [${line[0]}, ${line[1]}]. Ensure startend.`
);
}
}
// Sort the lines ascending by start value
lines.sort((a, b) => a[0][0] - b[0][0]);
let outLines = [lines[0]];
let last = outLines[0];
// Iterate over the lines, skipping the first one
lines.slice(1).forEach((line) => {
// There's an overlap, so extend the current segment's end
if (line[0][0] <= last[1][0]) {
last[1][0] = Math.max(last[1][0], line[1][0]);
} else {
// No overlap, start a new segment
outLines.push(line);
last = outLines[outLines.length - 1];
}
});
return outLines;
}
Edit: Here's an implementation for 2D segments which assumes that all points lie on the same line, and thus only needs to check the x coordinate of each point. A general 2D solution requires more sophisticated logic than this.
function resolveOverlaps2d(lines) {
if (lines.length <= 1) return lines;
if (lines[0].length !== 2 || lines[0][0].length !== 2) {
throw new Error(
"Invalid input shape. resolveOverlaps requires a list of N tuples like `[[[x0_0, y0_0], [x0_1, y0_1]], ..., [[xN_0, yN_0], [xN_1, yN_1]]]"
);
}
for (const line of lines) {
if (line[0][0] > line[1][0]) {
throw new Error(
`Invalid segment [${line[0]}, ${line[1]}]. Ensure start <= end.`
);
}
}
// Sort the lines ascending by start value
lines.sort((a, b) => a[0][0] - b[0][0]);
let outLines = [lines[0]];
let last = outLines[0];
// Iterate over the lines, skipping the first one
lines.slice(1).forEach((line) => {
// There's an overlap, so extend the current segment's end
if (line[0][0] <= last[1][0]) {
last[1][0] = Math.max(last[1][0], line[1][0]);
} else {
// No overlap, start a new segment
outLines.push(line);
last = outLines[outLines.length - 1];
}
});
return outLines;
}

Related

Javascript: How many arrays of 7 elements can i get out of an array of 49 elements

I am given an array with 49
My task is to select unique groups of 7 elements and also
find out the number of possible outputs.
Eg. [A,a,B,b,C,c,D,d,E,e,F,f,G,g,H,h,I,i,J,j,K,k,L,l,M,m,N,n,O,o,P,p,Q,q,R,r,S,s,T,t,U,u,V,v,W,w,X,x,Y]
Then outputs:
[ [A,a,B,b,C,c,D], [a,B,b,C,c,D,d], [B,b,C,c,D,d,E], [b,C,c,D,d,E,e],
. . . [A,C,c,D,d,E,e], [A,B,b,c,D,d,E],
.
.
.
]
How do I get this outputs?
Below is what I have tried based on the answer i got from stackoverflow:
let jar = ["A","a","B","b","C","c","D","d","E","e","F","f","G","g","H","h","I","i","J","j","K","k","L","l","M","m","N","n","O","o","P","p","Q","q","R","r","S","s","T","t","U","u","V","v","W","w","X","x","Y"];
const size = 7;
let result11 = [];
for(let i = 0; i <= (jar.length - size); i++){
result11.push(jar.slice(i, size+i));
}
console.log(result11)
Each of the output should be unique such that there should not be any repetition. Eg. outputs like aaaaaaa, aaxxxYY, AAAAAAA, AABbcDe are not valid but outputs like avWwXxY,bvWwXxY,cvWwXxY alongside others etc
Below you will find a working snippet that will generate all unique draws from a given array. Starting point is the initial draw vector v. This vector must contain exactly the number of array elements you want to have in each draw and the numbers must be in ascending order. Based on this initial vector the function nextDraw() will pick the next possible combination from the available number pool. The number pool is defined by 0 as the lower limit and n-1 as the upper limit.
My snippet is currently limited to 1000 draws as most of you would probably loose interest if you had to wait for all 85900584 possible combinations to be calculated and printed here.
function nextDraw(v,n){
// generate a new vector w by copying v
// set k to point to the last element of w:
let w=v.slice(0),l=w.length,k=l-1;
while (true){ // unconditional loop
// Is the k-th element of w lower than n-l+k?
if (w[k]<n-l+k) {
// increment w[k]
w[k]++;
// and, in case k<l-1:
// initialise all following elements of w with an increasing sequence:
for (let j=k+1;j<l;j++) {
w[j]=w[j-1]+1;
}
// return a valid draw vector!
return w;
}
else {
// as long as there is still a smaller k index available: decrement k
// and continue with the while loop
if(k) k--
// else: we have reached the end of the series!
else return false;
}
}
}
function allDraws(v,n){
const res=[];
res.push(v);
while (res.length<1000 && (v=nextDraw(v,n))) res.push(v)
return res;
}
// Show first 1000 and last 121 draws:
[[0,1,2,3,4,5,6],[38,43,44,45,46,47,48]].forEach(v=>{
let res=allDraws(v,49);
console.log(res.length,res.map(r=>r.join(",")));
});
Admittedly, my snippet works with index numbers and not with an array of arbitrary elements, but you can easily apply the calculated index vectors to retrieve the actual values from your source array of length n.

Identifying edge cases of a one-dimensional array in Javascript

I'm creating a 2-dimensional heat map which has functionality when you click on any pixel. It grabs data associated with the index of every pixel (including adjacent pixels) and plots it. It currently looks like this:
The problem that I'm encountering is when I click on a left or right edge pixel, since it grabs data from adjacent pixels, it can retrieve data from the opposite side of the graph since it is all within a one-dimensional array. I am trying to create a conditional which checks if the clicked pixel is an edge case, and then configures the magnified graph accordingly to not show points from the other side of the main graph. This is the code I have so far:
// pushes all dataMagnified arrays left and right of i to magMainStore
var dataGrabber = function(indexGrabbed, arrayPushed) {
// iterates through all 5 pixels being selected
for (var b = -2; b <= 2; b++) {
var divValue = toString(i / cropLength + b);
// checks if selected index exists, and if it is not in the prior row, or if it is equal to zero
if (dataMagnified[indexGrabbed + b] != undefined && (& divValue.indexOf(".")!=-1)) {
dataMagnified[indexGrabbed + b].forEach(function(z) {
arrayPushed.push(z);
})
}
}
};
I am trying to get the same result as if I had a two dimensional array, and finding when the adjacent values within a single array is undefined. This is the line where I'm creating a conditional for that
if (dataMagnified[indexGrabbed + b] != undefined && (& divValue.indexOf(".")!=-1)) {
The second condition after the and is my attempts so far trying to figure this out. I'm unsure if I can even do this within a for loop that iterates 5 times or if I have to create multiple conditions for this. In addition, here's an image displaying what I'm trying to do:
Thank you!
Your approach looks overly complex and will perform rather slowly. For example, converting numbers to strings to be able to use .indexOf() to find a decimal point just for the sake of checking for integer numbers doesn't seem right.
A much simpler and more elegant solution might be the following function which will return the selection range bounded by the limits of the row:
function getBoundedSelection(indexGrabbed, selectionWidth) {
return dataMagnified.slice(
Math.max(Math.floor(indexGrabbed/cropLength) * cropLength, indexGrabbed - selectionWidth),
Math.min(rowStartIndex + cropLength, indexGrabbed + selectionWidth)
);
}
Here, to keep it as flexible as possible, selectionWidth determines the width of the selected range to either side of indexGrabbed. This would be 2 in your case.
As an explanation of what this does, I have broken it down:
function getBoundedSelection(indexGrabbed, selectionWidth) {
// Calculate the row indexGrabbed is on.
var row = Math.floor(indexGrabbed/cropLength);
// Determine the first index on that row.
var rowStartIndex = row * cropLength;
// Get the start index of the selection range or the start of the row,
// whatever is larger.
var selStartIndex = Math.max(rowStartIndex, indexGrabbed - selectionWidth);
// Determine the last index on that row
var rowEndIndex = rowStartIndex + cropLength;
// Get the end index of the selection range or the end of the row,
//whatever is smaller.
var selEndIndex = Math.min(rowEndIndex, indexGrabbed + selectionWidth);
// Return the slice bounded by the row's limits.
return dataMagnified.slice(selStartIndex, selEndIndex);
}
So I discovered that since the results of the clicked position would create a variable start and end position in the for loop, the only way to do this was as follows:
I started the same; all the code is nested in one function:
var dataGrabber = function(indexGrabbed, arrayPushed) {
I then create a second function that takes a start and end point as arguments, then passes them as the for loop starting point and ending condition:
var magnifyCondition = function (start, end) {
for (var b = start; b <= end; b++) {
if (dataMagnified[indexGrabbed + b] != undefined) {
dataMagnified[indexGrabbed + b].forEach(function (z) {
arrayPushed.push(z);
})
}
}
};
After that, I created 5 independent conditional statements since the start and end points can't be easily iterated through:
if (((indexGrabbed - 1) / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(-1, 2);
}
else if ((indexGrabbed / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(0, 2);
}
else if (((indexGrabbed + 1) / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(-2, 0);
}
else if (((indexGrabbed + 2) / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(-2, 1);
}
else {
magnifyCondition(-2, 2);
}
};
Lastly, I pass the index grabbed (i of the on clicked function) and an arbitrary array where the values get stored.
dataGrabber(i, magMainStore);
If there's a better way instead of the if statements, please let me know and I'd be happy to organize it better in the future!

Find polygon perimeter of points quickly in Javascript

I'm making a terrain editor and I need to find the perimeter polygon of a set of points. If I just needed a convex hull then the speed would be no issue. To make a concave hull, I must go through a few hoops. I've figured out that I can triangulate the points and then throw away any triangles with a side longer than the known distance between the points.
The next step is the problem: Combining the triangles (as mini polygons) into one large polygon using the JSTS geometry library (http://github.com/bjornharrtell/jsts) is really slow.
See the full code: http://codepen.io/anon/pen/oCfDh
I've got an array (polys) that gets merged to form the final polygon. The problem is that with 552 points (I want to support 15k+), it takes ~3500ms to run. Look at the console in the codepen link for your speed.
var reader = new jsts.io.WKTReader(),
merged = reader.read(polys[0]).union(reader.read(polys[1]));
console.time('jsts mergization');
for(var i = 2; i<polys.length; i++){
try{
merged = merged.union(reader.read(polys[i]));
}catch(err){
console.log('Error triangulating points!');
};
};
console.timeEnd('jsts mergization');
Does anybody know of any faster way to either merge triangles into a polygon or to go even wider and build a concave polygon from a set a points in a whole different way?
Thanks simonzack!
I've rewritten the algorithm using your suggestion and it's much faster!
Reworked codepen: http://codepen.io/anon/pen/Btdyj
The same example now runs in ~15ms!
function pointsToPolygon(points, triangles, maxEdgeLength){
console.time('homebrewed mergization');
var dist = function(a, b){
if(typeof a === "number"){
a = points[a];
};
if(typeof b === "number"){
b = points[b];
};
return Math.sqrt(Math.pow(a[0] - b[0], 2) +
Math.pow(a[1] - b[1], 2));
};
if(!points.length){
return undefined;
};
var pointFreq = [];
points.forEach(function(v){
pointFreq.push(0);
});
for(var i = triangles.length; i; i-=3){
if(dist(triangles[i-1], triangles[i-2]) < maxEdgeLength &&
dist(triangles[i-3], triangles[i-2]) < maxEdgeLength &&
dist(triangles[i-1], triangles[i-3]) < maxEdgeLength){
pointFreq[triangles[i-1]]++;
pointFreq[triangles[i-2]]++;
pointFreq[triangles[i-3]]++;
};
};
// Keep points that are used in 3 or fewer triangles
var output =[];
pointFreq.forEach(function(freq, i){
if(freq<4){
output.push(points[i]);
};
});
// Sort points by looping around by each next closest point
var sorted = [];
while(output.length>0){
sorted.push(output.pop());
output=output.sort(function(a,b){
var distA =dist(sorted[sorted.length-1], a),
distB =dist(sorted[sorted.length-1], b);
if(distA < distB){
return 1;
}else if(distA === distB){
return 0;
};
return -1;
});
};
sorted=simplifyPath(sorted,0.1);
console.timeEnd('homebrewed mergization');
return sorted;
};
I can find the boundary by filtering the points that are used in 3 or fewer triangles then sort points by looping around by each next closest point from any arbitrary point.
Maybe not 100% as accurate due to the Douglas-Peucker simplification algorithm (adapted from https://gist.github.com/adammiller/826148) but it seems good enough for me.

Javascript flood fill algorithm getting caught in an infinite loop

Hello I am trying to implement a simple flood fill type algorithm in javascript. Basically I have a 3x3 board which I represent as a 1 dimensional array. I want to append the index for every equal value that is "touching" to a separate array. So for instance this board:
[1][1][0]
[3][1][3]
[0][0][0]
Would be represented as a 1D array ie [1,1,0,3,1,3,0,0,0]. And after running the floodFill on one of the [1] it would result with an array that looks like this [4, 1, 0] because those are the indexes in the 1d array that are touching, which have the same value.
Here is the code:
var boardArray = new Array(1,1,0,3,1,3,0,0,0);
var comboArray = new Array();
function floodFill(n, diceVal) {
if(boardArray[n] != diceVal) {
return;
}
comboArray.push(n);
if (n >0 && n < 8) {
// right
if(!(n%3==2)) {
floodFill(n+1, diceVal);
}
// left
if(!(n%3==0)) {
floodFill(n-1, diceVal);
}
// up
if(n>2) {
floodFill(n-3, diceVal);
}
// down
if(n<5) {
floodFill(n+3, diceVal);
}
} else {
return;
}
}
floodFill(4,1);
Can anyone tell me why this is getting stuck in an infinite loop?
In your "up" case, the first time through, you'll call floodFill(1,1);. That call, in its "down" case, will call floodFill(4,1);, which will soon call floodFill(1,1)
You're already keeping track of the matching squares - the only ones that will really cause any trouble. Just confirm that you're not checking the same square again:
function floodFill(n, diceVal) {
if(boardArray[n] != diceVal) {
return;
}
// have we been here before?
if (comboArray.indexOf(n) >= 0)
return;
comboArray.push(n);
// ...
}

Prevent touching corners (JS Game)

How can I prevent this map generator from creating touching corners like this:
-X
X-
Or
X-
-X
Here is a simplified example of the generator: http://jsfiddle.net/fDv9C/2/
Your question answers itself, almost.
Here's the fiddle: http://jsfiddle.net/qBJVY/
if (!!grid[y][x] && !!grid[y+1][x+1] && !grid[y+1][x] && !grid[y][x+1]) {
good=false;
grid[y+1][x]=2;
}
It simply checks for the combinations you do not want and patches them up. It always adds a grid point so as not to disconnect any parts of the map.
This in turn may lead to another situation where the issue may occur, but if it changed anything (that is, if it found a problem), it will simply check again. This can be optimized, for instance by recursively adjusting whatever was changed, but usually it only needs 1 or 2 passes. There's a limiter on there to not allow more than 100 passes, just in case there is some unforeseen circumstance in which it cannot fix it (I can't think of such a situation, though :) ).
Because of the way that you are creating board it's very difficulty to do this checking during generation. I create simple function that check board after. It's using flood algorithm. Here is the fiddle http://jsfiddle.net/jzTEX/8/ (blue background is original map, red background is map after checking)
Basically we create second array grid2. After filling grid we run recursively floodV function
function floodV(x,y) {
var shiftArray = [[0,1],[0,-1],[1,0],[-1,0]];
grid2[y][x]=1;
for(var k=0;k<4;k++) {
var x1=x+shiftArray[k][0];
var y1=y+shiftArray[k][1];
if(grid[y1][x1] == 1 && grid2[y1][x1] == 0 && checkV(x1,y1)) {
grid2[y1][x1] = 1;
floodV(x1,y1);
}
}
}
with the check function
function checkV(x,y) {
var checkVarr = [[-1,-1], [-1,1], [1,1], [1,-1]];
for(var k=0;k<4;k++) {
if(grid[y+checkVarr[k][0]][x+checkVarr[k][1]] == 1 && grid[y+checkVarr[k][0]][x] == 0 && grid[y][x+checkVarr[k][1]] == 0 && grid2[y+checkVarr[k][0]][x+checkVarr[k][1]] == 1)
return false;
}
return true;
}
This isn't perfect because we can sometimes throw away big parts of the map but if we try to start adding new elements we have to check whole map again (in worths case).
This is what I did: http://jsfiddle.net/fDv9C/13/
Where's the magic happening? Scroll down to lines 53 through 58:
var bottom = y_next + 1;
var left = x_next - 1;
var right = x_next + 1;
var top = y_next - 1;
if (grid[top][left] || grid[top][right] ||
grid[bottom][left] || grid[bottom][right]) continue;
In short your touching corner points can only occur at the computed next position. Hence if any one of the four corner neighbors of the next position exists, you must compute another next position.
You may even decrement the counter i when this happens to get as many paths as possible (although it doesn't really make a big difference):
var bottom = y_next + 1;
var left = x_next - 1;
var right = x_next + 1;
var top = y_next - 1;
if (grid[top][left] || grid[top][right] ||
grid[bottom][left] || grid[bottom][right]) {
i--;
continue;
}
See the demo here: http://jsfiddle.net/fDv9C/12/
Edit: I couldn't resist. I had to create an automatic map generator so that I needn't keep clicking run: http://jsfiddle.net/fDv9C/14/

Categories