Find area within set distance from point in a complicated polygon - javascript

Given a simple (non intersecting) polygon such as a floor plan (with doors between rooms missing so as to give 1 simple uninterrupted boundary). How can I find all areas within the polygon reachable from a (x, y) point (within or on the boundary of the polygon)? I’d ideally like from this to return a polygon which could be then overlaid to show all reachable areas.
I’ve considered A* search type methods where I would search the shortest path iterating over all points located on the polygon perimeter (as destination) then plotting new points at the set distance limit along the shortest path polyline to give a new polygon hull.
I’ve also thought about wave propagation as a method.
I’m wondering if I’m missing something obvious here library/method wise and if anyone has any other ideas of how I might achieve this.
Given a polygon like this:
I am creating a polygon showing internal space (excluding internal doors) like this:
This is the part my question refers to. I would like to find all reachable points within the polygon (shown in red as a new polygon) from a given point on the polygon boundary at a set maximum travel distance from that point (donated below with a red square) like this:

Triangulate the polygon.
If your chosen origin vertex is not a polygon vertex (i.e. it's a point within the polygon), include this point as a steiner point in the triangulation.
Build an undirected weighted graph from the triangulation's vertices and constrained edges (where graph edge weights are triangulation edge lengths).
Constrained edges are edges that do not lie outside the polygon.
Compute shortest paths from your origin vertex to all other vertices in the graph (using Dijkstra or Bellman-Ford algorithm). The path distance from the origin to a vertex is that vertex's Z value.
Update/create another triangulation mesh, using the same vertices with the Z values calculated from before.
Calculate distance values for every pixel by interpolating within/between triangles (interpolating based on each triangle's vertices' Z values). This is readily done by using barycentric coordinates. The interpolation output for a coordinate gives you the distance from the origin position to that coordinate.
For the illustrations below I used the NaturalNeighborInterpolator from the TinFour Java library. It eases the interpolation step by operating on a triangulation — I simply call the interpolator at each pixel coordinate and finally mask the output with the original polygon (since it effectively computes over the convex hull of the polygon).
Illustrative Code
The graph and Dijkstra implementation use the JGraphT library.
IncrementalTin tin = new IncrementalTin();
tin.add(listOfPolygonVertices); // triangulates upon insertion
Graph<Vertex, IQuadEdge> graph = new DefaultUndirectedWeightedGraph<>(IQuadEdge.class);
tin.edges().forEach(e -> {
if (e.isConstrainedRegionInterior() || e.isConstrainedRegionBorder()) {
graph.addVertex(e.getA());
graph.addVertex(e.getB());
graph.addEdge(e.getA(), e.getB(), e);
graph.setEdgeWeight(e.getA(), e.getB(), e.getLength());
}
});
DijkstraShortestPath<Vertex, IQuadEdge> shortestPaths = new DijkstraShortestPath<>(graph);
Vertex originVertex = tin.getNavigator().getNearestVertex(originX, originY);
var paths = shortestPaths.getPaths(originVertex);
IncrementalTin distanceMesh = new IncrementalTin();
for (Vertex v : graph.vertexSet()) {
var d = paths.getWeight(v);
distanceMesh.add(new Vertex(v.x, v.y, d)); // add vertices with Z to new tin
}
IInterpolatorOverTin interpolator = new NaturalNeighborInterpolator(distanceMesh);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
double z = interpolator.interpolate(x, y, null);
if (!Double.isNaN(z)) {
pixels[y * width + x] = someColour;
}
}
}
Update: Distance Boundary Vertices
If you want merely the distance boundary line, you can forgo step 5. Instead, compute (if applicable) the isoline for each triangle, based on the desired distance. If an isoline crosses a triangle (as in the illustration below), it will intersect with two of the triangle's edges — plotting a line segment between each pair of intersecting points for every such triangle give you a distance boundary.
Call a method (such as below) for each edge for every constrained triangle in the triangulation. If the distance isoline crosses the triangle, you'll get two points of intersection for that triangle; otherwise none.
/**
* Compute isoline vertex (if applicable) for a triangle side given by two vertices
*/
Vertex isoVertex(Vertex a, Vertex b, double d) {
Vertex min, max;
if (a.getZ() > b.getZ()) {
max = a;
min = b;
} else {
max = b;
min = a;
}
if (d > min.getZ() && d < max.getZ()) {
double diff = max.getZ() - min.getZ();
double numerator = d - min.getZ();
double fract = numerator / diff;
double xDiff = max.getX() - min.getX();
double yDiff = max.getY() - min.getY();
return new Vertex(min.getX() + fract * xDiff, min.getY() + fract * yDiff);
}
return null;
}

Related

How to get a fixed number of coordinates between two points polyline in Javascript

