Finding inaccessible points on a 2D plane - javascript

I have been working on JavaScript / JQuery code which allows arrow key movement between input boxes (yes, I am aware this breaks standard UI).
It works by by looping through each element and finding the closest in each direction (left, right, up and down).
Example
P1:(0, 0), P2:(1, 0), P3:(0, 2)
P1 has one point to the right (P2) and one point up (P3).
P2 has one point to the left (P1) and one point up (P3).
No picture
P3 has two points down (P1 & P2) but P1 is closer.
Therefore the final movements are:
Up
1 -> 3
2 -> 3
Right
1 -> 2
Down
3 -> 1
Left
2 -> 1
For this example:
P1 has two incoming and two outgoing connections.
P2 has one incoming and two outgoing connections.
P3 has two incoming and one outgoing connections.
This made me think.
Is there a set of points such that one or more point is inaccessible (0 incoming connections), or can it be proven no such set exists?
Side note:
If you ignore the up / down component (only using left and right with a vertical split) then point P3 in inaccessible in P1: (0, 0), P2: (2, 0), P3: (1, 4).
Here is the JavaScript / JQuery code if it will help anyone.
function arrowKeyNavigation(elements) {
// Get the position of each element.
var elementOffsets = [];
elements.each(function(key, element) {
elementOffsets[key] = $(element).offset();
});
// Find the closest point in each direction and store the points in data('keyNav') for later use.
for (var i = 0; i < elementOffsets.length; i++) {
var closestPoints = [];
for (var j = 0; j < elementOffsets.length; j++) {
if (i != j) {
var distance = calcDistanceSquared(elementOffsets[i], elementOffsets[j]);
var quadrant = calcQuadrant(elementOffsets[i], elementOffsets[j]);
if (closestPoints[quadrant] == undefined || calcDistanceSquared(elementOffsets[i], elementOffsets[closestPoints[quadrant]]) > distance) {
closestPoints[quadrant] = j;
}
}
}
var closestElements = [];
for (var j = 0; j < closestPoints.length; j++) {
closestElements[j] = elements[closestPoints[j]];
}
$(elements[i]).data('keyNav', closestElements);
}
}
// Returns the distance between two points squared.
function calcDistanceSquared(offset1, offset2) {
...
}
// Returns 0, 1, 2 or 3 for left, up, right and down respectively.
// If a point is EXACTLY 45 degrees it will be classified as either left / right.
function calcQuadrant(offset1, offset2) {
...
}

I've thought about it more and I think I have the solution.
The proof sketch goes as follows:
Assume you have a finite number of points in the plane (R^2). Take an arbitrary point and call it your destination. Then take any other point. That point divides R^2 into four quadrants, as you've drawn in red. By definition the destination is in one of those four quadrants. Move in that direction. One of two things can happen:
1) you arrive at the destination and you're done
2) you move to another point.
If 2, then you've moved closer (EDIT: by the 1-norm distance, d((x1,y1),(x2,y2)) = |x1-x2|+|y1-y2|). This requires more proof but I'm just sketching. The destination is now in some quadrant of this new point.
Now note that If you repeat this, always moving one step closer in the direction of the destination (which may change each step) your distance to the destination point will decrease each step; you can never revisit a point because you distance is always decreasing. So, if you have a finite number of points you'll eventually reach your destination.

Related

PIXI Logic is Skewed from the Actual Visuals

I am pretty new to JavaScript and PIXI. I am making a little game that requires the player to navigate and grab a key (move onto the same space as the key). This takes them to the next level. My problem is that whenever a new level is generated, it somehow uses the x and y coordinates for the key of the previous level to skew the logic. Because of this, the first level works fine, but every level afterwards is messed up.
The levels get skewed up and to the left by the respective values of the previous key. I have a 2d array in the background which holds values of where and where not the player can (holds values for things like walls, turrets, and the key). I randomly generate the key coordinates and for example set grid[9][12] = 3; for the key space. The next level visually looks perfect, with the walls and key generating, and the player being in the top left. However because of the skew, the top left of the visuals is really at [max_X - 9][max_Y - 12] and the player can go off screen to invisibly access the rest of the maze. So I can then phase through walls because the logic has the walls in a different place than the visuals, and the actual key space usually ends up off screen.
Here is my code that generates a new goal (key) object
function createGoal(scene) {
while (true) {
let x = getRandomInt(APP_WIDTH);
let y = getRandomInt(APP_HEIGHT);
//if space is not already occupied, populate
if (grid[x][y] == 0) {
console.log(x);
console.log(y);
goal = null;
goal = new Goal(TILE_WIDTH, x, y);
scene.addChild(goal);
grid[x][y] = 3;
break;
}
}
}
And here is the grid initialization:
const grid = new Array(APP_WIDTH);
for (let i = 0; i < APP_WIDTH; i++) {
grid[i] = new Array(APP_HEIGHT);
for (let j = 0; j < APP_HEIGHT; j++) {
grid[i][j] = 0;
}
}
Any help would be greatly appreciated.

