Javascript collision of two point array polygons - javascript

I have searched all over, and I have found answers for rectangle circle and sprite collisions. Nothing that provides collision detection between two arrays of points like for example,
var poly1=[
[0,0],
[20,50],
[50,70],
[70,20],
[50,0]
];
// each point from one to the next represent a line in the shape, then the last point connects to the first to complete it.
var poly2=[
[50,30],
[40,90],
[70,110],
[90,70],
[80,20]
];
var collided=arraysCollided(poly1,poly2);
Does anyone know of a library that can do just this? My research has come up with nothing that supports just that, and isnt associated with some game engine library.
For example a collision is triggered true when one or more points is inside the polygon of the other.

SAT.js was the anser for me, I just put every point into SAT.Vector then into SAT.Polygon, then test them with SAT.testPolygonPolygon(SAT.Polygon,SAT.Polygon);
var poly1={
name:"poly2",
x:400,
y:60,
rotation:0,
runner:function(){
},
points:[
[20,-50],
[-30,-50],
[-30,30],
[10,60],
[50,20]
]
};
var poly2={
name:"poly2",
x:50,
y:70,
runner:function(){
this.x+=1;
},
points:[
[-20,-40],
[-60,50],
[10,70],
[50,30],
[30,-20]
]
};
pGon=(poly)=>{
var center=SAT.Vector(0,0);
var pts=[];
for(var i in poly.points){
var point=poly.points[i];
// point = [0:x,1:y]
pts[pts.length]=new SAT.Vector(point[0]+poly.x,point[1]+poly.y);
}
var poly_a=new SAT.Polygon(center,pts);
return poly_a;
};
pCollide=(p1,p2)=>{
var p1_poly=pGon(p1);
var p2_poly=pGon(p2);
var res=new SAT.Response();
var collided=SAT.testPolygonPolygon(p1_poly,p2_poly,res);
console.log(collided);
console.log(res);
return collided;
};
var collided=pCollided(poly1,poly2);
With that, it maps each point to a polygon on the coordinate system, then tests it from there. So collided = true

I checked for if each point of each polygon is in the other polygon. This is the code for checking if a point is in a polygon:
function pip(x, y, polygon) {
let odd = false;
let v = polygon.va; //The vertices array
let j = v.length - 2;
for (let i=0; i<v.length-1; i+=2) {
if ((v[i+1]<= y && v[j+1]>=y || v[j+1]<= y && v[i+1]>=y)
&& (v[i]<=x || v[j]<=x)) {
odd ^= (v[i] + (y-v[i+1])*(v[j]-v[i])/(v[j+1]-v[i+1])) < x;
}
j=i;
}
if(odd === false) odd = 0;
return odd;
}
I got this from Here, but modified it to work for an array like this [x1,y1,x2,y2,x3,y3...]. To make it work for x,y pairs, you would just change the for loops modifier thing and look at polygon.va[i][0] as x and polygon[i][1] as y.

Related

Why is my maze generator not detecting if a cell has been visited in p5.js?