I'm looking for a way to find multiple coordinates between 2 points in Javascript (I don't need distance or mid point).
Say I have these 2 points:
Point A (left) lat: 39.091868 long: -9.263187
Point B (right) lat:
39.089815 long: -9.261857
I want to generate for instance 20 coordinates between points A and B.
I tried using this piece of Javacript code (Found on an old thread):
Point = function(x, y) {
this.x = x;
this.y = y;
}
var pointA = new Point(39.091868, -9.263187);
var pointB = new Point(39.089815, -9.261857);
var numberOfPoints = 20;
var points = new Array();
for (var i = 0; i < numberOfPoints; i++) {
points.push(new Point((Math.abs(pointA.x - pointB.x) / 10) * i + pointB.y, (Math.abs(pointA.y - pointB.y) / numberOfPoints) * i + pointB.y));
}
console.log(points);
When I run the code above, it prints out 20 values of the points array between point A and B. However, it only seems to store the correct longitude values of the points. (x and y both show longitude values).
For example, the first item of the points array is:
Point x: -9.261857 y: -9.261857
How should I adjust the calculation inside the for loop so that each point between point A and B also stores a correct latitude value? Or does anyone know another way to calculate a fixed number of coordinates between 2 points in Javascript?
Any kind of help is appreciated
Because the earth is not flat, the calculation can be good for small distance.
Just calculate the point.
1 Degree is approximal 110 km. so you may do just Pitagoras calculation for small distance and multiply by 110 km, and do iterative loop.
For accuracy you may use open-layers library (most of it is free), but you should provide map with projection. Make things a bit complicated.
One of the function is find the coordinate of a specific point, give distance and azimuth.
Need to find the the azimuth first of two points.
Use O/L: openlayers sphere getlength & bearing.
O/L distance
Open layers

How to get the angle of a 3d plane using the position of the 4 corners