Three JS Raycasting - Find point closest to cursor

Three.js r85
When raycasting with Three JS, a series of points is returned, and I'd like to find the point that is closest to the cursor. The first point returned seems to be the point that is closest to the camera.
Is there a way to find the distance between the cursor position and a point?
Here's the code I'm using to debug this right now:
var raygun = new THREE.Raycaster();
raygun.setFromCamera(mouse, camera);
var hits = raygun.intersectObjects([plotpoints]);
if (hits.length > 0) {
scope.remove(dotPlot);
scope.remove(dotPlot2);
// All points except the first one - Grey
dotGeo = new THREE.Geometry();
for (var i=1; i < hits.length; i++) {
dotGeo.vertices.push(plotpoints.geometry.vertices[hits[i].index]);
}
dotPlot = new THREE.Points(dotGeo, dotMat);
scope.add(dotPlot);
// First point - Orange
var geo2 = new THREE.Geometry();
geo2.vertices.push(plotpoints.geometry.vertices[hits[0].index]);
dotPlot2 = new THREE.Points(geo2, dotMat2);
scope.add(dotPlot2);
scope.render();
}
And here's what I'm seeing:
Ah, figured it out with math!
First thing to note is that hits[].points returns a point directly under the cursor, but it doesn't "snap" to points.
In order to get the actual position of the point, we need to use hits[].index first to get the index number of the point/vertex we hit. We can then access that vertex directly by using GEOMETRY.vertices[] which returns a THREE.Vector3 of the vertex point we hit with our raycast.
So by feeding in the index, we can get the exact position of each vertex hit by our raycast:
GEOMETRY.vertices[hits[i].index]
This provides rudimentary "snapping" to vertices.
Note: When using THREE.LineSegments, the result will always be the starting point, and not the ending point. To get the ending point, you can just add 1 to the index value:
GEOMETRY.vertices[hits[i+1].index]
To snap directly to the vertex closest to the cursor, we need to find the vertex that has the shortest perpendicular distance from the raycaster's ray. To do this we use a cross product of 2 vectors. This is more of a math concept than a programming concept though, so if you want to understand the why behind this, look up something like: perpendicular distance from a point to a line
I just took the code from this question and translated it: http://answers.unity3d.com/questions/568773/shortest-distance-from-a-point-to-a-vector.html
And the end result:
// Variables to record and compare
var smallestDist = 99;
var smallestPointIndex = 0;
// Declare variables outside of loop to save memory
var m_ray = raycaster.ray;
var raydir = m_ray.direction;
var origin = m_ray.origin;
var hitray = new THREE.Vector3(0,0,0);
var dist = 1;
// Loop over all points to find the closest
for (var i=0; i<hits.length; i++){
// Math is magic
hitray.subVectors(plotpoints.geometry.vertices[hits[i].index], origin);
dist = new THREE.Vector3().crossVectors(raydir, hitray).lengthSq();
// Record the closest point
if (dist < smallestDist) {
smallestDist = dist;
smallestPointIndex = i;
}
}
// Now we can use that single point
Here's the result :)

Custom THREE.Curve.create displays incorrectly