I am trying to make a maze generator, and almost everything is working so far. I have been able to set my position to a random pos, and then I repeat the standard() function. In the function, I add pos to posList, and then I choose a random direction. Next, I check if the cell has been visited by running through all of the posList vectors in reverse. I haven't executed the code that backtracks yet. If visited = false then I move to the square and execute the yet-to-be-made path() function. However, for some reason, the mover just doesn't detect if a cell has been visited or not. I am using p5.js. What am I doing wrong?
var posList = [];
var pos;
var tempo;
var boole = false;
var direc;
var mka = 0;
function setup() {
createCanvas(400, 400);
//Set up position
pos = createVector(floor(random(3)), floor(random(3)));
frameRate(1)
}
//Choose a direction
function direct(dire) {
if(dire === 0) {
return(createVector(0, -1));
} else if(dire === 1) {
return(createVector(1, 0));
} else if(dire === 2) {
return(createVector(0, 1));
} else {
return(createVector(-1, 0));
}
}
/foLo stands fo forLoop
function foLo() {
//If we have checked less than three directions and know there is a possibility for moving
if(mka < 4) {
//tempoRARY, this is what we use to see if the cell has been visited
tempo = createVector(pos.x + direct(direc).x, pos.y + direct(direc).y);
//Go through posList backwards
for(var i = posList.length - 1; i >= 0; i --) {
//If the cell has been visited or the cell is off of the screen
if(tempo === posList[i]) {
//Change the direction
direc ++;
//Roll over direction value
if(direc === 4) {
direc = 0;
}
//Re-execute on next frame
foLo();
//The cell has been visited
boole = false;
//Debugging
console.log(direc)
mka++;
} else if(tempo.x < 0 || tempo.x > 2 || tempo.y < 0 || tempo.y > 2) {
direc ++;
if(direc === 4) {
direc = 0;
}
foLo();
boole = false;
console.log(direc)
mka++;
}
}
//If it wasn't visited (Happens every time for some reason)
if(boole === true) {
//position is now the temporary value
pos = tempo;
console.log("works")
mka = 0;
}
}
}
function standard() {
//Add pos to posList
posList.push(pos);
//Random direction
direc = floor(random(4));
//Convert to vector
direct(direc);
foLo();
//Tracks pos
fill(255, 255, 0);
rect(pos.x*100+50, pos.y*100+50, 50, 50)
}
function draw() {
background(255);
fill(0);
noStroke();
//draw grid
for(var i = 0; i < 4; i ++) {
rect(i*100,0,50,350);
rect(0, i*100, 350, 50);
}
standard();
boole = true;
console.log(pos)
console.log(posList);
}
Your issue is on the line where you compare two vectors if(tempo === posList[i]) {: This will never be true.
You can verify that with the following code (in setup() for example):
const v1 = new p5.Vector(1, 0);
const v2 = new p5.Vector(1, 0);
const v3 = new p5.Vector(1, 1);
console.log(v1 === v2) // false
console.log(v1 === v3) // false
This is because despite having the same value v1 and v2 are referencing two different objects.
What you could do is using the p5.Vector.equals function. The doc has the following example:
let v1 = createVector(10.0, 20.0, 30.0);
let v2 = createVector(10.0, 20.0, 30.0);
let v3 = createVector(0.0, 0.0, 0.0);
print(v1.equals(v2)); // true
print(v1.equals(v3)); // false
This might not give you a working algorithm because I suspect you have other logical errors (but I could be wrong or you will debug them later on) but at least this part of the code will do what you expect.
Another solution is to use a Set instead of your list of positions. The cons of this solution is that you will have to adapt your code to handle the "out of grid" position situation. However when you want to keep track of visited items a Set is usually a great solution because the access time is constant. That this means is that to define is a position has already been visited it will always take the same time (you'll do something like visitedSet.has(positionToCheck), whereas with your solution where you iterate through a list the more cells you have visited to longer it will take to check if the cell is in the list.
The Set solution will require that you transform your vectors before adding them to the set though sine, has I explained before you cannot simply compare vectors. So you could check for their string representation with something like this:
const visitedCells = new Set();
const vectorToString = (v) => `${v.x},{$v.y}` // function to get the vector representation
// ...
visitedCells.add(vectorToString(oneCell)); // Mark the cell as visited
visited = visitedCells.has(vectorToString(anotherCell))
Also has a general advice you should pay attention to your variables and functions name. For example
// foLo stands fo forLoop
function foLo() {
is a big smell: Your function name should be descriptive, when you see your function call foLo(); having to find the comment next to the function declaration makes the code less readable. You could call it generateMaze() and this way you'll know what it's doing without having to look at the function code.
Same for
//tempoRARY, this is what we use to see if the cell has been visited
tempo = createVector(pos.x + direct(direc).x, pos.y + direct(direc).y);
You could simply rename tempo to cellToVisit for example.
Or boole: naming a boolean boole doesn't convey a lot of information.
That could look like some minor details when you just wrote the code but when your code will be several hundred lines or when you read it again after taking several days of break, you'll thank past you for taking care of that.

Leaflet screen distance to LatLng distance

I want to combine markers based on the zoom level. I'm not using Markercluster but my own algorithm to detect which markers should be combined. This algorithm works perfectly fine. The only thing I have to add is a zoom-based condition when the markers should be combined. Currently, all markers within a distance of 0.001 get combined. What I want is that every marker within a distance of 0.5cm on the screen gets combined. So I need a function that converts a distance in cm, px, or something else into a distance in degree.
An example: On zoom-level 18, two markers with a distance of 0.00005 in the longitude have a distance of nearly 0.3cm on my screen.
EDIT:
So this is what I did:
function in_range(location1, location2, map) {
return get_distance_in_px(location1, location2, map) < 25;
}
function get_distance_in_px(location1, location2, map) {
var p1 = map.latLngToContainerPoint(L.latLng(location1[0], location1[1]));
var p2 = map.latLngToContainerPoint(L.latLng(location2[0], location2[1]));
var a = p1.x - p2.x;
var b = p1.y - p2.y;
return Math.sqrt(a * a + b * b);
}
You can get the containerPoint of the latlngs and then calculate the distance:
map.on('zoomend',(e)=>{
console.log(e);
recalcZoom();
});
var DISTANCE = 20; //px
function recalcZoom() {
var layers = findLayers(map);
var resultLayers = []; //sturcture: {main: layer, childs: [];
layers.forEach((layer)=>{
if(resultLayers.length === 0){ // set the first layer as main layer
resultLayers.push({
main: layer,
childs: []
});
}else{
var found = false;
var mainObj = null;
var lastDis = null;
resultLayers.forEach((rLayer)=>{
var main = rLayer.main;
var p1 = map.latLngToContainerPoint(main.getLatLng());
var p2 = map.latLngToContainerPoint(layer.getLatLng());
var dis = p1.distanceTo(p2);
if(dis <= DISTANCE){ // distance between main layer and current marker is lower then DISTANCE
if(lastDis == null || dis < lastDis) { // a main layer is found, where the distance between them is lower
if(mainObj && mainObj.childs.indexOf(layer) > -1){ // remove the layer from the old main layer childs array
mainObj.splice(mainObj.childs.indexOf(layer),1);
}
rLayer.childs.push(layer);
found = true;
mainObj = rLayer;
}
}
});
if(!found){ // if no main layer is found, add it as new main layer
resultLayers.push({
main: layer,
childs: []
});
}
}
});
console.log(resultLayers);
// Logic to find center of all childs + main
// remove the old layers and add the new clustered layer
// keep in mind, that you have to store somewhere the original markers, else you can't recalc the clusters
}
function findLayers(map) {
// your logic to get the original layers
let layers = [];
map.eachLayer(layer => {
if (layer instanceof L.Marker) {
layers.push(layer);
}
});
return layers;
}
You have to implement by yourself the logic to find center of all childs + main layer, then remove the old layers and add the new clustered layer.
But keep in mind, that you have to store somewhere the original markers, else you can't recalc the clusters.
Little Example: https://jsfiddle.net/falkedesign/ny9s17cb/ (look into the console)
A note from the Leaflet Examples
One of the disadvantages of using a cylindrical projection is that the
scale is not constant, and measuring distances or sizes is not
reliable, especially at low zoom levels.
In technical terms, the cylindrical projection that Leaflet uses is
conformal (preserves shapes), but not equidistant (does not preserve
distances), and not equal-area (does not preserve areas, as things
near the equator appear smaller than they are).
learn more
After that being said you're really getting yourself into so many more problems trying to get the dimension exact where they aren't reliable measurements to start with.

meet strange during build up the basic 2D grids for future simulation in javascript?

I am doing one project while stuck at the first step. The idea is to set up a 3D model. The (x,y) is the geometry area and z is the value of interests. I need simulate all area of (x,y) coordinates.Firstly, I try to initiate the z value for all (x,y) coordinate. I met problems at this step by following codes.
<script type="text/javascript">
// use the rectangle as the "unit" of the 2D model.
var x = [];
var y = [];
var z = [];
var R1 = 5;
var dataset = [];
// use function to initiate a row
var rowGen = function(k){
for (j=0;j<10;j++){
x.push(j*5);
y.push(k*2);
z.push(R1);
dataset.push({x:x[j],y:y[j],z:z[j]});
}
}
// initiate 10 rows
for (i=0;i<10;i++){
rowGen(i);
}
This always just initiate the z value for parts of the (x,y) area. Just the first row (y=0). I checked dataset in console, it has the correct numbers(100) of objects (x,y,z), while all of them have y=0, which shows in the figure only one line, instead of 2D area.
I tried a different loops to do that but just can not find out the solution. Could you please help bring me some light?
Just found the logic bug. I had pushed the wrong value into y.Changed the code as below works.
// use the rectangle as the "unit" of the 2D model.
var x = [];
var y = [];
var z = [];
var R1 = 5;
var dataset = [];
// use function to initiate a row
var rowGen = function(k){
for (j=0;j<10;j++){
x.push(j*5);
y.push(k*2);
z.push(R1);
dataset.push({x:x[j],y:k*2,z:z[j]});
}
}
// initiate 10 rows
for (i=0;i<10;i++){
rowGen(i);
}

All series data are equal highcharts

I have function for updating sensors. Idea is simple, i add new points from sensors (1 sensor = 1 line) and after appending I update charts. But something went wrong and all series equal to themself
seriesArr - just array of sensors like {"sensorId1", sensorId2 etc}
chartObj - object of charts like chart 1: { chart: highcharts,
seriesArr: seriesArr }
Simple explanations:
chartObject.chart.series[0].points = chartObject.chart.series[1].points = chartObject.chart.series[2].points = etc
My code:
updateAllCharts: function() {
var allCharts = this.charts;
console.log('charts');
for (var chart in allCharts) {
var chartObject = allCharts[chart];
for (var i = 0; i < chartObject.seriesArr.length; i++) {
var elemId = chartObject.seriesArr[i][0];
var val = this.sensors[elemId].get('value');
console.log(val);
if (val === undefined) { // if sensor cant get data
continue;
}
var now = new Date;
var x = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),
now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds());
var y = parseFloat(val);
chartObject.chart.series[i].addPoint([x, y], false, false);
console.log(x, y);
console.log(chartObject.chart.series[i].data);
}
}
for (var chart in allCharts) {
var chartObject = allCharts[chart];
chartObject.chart.redraw();
}
}
Screen:
UPDATE:
ProcessedDataX and Y arrays are changing, but there problem with points array, I always get some weird (maybe cached) points. WTF.
Playground
If you wanna play, i've setted up here jsfiddle, but actually it probably doesn't work. Can't add points to highchart object with setInterval.
JSFIDDLE
UPDATE2:
Actually it works fine in jsfiddle but i don't know wtf is going on in my project.
UPDATE3:
Found, that the same last point adds to each series. For example, series[i].lastPoints = series[n] , where n = last iteration.
Works, only if i REDRAW EVERYTIME AFTER ADDING EACH POINT. so bad solution, so bad.
just put true in parameter after Point object
chartObject.chart.series[z].addPoint(Point, true, false);
To all chart.redraw() you need to set isDirty flag
so try to use this code:
for (var chart in allCharts) {
var chartObject = allCharts[chart];
chartObject.yAxis[0].isDirty = true;
chartObject.chart.redraw();
}

Can anyone explain this snippet of Javascript?

Can anyone explain the following code? Forget the sine and cosine parts. Is it trying to build a space for the object?
objectsInScene = new Array();
for (var i=space; i<180; i+=space) {
for (var angle=0; angle<360; angle+=space) {
var object = {};
var x = Math.sin(radian*i)*radius;
object.x = Math.cos(angle*radian)*x;
object.y = Math.cos(radian*i)*radius;
object.z = Math.sin(angle*radian)*x;
objectsInScene.push(object);
}
}
If I'm not much mistaken it's arranging objects in a hemispherical shape.
objectsInScene is an array of all these objects.
It's filling objectsInScene with a sphere of points (not a hemisphere), spaced space degrees apart. The diameter is 2 times radius.

Categories

Resources