About 6 months ago i started making a 3d graphics engine.
Its already looking very good. I already implemented rotation, translation, scaling, Z-buffer(painter's algoritm),... Im now working on a specular shader. For that i need some way to get the angle of he individual faces
My question is, how do i get the angle of a plane by only knowing the position of the four corners?
Here is what i got so far:
function faceAngle(verts,faces){
var arr = [];
for(var i=0;i<faces.length;i++){
var posA = verts[faces[i][0]];//the four corners
var posB = verts[faces[i][1]];// A B
var posC = verts[faces[i][2]];// -----
var posD = verts[faces[i][3]];// | |
// | |
var ar = []; // -----
ar.push(/*some Maths*/);//x // D C
ar.push(/*some Maths*/);//y
ar.push(/*some Maths*/);//z
arr.push(ar);
}
return arr;
}
Orientation of plane in the space is defined by normal vector. To get this vector, calculate cross product of two edges (belonging to the plane). So you need only three non-collinear points in the plane.
n = (posB - posA) x (posC - posA) //cross product of two vectors
Note that components of normalized (unit) normal vector are direction cosines

Draw a perpendicular line through middle of two long/lat points

Hello i'm currently trying to draw a line through two long/lat lines to create a triangle. So far i have manged to draw a line through but the line is not perpendicular and looks skewed. Here is my code:
startPosition = [-0.17640, 51.426700];
endPosition = [0.17640, 51.796700];
triangleSizeY = (endPosition [1] - startPosition[1]) / 6;
/*subtract
end from start to work out direction and also use this divided by 6 to
get distance*/
triangleSize *= -1;
triangleSizeX = (endPosition [0] - startPosition[0]) / 6;
/*subtract
end from start to work out direction and also use this divided by 6 to
get distance*/
triangleSize *= -1;
var cx = (startPosition[0] + endPosition[0]) / 2;
var cy = (startPosition[1] + endPosition[1]) / 2;
var dx = (endPosition[0] - startPosition[0]) / 2;
var dy = (endPosition[1] - startPosition[1]) / 2;
positions[0] = [midPoint[0] + triangleSizeX, midPoint[1] +
triangleSizeY];
positions[1] = [cx - dy, cy + dx];
positions[2] = [cx + dy, cy - dx];
This is what it looks like:
First, lat/lon are angular so you can not do linear type distances. The steps you need to take to solve this:
Compute the distance between the 2 lat/lon pairs you want a perpendicular line from.
Take half the distance computed from the above step to get you the midpoint range.
Calculate the bearing between the 2 lat/lon pairs. (see reference below on computing a bearing from 2 lat/lon's)
With the half distance and bearing, you can compute the lat/lon of the midpoint. This is called computing a lat/lon based on a range and bearing. (See the reference below.)
Now you can go perpendicular from the midpoint by adding/subtracting 90 degrees from the bearing in step 3. Decide on a range you want to compute the new lat/lon from a range/bearing like in step 4.
This site (https://www.movable-type.co.uk/scripts/latlong.html) has the calculations you need to do this. Also, since the distance is relatively small, you can use the Equirectangular approximation over Haversine for distance calculation.

Incorrect angle, wrong side calculated

I need to calculate the angle between 3 points. For this, I do the following:
Grab the 3 points (previous, current and next, it's within a loop)
Calculate the distance between the points with Pythagoras
Calculate the angle using Math.acos
This seems to work fine for shapes without angels of over 180 degrees, however if a shape has such an corner it calculates the short-side. Here's an illustration to show what I mean (the red values are wrong):
This is the code that does the calculations:
// Pythagoras for calculating distance between two points (2D)
pointDistance = function (p1x, p1y, p2x, p2y) {
return Math.sqrt((p1x - p2x)*(p1x - p2x) + (p1y - p2y)*(p1y - p2y));
};
// Get the distance between the previous, current and next points
// vprev, vcur and vnext are objects that look like this:
// { x:float, y:float, z:float }
lcn = pointDistance(vcur.x, vcur.z, vnext.x, vnext.z);
lnp = pointDistance(vnext.x, vnext.z, vprev.x, vprev.z);
lpc = pointDistance(vprev.x, vprev.z, vcur.x, vcur.z);
// Calculate and print the angle
Math.acos((lcn*lcn + lpc*lpc - lnp*lnp)/(2*lcn*lpc))*180/Math.PI
Is there something wrong in the code, did I forget to do something, or should it be done a completely different way?
HI there your math and calculations are perfect. Your running into the same problem most people do on calculators, which is orientation. What I would do is find out if the point lies to the left or right of the vector made by the first two points using this code, which I found from
Determine which side of a line a point lies
isLeft = function(ax,ay,bx,by,cx,cy){
return ((bx - ax)*(cy - ay) - (by - ay)*(cx - ax)) > 0;
}
Where ax and ay make up your first point bx by your second and cx cy your third.
if it is to the left just add 180 to your angle
I've got a working but not necessarily brief example of how this can work:
var point1x = 0, point1y = 0,
point2x = 10, point2y = 10,
point3x = 20, point3y = 10,
point4x = 10, point4y = 20;
var slope1 = Math.atan2(point2y-point1y,point2x-point1x)*180/Math.PI;
var slope2 = Math.atan2(point3y-point2y,point3x-point2x)*180/Math.PI;
var slope3 = Math.atan2(point4y-point3y,point4x-point3x)*180/Math.PI;
alert(slope1);
alert(slope2);
alert(slope3);
var Angle1 = slope1-slope2;
var Angle2 = slope2-slope3;
alert(180-Angle1);
alert(180-Angle2);
(see http://jsfiddle.net/ZUESt/1/)
To explain the multiple steps the slopeN variables are the slopes of the individual line segments. AngleN is the amount turned at each junction (ie point N+1). A positive angle is a right turn and a negative angle a left turn.
You can then subtract this angle from 180 to get the actual interior angle that you want.
It should be noted that this code can of course be compressed and that five lines are merely outputting variables to see what is going on. I'll let you worry about optimizing it for your own use with this being a proof of concept.
You need to check boundary conditions (apparently, if points are colinear) and apply the proper calculation to find the angle.
Also, a triangle can't have any (interior) angle greater than 180 degress. Sum of angle of triangle is 180 degrees.

Space filling with circles of unequal size

Here is my problem:
I have a bunch of circles that I need to display inside a canvas.
There are an arbitrary number of circles, each with a predefined radius.
The summed area of circles is always smaller than the area of the canvas.
I want to position the circles so that they take up the maximal space available inside the canvas, without touching each other. My goal is to achieve a visually pleasing effect where the circles appear well distributed inside the canvas. I don't know if this is really "space filling", as my goal is not to minimize the distance between elements, but rather to maximize it.
Here is an example of what I am trying to achieve:
My first "brute force" idea was the following:
For each circle: calculate the shortest distance between its border and each other circle's border; sum all of these distances, call that X.
Calculate the sum of all X's.
Randomly change the distances between the circles.
Redo 1-3 for a preset number of iterations and take the maximal value obtained at step (2).
However, this does not seem elegant; I'm sure there is a better way to do it. Is there any existing algorithm to achieve such a layout? Is there any existing library that I could use (JavaScript or Ruby) to achieve this?
Edit
Here is a Javascript version of the accepted answer, which uses Raphael to draw the circles.
I would try to insert sphere after sphere (largest first). Each one is added in the largest available space, with some random jitter.
One relatively easy way to find (more or less) the largest available space, is to imagine a grid of points on your view and store for each grid point (in a 2D array) the closest distance to any item: edge or sphere, whichever is closest. This array is updated as each new sphere is added.
To add a new sphere, just take the grid point with highest distance and apply some random jitter (you actually know how much you can jitter, because you know the distance to the closest item). (I would randomize not more than (d-r)/2 where d is the distance in the array and r is the radius of the sphere to add.
Updating this array after adding another circle is no rocket science: you calculate for each grid point the distance to newly added sphere and replace the stored value if that was larger.
It is possible that your grid is too coarse, and you can't add any more circle (when the 2D array contains no distances larger than the radius of the circle to add). Then you have to increase (e.g. double) the grid resolution before continuing.
Here are some result of this implementation (it took me about 100 lines of code)
100 Circles of varying size
500 Circles of varying size
100 Circles of same size
And here is some rough C++ code (just the algorithm, don't expect this to compile)
// INITIALIZATION
// Dimension of canvas
float width = 768;
float height = 1004;
// The algorithm creates a grid on the canvas
float gridSize=10;
int gridColumns, gridRows;
float *dist;
void initDistances()
{
// Determine grid dimensions and allocate array
gridColumns = width/gridSize;
gridRows = height/gridSize;
// We store a 2D array as a 1D array:
dist = new float[ gridColumns * gridRows ];
// Init dist array with shortest distances to the edges
float y = gridSize/2.0;
for (int row=0; row<gridRows; row++)
{
float distanceFromTop = y;
float distanceFromBottom = height-y;
for (int col=0; col<gridColumns; col++)
{
int i = row*gridColumns+col;
dist[i]=(distanceFromTop<distanceFromBottom?distanceFromTop:distanceFromBottom);
}
y+=gridSize;
}
float x = gridSize/2.0;
for (int col=0; col<gridColumns; col++)
{
float distanceFromLeft = x;
float distanceFromRight = width-x;
for (int row=0; row<gridRows; row++)
{
int i = row*gridColumns+col;
if (dist[i]>distanceFromLeft) dist[i] = distanceFromLeft;
if (dist[i]>distanceFromRight) dist[i] = distanceFromRight;
}
x+=gridSize;
}
}
void drawCircles()
{
for (int circle = 0; circle<getNrOfCircles(); circle++)
{
// We assume circles are sorted large to small!
float radius = getRadiusOfCircle( circle );
// Find gridpoint with largest distance from anything
int i=0;
int maxR = 0;
int maxC = 0;
float maxDist = dist[0];
for (int r=0; r<gridRows; r++)
for (int c=0; c<gridColumns; c++)
{
if (maxDist<dist[i]) {
maxR= r; maxC= c; maxDist = dist[i];
}
i++;
}
// Calculate position of grid point
float x = gridSize/2.0 + maxC*gridSize;
float y = gridSize/2.0 + maxR*gridSize;
// Apply some random Jitter
float offset = (maxDist-radius)/2.0;
x += (rand()/(float)RAND_MAX - 0.5) * 2 * offset;
y += (rand()/(float)RAND_MAX - 0.5) * 2 * offset;
drawCircle(x,y,radius);
// Update Distance array with new circle;
i=0;
float yy = gridSize/2.0;
for (int r=0; r<gridRows; r++)
{
float xx = gridSize/2.0;
for (int c=0; c<gridColumns; c++)
{
float d2 = (xx-x)*(xx-x)+(yy-y)*(yy-y);
// Naive implementation
// float d = sqrt(d2) - radius;
// if (dist[i]>d) dist[i] = d;
// Optimized implementation (no unnecessary sqrt)
float prev2 = dist[i]+radius;
prev2 *= prev2;
if (prev2 > d2)
{
float d = sqrt(d2) - radius;
if (dist[i]>d) dist[i] = d;
}
xx += gridSize;
i++;
}
yy += gridSize;
}
}
}
Perhaps some application of force-directed layout would be useful.
Since your goal is just to "achieve a pleasing effect", not solve a math problem, you should try the simplest algorithm which could work first and see if it looks good. There should be no need to use very complex math.
I understand that you want the spheres to "fill" the available space, not leaving large empty areas while other areas are crowded. You also want the layout to appear random -- not lined up on a grid or anything like that.
The obvious, dead-simple way to achieve that is simply to place the spheres one by one, in random locations. If one lands on top of an already-placed sphere, generate another random location until you find one where it fits.
It appears there are about 40 spheres in the image shown. The chances of 40 spheres all landing in the same area of the image, leaving the rest of the image empty, is very, very small. As the number of spheres increases, the chances of getting a very unbalanced layout will become close to zero.
Try that first and see if it meets your needs. If it is not "even" enough, you should be able to use some very simple math to bias the randomly chosen locations in favor of choosing empty areas. There should be no need to use complex algorithms.

Categories

Resources