As suggested in this answer, I've created a linearly interpolated curve like this:
THREE.Linear3 = THREE.Curve.create(
function ( points, label /* array of Vector3 */) {
this.points = (points == undefined) ? [] : points;
this.label = label;
},
function ( t ) {
var v = new THREE.Vector3();
var c = [];
var points = this.points, point, intPoint, weight;
point = ( points.length - 1 ) * t;
intPoint = Math.floor( point );
weight = point - intPoint;
c[ 1 ] = intPoint;
c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1;
var pt1 = points[ c[1] ],
pt2 = points[ c[2] ];
v.copy( pt1 ).lerp( pt2, weight );
return v;
}
);
However, when I'm trying to display a trajectory at different lengths (in an animated kinda-way) I get the following behavior i.e. instead of the curve going through the points, it kinda cuts through the space, note that in the example below each trajectory is supposed to go through the coordinates of each of the spheres (animated gif below):
I am not sure I understand the getPoint function or what is it supposed to return. Any Help is greatly appreciated.
JSFiddle
This is a minimal example but you can see how the right corner has a jerky motion as the tube expands.
http://jsfiddle.net/ElDeveloper/3uyf3sq3/1/
Cleaning some code
That helped me investigate the issue.
You are leaking geometries, you need to dispose the geometry after removing the mesh from the scene
scene.remove(c_mesh)
c_tube && c_tube.dispose();
Use WebGLRenderer. The CanvasRenderer leaks removed objects, and you're creating new objects on each frame. (If you're stuck with CanvasRenderer for some reason, sorry for you)
(For the fiddle) slow the motion, requestAnimationFrame isn't required for a test, setTimeout(animate, 500); allows the user to see what's happening.
What's the point of a 0-segment tube ?
if (index >= points.length - 1){
index = 1; //start with 2 points
}
Works as expected
The TubeGeometry does a tube of N (second argument in constructor, 16 in fiddle) segments. (I'll come back to that later, but I don't think you always want 16 segments)
The default behaviour of Curve.getPoinAt (the method used by TubeGeometry and probably lots of other geometries) is to return equidistant points. You can expect: distance(getPointAt(0),getPointAt(0.1)) == distance(getPointAt(0.1),getPointAt(0.2)) to be true.
Because of these points, no matter how many points you put in your path, the TubeGeometry will build a 16-segments tube with all segment of the same length, going from the first to the last point of your path. There is little chance that one of the 15 intermediate points will be exactly at the position of an edge. That should explain what you see.
Trying to fix the stuff
First get rid of that equidistant way-to-be of the TubeGeometry+Path. Overloading getUtoTmapping should be enough (I found that reading the source):
THREE.Linear3.prototype.getUtoTmapping = function(u) {
return u;
};
I changed your getPoint. It probably does the same thing, but I was more comfortable with my code the investigate
function ( t ) {
var points = this.points;
var index = ( points.length - 1 ) * t;
var floorIndex = Math.floor(index);
if(floorIndex == points.length-1)
return points[floorIndex];
var floorPoint = points[floorIndex];
var ceilPoint = points[floorIndex+1];
return floorPoint.clone().lerp(ceilPoint, index - floorIndex);
}
Give the correct number of segments to the TubeGeometry constructor:
var pathPoints = points.slice(0, index);
c_path = new THREE.Linear3(pathPoints, 'Test');
c_tube = new THREE.TubeGeometry(c_path, pathPoints.length-1, 10, 16, false, true);
At this point, you should have roughly what you were expecting
You should see the tube always going through the edges. You should also see that the TubeGeometry isn't meant to have angles. You could improve this angle issue either by looking at how TubeGeometry and Curve handles tangents, or (if you don't care about slowness) by increasing the number of segments to a very large number:
c_tube = new THREE.TubeGeometry(c_path, 200/*pathPoints.length-1*/, 10, 16, false, true);
That's all for the answer. You can find the last version of my experiments here: http://jsfiddle.net/dqn73m98/5/. You can also ask the three.js developers if such a feature exists, or request an implementation in a github issue (if someone as time to do it), here

Color all adjacent same tiles iteratively

I try to create a paint bucket tool. I need to find all adjacents points of the point I clicked, and change their color if they have the same color than the original. The color need to propagate on all points with same color. The propagation can only be done on 4 directions (no diagonals).
I can do this easily recursively, but sadly I get an error when the map is too big :
Uncaught RangeError: Maximum call stack size exceeded
This is a basic example to reproduce the problem, and I would like to transform it to iterative way:
// Fill the map
var map = [];
for (var x = 0; x < 500; x++){
var row = [];
for (var y = 0; y < 500; y++){
row.push(1);
}
map.push(row);
}
var paintTile = function(x, y){
// If X or Y is out of map range
if (x < 0 || x >= 500 || y < 0 || y >= 500){
return
}
// If this tile is already painted in new color
if (map[x][y] == 0){
return;
}
// Draw tile with new color
map[x][y] = 0;
// Paint all adjacent tiles
paintTile(x - 1, y);
paintTile(x + 1, y);
paintTile(x, y - 1);
paintTile(x, y + 1);
};
paintTile(0, 0);
In this example, all the map is populated of "1" (let's say it's white color), and I transform them to "0" (black color), but I get this stack size error.
Regards
Do you mean Flood fill algorithm?
From http://en.wikipedia.org/wiki/Flood_fill:
Flood-fill (node, target-color, replacement-color):
1. Set Q to the empty queue.
2. If the color of node is not equal to target-color, return.
3. Add node to Q.
4. For each element N of Q:
5. If the color of N is equal to target-color:
6. Set w and e equal to N.
7. Move w to the west until the color of the node to the west of w no longer matches target-color.
8. Move e to the east until the color of the node to the east of e no longer matches target-color.
9. For each node n between w and e:
10. Set the color of n to replacement-color.
11. If the color of the node to the north of n is target-color, add that node to Q.
12. If the color of the node to the south of n is target-color, add that node to Q.
13. Continue looping until Q is exhausted.
14. Return.
Keep a list of tiles you need to process and tiles you have processed. Put your first tile in the ToBeProcessed list and then loop repeatedly until your to be processed list is empty.
On each loop firstly check if you want to recolour this tile or not. If you don't then remove it from the ToBeProcessed and go to the next cycle of the loop. If it does then do the normal processing (ie change the colour). Then add the adjacent tiles to the ToBeProcessed list if they aren't on the already processed list (or already in the ToBeProcessed list).
Then at the end of your loop iteration remove the current item from the ToBeProcessed list.
here is a video on the topic: https://www.youtube.com/watch?v=LvacRISl99Y
Instead of using complex logic to track previously verified neighbor spaces, I have a 2D array that records all the verified spaces. Reading from the verified array is 2-3 instructions: IF pixel[23,23] is verified, THEN fill it and check it's neighbors.

