Iterate each point within a polygon - javascript

Assuming I have the vertices of some polygon in a grid environment, how can I iterate through each cell it contains (including those on the edge)?
To clarify, I have the following vertices (counted as if the topleft is (0, 0)):
//each point is [x, y]
var verts = [
[1, 1],
[3, 1],
[3, 2],
[4, 2],
[4, 4],
[0, 4],
[0, 3],
[1, 3]
];
Which would define a polygon such as this:
Where each green dot is a point I would like to iterate, based on the vertices above. There is no pattern to the direction the vertices will walk along the edge of the polygon, it could go clockwise or counter-clockwise around the polygon. However, they will be in order; that is, if you put down your pen and move to each vertex in order, without lifting up, and it would draw the outline without crossing inside the polygon.
The use case being I have the imageData from a PNG loaded via the canvas API. This PNG is split into "zones", and I need to iterate each pixel of the current "zone". Each "zone" is defined by a vertex array like above.
I tried something like the following, which will create a square to iterate through for each set of 4 vertices in the array.
for(var v = 0, vl = verts.length - 4; v < vl; ++v) {
//grabbing the minimum X, Y and maximum X, Y to define a square to iterate in
var minX = Math.min(verts[v][0], verts[v + 1][0], verts[v + 2][0], verts[v + 3][0]),
minY = Math.min(verts[v][1], verts[v + 1][1], verts[v + 2][1], verts[v + 3][1]),
maxX = Math.max(verts[v][0], verts[v + 1][0], verts[v + 2][0], verts[v + 3][0]),
maxY = Math.min(verts[v][1], verts[v + 1][1], verts[v + 2][1], verts[v + 3][1]);
for(var x = minX; x < maxX; ++x) {
for(var y = minY; y < maxY; ++y) {
//do my checks on this pixel located at X, Y in the PNG
}
}
}
Two big problems with that though:
It can repeat points within the polygon, and
It can grab points outside the polygon
I can solve the first issue by tracking which points I check, so I don't repeat a check. The second can only be solved by running a PointInPoly check on each point, which would make this solution much heavier than I want it to be.
EDIT
Iterating each pixel in the entire image and applying a PointInPoly check to each is also unacceptable; it would be even slower than the above algorithm.

If your polygons are convex, you can do the following:
Create a line for each edge of the polygon denoting one side inside and one side outside (this is based on the normal, which can be dependent on the winding direction)
For every pixel inside the bounding box that you already calculated, check to see if the pixel is on the in-side of the line. If the pixel is on the out-side of any of the lines, then it is outside the polygon. If it is inside all of them, then it is inside.
The basic algorithm is from here: https://github.com/thegrandpoobah/voronoi/blob/master/stippler/stippler.cpp#L187
If your polygons are not convex, then what I would do is to actually draw the polygon on the canvas in a known colour, and then apply the iterative flood fill algorithm. That requires that you know at least one pixel which is on the inside, but that shouldn't be an expensive test. But this may not be suitable in JavaScript if you can't do it in an offscreen buffer (not familiar with the canvas tag).

Related

Find area within set distance from point in a complicated polygon

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;
}

Spline offset in three.js

I have to make 3d text from font glyphs. Yes, I know that I can use TextGeometry, but I need to draw this manually because I need to do offset on font splines.
At this moment I have splines with their points and I can draw letters.
From points I know: previousPoint, currentPoint and nextPoint and I need to compute bisector between previous and next points and I have no idea to do that.
Or if is another way to move spline points outer of initial position to do offset.
My idea:
Thank you!
EDIT:
With yours answers I obtained correct values for each splines from font, but only at 'o' and '0' I have a problem.
This method draw a weird tangent in bottom of the letter and I don't know to resolve this problem..
here is the result
Anybody know how to resolve this?
EDIT 2:
Finally I finished my project. And this is the final product ( .stl exporter )
final offset
Thank you for yours answers!
There are the result from: x = (prev_x + next_x) / 2 and y = (prev_y + next_y) / 2
wrong result
desired result
Here is my code where let points is all the points from the path:
getPathPoints(path) {
let points = path.getPoints();
console.log(points)
for (let i = 0; i < points.length; i++) {
let A = points.extended(i - 1); // previousPoint => where extends is a custom array prototype
let B = points.extended(i); // currentPoint
let C = points.extended(i + 1); // nextPoint
let x = (A.x + C.x) / 2;
let y = (A.y + C.y) / 2;
let bisector = new THREE.Vector2(x,y);
console.log(bisector);
}
}
What splines describe your glyphs?
I know that TTF fonts use quadratic Bezier curves. For Bezier direction vector in starting and ending points has direction onto control point. So difference
S = ControlPoint[1] - ControlPoint[0]
represents direction in the starting point, difference
E = ControlPoint[1] - ControlPoint[2]
represents direction in the ending point.
Normalize these vectors for two neighbour curves and add them - now you have bisector vector.
Bisector = E(i).Normalized + S(i+1).Normalized