Google Maps API V3 - Showing progress along a route

I want to show a custom route on a route along with the current progress. I've overlayed the a Polyline to show the route and I am able to find the LatLng of the current postion and place a marker. What I want to do now is to highlight the traveled part of the Polyline using a different colour. I'm pretty sure it's not possible to have multiple colours for one Polyline so I plan to overlay a second Polyline over the first the first to give this effect. Here's my question:
Knowing the LatLng of the current position and armed with the array of LatLng points used to create the orginal Polyline how best can I create the second 'Progess' route?
Thanks ahead.
With a few assumptions:
Your path is quite dense (if not, you could interpolate intermediate points)
Your route doesn't overlap with itself (wouldn't work with a repeating circular path, say)
..one crude way would be as follows:
Using Python'y pseudo-code, say you have a route like this:
points = [LatLng(1, 1), LatLng(2, 2), LatLng(3, 3), LatLng(4, 4)]
You draw this as a PolyLine as usual.
Then, given your current position, you would find the nearest point on the route:
cur_pos = LatLng(3.1, 3.0123)
nearest_latlng = closest_point(points, to = cur_pos)
Then nearest_latlng would contain LatLng(3, 3)
Find nearest_latlng in the list, then simply draw a second polyline up to this point. In other words, you truncate the points list at the current LatLng:
progress_points = [LatLng(1, 1), LatLng(2, 2), LatLng(3, 3)]
..then draw that on the map
As mentioned, this will break if the path loops back on itself (closest_point would only ever find the first or last point)
If you know how far has been travelled, there is an epoly extension which gives a few methods that could be used, mainly:
.GetIndexAtDistance(metres)
Returns the vertex number at or after the specified distance along the path.
That index could be used instead of the closest_point calculated one above
Typescript example:
getRouteProgressPoints(directionResult: google.maps.DirectionsResult | undefined, targetDistance: number) {
if (!directionResult || targetDistance <= 0) return [];
const route = directionResult.routes[0];
const leg: google.maps.DirectionsLeg = route.legs[0];
const routePoints: google.maps.LatLng[] = [];
// collect all points
leg.steps.forEach((step) => {
step.path.forEach((stepPath) => {
routePoints.push(stepPath);
});
});
let dist = 0;
let olddist = 0;
const points = [];
// go throw all points until target
for (let i = 0; i < routePoints.length && dist < targetDistance; i++) {
const currentPoint = routePoints[i];
// add first point
if (!i) {
points.push(currentPoint);
continue;
}
const prevPoint = routePoints[i - 1];
olddist = dist;
// add distance between points
dist += google.maps.geometry.spherical.computeDistanceBetween(prevPoint, currentPoint);
if (dist > targetDistance) {
const m = (targetDistance - olddist) / (dist - olddist);
const targetPoint = new google.maps.LatLng(
prevPoint.lat() + (currentPoint.lat() - prevPoint.lat()) * m,
prevPoint.lng() + (currentPoint.lng() - prevPoint.lng()) * m,
);
points.push(targetPoint);
} else {
points.push(currentPoint);
}
}
return points;
}
That is going to be really difficult unless you already have a way of determining that the vehicle has passed certain points already. If you are aware that it has passed certain points you just create the second polyline with the passed points and the current marker as the end of the polyline. You will probably want to change the width of the polyline in order to make it viewable when it is overlaying the initial route.
All of that being said if you don't know if the points have been passed I am not sure what you can do other than write some sort of code that determines if your current position is past a certain set of points, but I don't know how you do that if a route zigzags all over the place possibly..... Now if your points go from straight west to straight east etc, than coding something will be easy, but I doubt that is the case.
Hope this helps somewhat....

Categories