D3.js Convex Hull With 2 Data Points

The API for Hull Geom states: "Assumes the vertices array is greater than three in length. If vertices is of length <= 3, returns []." (https://github.com/mbostock/d3/wiki/Hull-Geom)
I need to draw convex hulls around 2 nodes. I am using the force layout, so the convex hull needs to be dynamic in that it moves around the nodes if I click a node and drag it around. My code is currently based off of this example: http://bl.ocks.org/donaldh/2920551
For context, this is what I am trying to draw a convex hull around:
Here it works when there are 3 nodes:
Here is what I am trying to draw a convex hull around (doesn't work with the code from the example above because Hull Geom will only take arrays with 3+ vertices):
I understand the traditional use of a convex hull would never involve only two points, but I have tried drawing ellipses, rectangles, etc around the 2 nodes and it doesn't look anywhere near as good as the 3 nodes does.
I understand that Hull Geom ultimately just spits out a string that is used for pathing, so I could probably write a modified version of Hull Geom for 2 nodes.
Any suggestions on how to write a modified Hull Geom for 2 nodes or any general advice to solve my problem is really appreciated.
Basically, you need to at least one fake point very close to the line to achieve the desired result. This can be achieved in the groupPath function.
For d of length 2 you can create a temporary array and attach it to the result of the map function as follows:
var groupPath = function(d) {
var fakePoints = [];
if (d.values.length == 2)
{
//[dx, dy] is the direction vector of the line
var dx = d.values[1].x - d.values[0].x;
var dy = d.values[1].y - d.values[0].y;
//scale it to something very small
dx *= 0.00001; dy *= 0.00001;
//orthogonal directions to a 2D vector [dx, dy] are [dy, -dx] and [-dy, dx]
//take the midpoint [mx, my] of the line and translate it in both directions
var mx = (d.values[0].x + d.values[1].x) * 0.5;
var my = (d.values[0].y + d.values[1].y) * 0.5;
fakePoints = [ [mx + dy, my - dx],
[mx - dy, my + dx]];
//the two additional points will be sufficient for the convex hull algorithm
}
//do not forget to append the fakePoints to the input data
return "M" +
d3.geom.hull(d.values.map(function(i) { return [i.x, i.y]; })
.concat(fakePoints))
.join("L")
+ "Z";
}
Here a fiddle with a working example.
Isolin has a great solution, but it can be simplified. Instead of making the virtual point on the line at the midpoint, it's enough to add the fake points basically on top of an existing point...offset by an imperceptible amount. I adapted Isolin's code to also handle cases of groups with 1 or 2 nodes.
var groupPath = function(d) {
var fakePoints = [];
if (d.length == 1 || d.length == 2) {
fakePoints = [ [d[0].x + 0.001, d[0].y - 0.001],
[d[0].x - 0.001, d[0].y + 0.001],
[d[0].x - 0.001, d[0].y + 0.001]]; }
return "M" + d3.geom.hull(d.map(function(i) { return [i.x, i.y]; })
.concat(fakePoints)) //do not forget to append the fakePoints to the group data
.join("L") + "Z";
};

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.

Sub pixel antialiasing canvas pixel shift algorithms in javascript

I'm trying to work out a way of doing pseudo 3d, distorting textures with the javascript canvas.
The best method for my needs so far has been to use displacement maps which I'm largely following from this tutorial and source code example.
The basic principle is to use the channel level (RGBA) from a selected pixel in the displacement map then applying a pixel shifting algorithm... all good so far.
The problem is that this method of shifting the texture image's pixels is very binary and renders a slightly 'jagged' edge due to the fact that - it's simply shifting 'full' pixels.
When compared to PhotoShop or some of the ImageMagick examples here the javascript method looks much less realistic. This is due to PS & IMs sub-pixel processing abilities whereby medians can be derived for inter-pixel data.
Question: Can anyone suggest a step which can be integrated into my algorithm to produce a gaussian/aliased smoothness to the output?
Perhaps I can simply run the imagedata through an FFT and back again? are there any examples of this in action?
I'm a little stumped and would very much appreciate some pointers.
1) You are mentionning two very differents algorithms : Displacement mapping is a 3D technique, so it involves 'Z', and projection, and the other one is a 2D pixel shifting algorithm way way simpler.
(The soundstep link provided uses the word 'displacement mapping', yet it is a pixel shifting
technique.)
2) Whatever the size of your MVC project, the algorithm should be isolated, and have a signature like :
var pixelShift = function (sourceCanvas, shiftCanvas, xOffset, yOffset)
and either return a new canvas OR change sourceCanvas in place.
If there's no such function, please do not talk about MVC, unless the M stands for 'Mess'. ;-) -
3) The algorithm is quite simple in fact, you must iterate through the destination pixel and look the color of the pixel they should come from (and not the other way around) :
var pixelShift = function (sourceCanvas, shiftCanvas, xOffset, yOffset) {
var shiftXY = { xS:0, yS:0 };
var shiftCanvasWidth = shiftCanvas.width ;
var shiftCanvasHeight = shiftCanvas.height;
for ( var x=0 ; x < shiftCanvasWidth ; x ++) {
for ( var y = 0 ; y < shiftCanvasHeight ; y++ ) {
readShift ( shiftCanvas, x, y, shiftXY );
var sourceColor = readPixelColor ( sourceCanvas,
xOffset + shiftXY.xS,
yOffset + shiftXY.yS) ;
writePixel(sourceCanvas, xOffset + x , yOffset + y, sourceColor );
}
}
};
// sourceColor represents the color within a 32 bits integer (r,g,b,a * 8 bits).
it would be too long to write everything here but :
-- within pixelShift loop you should not deal with the source canvas, but a with 32 bits performance array.
-- the shift canvas should be converted ONCE into a Int8Array array, and stored as this.
this array size is shiftWidth * shiftHeight
odd index contains x shift, even contains y shift.
the array is pre-processed, and contains the shift value - 128.
for this shiftArray :
shiftX (x,y) = shiftArray [ 2 * (x + y * shiftWidth) ] ;
shiftY (x,y) = shiftArray [ 2 * (x + y * shiftWidth) + 1 ] ;
-- So pixelShift should look like :
var pixelShift = function (sourceCanvas,
shiftArray, shiftWidth, shiftHeight, xOffset, yOffset) {
[ Get a 32 bit performance array out of the canvas's target area ]
[ process this array using the shiftArray ]
[ write back the processed array onto the canvas's target area ]
}
-- And the core loop can be processed in a linear fashion :
var areaSize = shiftWidth * shiftHeight ;
for ( pixelIndex=0 ; pixelIndex < areaSize ; pixelIndex++ ) {
var linearShift = shiftArray [ 2*pixelIndex ]
+ shiftWidth * shiftArray [ 2*pixelIndex + 1 ] ;
targetAreaArray [ pixelIndex ] = targetAreaArray [ pixelIndex + linearShift ] ;
}
-- Rq : you might want to perform a boundary check on (pixelIndex + linearShift) within [0, areaSize[.
-- I think now you cannot get any faster.
The performance bottleneck will be the getImageData and putImageData you need to get/put the target area, but as far as i know, there's no other way to get a binary view on a Canvas than those two slooooow functions.

Categories

Resources