outline around group of hexagons - javascript

I have a group of hexagons in my hexagonal grid, which I'd like outline with a border.
This is how it looks like so far:
desired output:
I have stored all the cotrner points of each hexagon from that group. I suppose these points can be used to calculate the border around the group later on.
[[{"x":123.39745962155612,"y":260},{"x":101.74682452694516,"y":272.5},{"x":80.0961894323342,"y":260},{"x":80.0961894323342,"y":235},{"x":101.74682452694516,"y":222.5},{"x":123.39745962155612,"y":235}],[{"x":145.0480947161671,"y":222.5},{"x":123.39745962155614,"y":235},{"x":101.74682452694518,"y":222.5},{"x":101.74682452694518,"y":197.5},{"x":123.39745962155614,"y":185},{"x":145.0480947161671,"y":197.5}],[{"x":166.69872981077808,"y":260},{"x":145.0480947161671,"y":272.5},{"x":123.39745962155612,"y":260},{"x":123.39745962155612,"y":235},{"x":145.0480947161671,"y":222.5},{"x":166.69872981077805,"y":235}],[{"x":188.34936490538905,"y":297.5},{"x":166.69872981077808,"y":310},{"x":145.0480947161671,"y":297.5},{"x":145.0480947161671,"y":272.5},{"x":166.69872981077808,"y":260},{"x":188.34936490538902,"y":272.5}],[{"x":188.34936490538905,"y":222.5},{"x":166.69872981077808,"y":235},{"x":145.0480947161671,"y":222.5},{"x":145.0480947161671,"y":197.5},{"x":166.69872981077808,"y":185},{"x":188.34936490538902,"y":197.5}],[{"x":210,"y":260},{"x":188.34936490538902,"y":272.5},{"x":166.69872981077805,"y":260},{"x":166.69872981077805,"y":235},{"x":188.34936490538902,"y":222.5},{"x":209.99999999999997,"y":235}],[{"x":231.65063509461098,"y":297.5},{"x":210,"y":310},{"x":188.34936490538902,"y":297.5},{"x":188.34936490538902,"y":272.5},{"x":210,"y":260},{"x":231.65063509461095,"y":272.5}]]
Also added a live example to see how the grid works so far.
And now I'm trying to figure out how exactly do I determine how the border will be generated.
Now how do I exactly generate the border?
Should I just find the center of this group, and then
find all the points with the greater distance, and
then connect them?
As advised by someone in discussion below, I tried sorting the points by their [X,Y] coordinates and also removed all duplicate coordinates, but it turned into this disaster :/.
Or is there any other technique to achieve this?

Here's a simple algorithm:
For each hex H in your brown region, iterate over each of the 6 directions 0 ≤ dir < 6, and set D to be hex_neighbor(H, dir).
If D is green then draw a border between the two.
Caveat: this does not connect the border edges into a single polyline.
I have a live demo with an algorithm that works on any number of regions, as well as an extension to draw curved lines.

Thanks Amit, for sharing your knowledge with us. Your algorithm is way more efficient and faster. I have had to make very little amendments to make it work in my example above.
First one, if the hex didn't have a neighbor on one of its direction , mark the direction with null.
The second, take the neighbors with null value to consideration while generating edges.
Otherwise it was leaving gaps if the hex didn't have neighbor on one of their sides.
This a modified function that I'm using to draw the edges:
generateEdges: function( ctx ){
var edges = [];
var self = this;
var neighbor = null;
self.obs_arr.forEach( function( hex ){
for( var dir = 0, nb_l = hex.neighbors.length; dir < nb_l; dir++ ){
neighbor = hex.neighbors[dir];
if(neighbor == null || hex.content != neighbor.content ){
var p1 = self.point_add( hex.center, self.hex_corner_offset( dir ) );
var p2 = self.point_add( hex.center, self.hex_corner_offset( (dir+1)%6 ) );
edges.push( p1, p2 );
}
}
});
return edges;
},
It all now works perfectly . Thanks a lot Amit for all your help!

Related

How to make sure that a shape has straight corners

i'm looking for a way to draw a shape on an image, get the coordinates, then make minor corrections to have a perfect square or diamond.
I have a floor plan and i'm trying to highlight certain areas. Since i'm drawing over the image, i can't always get it a perfect shape and manual corrections takes time.
I would like to draw over the image, then pass it to a functiton which makes sure that each angles are 90 degrees, or it fixes the coordinates. The shape is always gonna be a square or a rectangle. All I want are the corrected coordinates so they can be saved in a file.
Here's a sample of the image i'm working with, along with one location:
var loc6 = [{x:711, y:596}, {x:787, y:595}, {x:784, y:641}, {x:711, y:641}];
https://imagebin.ca/v/2zjcnTJCRL2n
Math isn't my biggest asset, i've got the function below, but it only works to make a perfect square on squares, and messes up diamonds.
function fixRectangle(coords){
var precision = 8;
if(coords[0].y - coords[1].y < precision){
coords[0].y = coords[1].y;
}
if(coords[1].x - coords[0].x < precision){
coords[0].x = coords[1].x;
}
if(coords[2].y - coords[3].y < precision){
coords[2].y = coords[3].y;
}
if(coords[2].x - coords[3].x < precision){
coords[2].x = coords[3].x;
}
return coords;
}
(I do use jquery in case it is required)
Thanks for your help !

Finding the center of Leaflet polygon?

I have a bunch of leaflet polygons on a map I created. Each polygon represents something different. A specific set of information is displayed in a popup depending on the page the user is on. I need to find a way to make the "popup" bubble open in the center of the polygon it represents.
Each polygon is drawn using the following code:
var L20 = [
[74.0995, -99.92615],
[74.14008, -99.4043],
[74.07691, -99.33838],
[74.03617, -99.86023]
];
var L19 = [
[74.02559, -99.84924],
[74.06636, -99.32739],
[74.0029, -99.26147],
[73.96197, -99.77783]
];
var L18 = [
[73.95142, -99.76684],
[73.99235, -99.25048],
[73.92889, -99.18456],
[73.8878, -99.69543]
];
var set1 = L.polygon([L20, L19, L18], {
color: "#fff",
weight: 1,
stroke: true,
opacity: 0.05,
fillColor: "#346B1F",
}).addTo(map);
The popup is drawn using the following code:
var popup = L.popup({})
.setLatLng([73.64017, -100.32715])
.setContent(content).openOn(map);
var popup = L.popup();
So I need to find a way for .setLatLang to determin or be given the center of the polygon.
I came up with 3 solutions that may work, not sure how to go about it.
find a way to use the coordinates of a polygon to determine the center of the polygon where the popup will open.
call one point of the polygon, then offset the position of the popup.
Use an id for each polygon, so each popup knows the box area (polygon) it can be opened in.
Can someone help me please?
Since some time Leaflet has built-in getCenter() method:
polygon.getBounds().getCenter();
There are a few ways to approximate the centroid of a polygon.
The easiest (but least accurate method) is to get the center of the bounding box that contains the polygon, as yarl suggested, using polygon.getBounds().getCenter();
I originally answered the question with the formula for finding the centroid of the points, which can be found by averaging the coordinates of its vertices.
var getCentroid = function (arr) {
return arr.reduce(function (x,y) {
return [x[0] + y[0]/arr.length, x[1] + y[1]/arr.length]
}, [0,0])
}
centerL20 = getCentroid(L20);
While the centroid of the points is a close enough approximation to trick me, a commenter pointed out that it is not the centroid of the polygon.
An implementation based on the formula for a centroid of a non-self-intersecting closed polygon gives the correct result:
var getCentroid2 = function (arr) {
var twoTimesSignedArea = 0;
var cxTimes6SignedArea = 0;
var cyTimes6SignedArea = 0;
var length = arr.length
var x = function (i) { return arr[i % length][0] };
var y = function (i) { return arr[i % length][1] };
for ( var i = 0; i < arr.length; i++) {
var twoSA = x(i)*y(i+1) - x(i+1)*y(i);
twoTimesSignedArea += twoSA;
cxTimes6SignedArea += (x(i) + x(i+1)) * twoSA;
cyTimes6SignedArea += (y(i) + y(i+1)) * twoSA;
}
var sixSignedArea = 3 * twoTimesSignedArea;
return [ cxTimes6SignedArea / sixSignedArea, cyTimes6SignedArea / sixSignedArea];
}
The problem you are trying to solve is called the pole of inaccessibility problem. Finding the best place to put a label in a polygon isn't completely solved by finding the center of the bounding box. Consider a polygon in the shape of the letter U. The center of the bounding box puts the label outside of the polygon. It took me forever to find this outstanding library: https://github.com/mapbox/polylabel
From the README.MD:
A fast algorithm for finding polygon pole of inaccessibility, the most distant internal point from the polygon outline (not to be confused with centroid), implemented as a JavaScript library. Useful for optimal placement of a text label on a polygon.
It's an iterative grid algorithm, inspired by paper by Garcia-Castellanos & Lombardo, 2007. Unlike the one in the paper, this algorithm:
guarantees finding global optimum within the given precision
is many times faster (10-40x)
Usage:
Given polygon coordinates in GeoJSON-like format and precision (1.0 by default), Polylabel returns the pole of inaccessibility coordinate in [x, y] format.
var p = polylabel(polygon, 1.0);
How the algorithm works:
This is an iterative grid-based algorithm, which starts by covering the polygon with big square cells and then iteratively splitting them in the order of the most promising ones, while aggressively pruning uninteresting cells.
Generate initial square cells that fully cover the polygon (with cell size equal to either width or height, whichever is lower). Calculate distance from the center of each cell to the outer polygon, using negative value if the point is outside the polygon (detected by ray-casting).
Put the cells into a priority queue sorted by the maximum potential distance from a point inside a cell, defined as a sum of the distance from the center and the cell radius (equal to cell_size * sqrt(2) / 2).
Calculate the distance from the centroid of the polygon and pick it as the first "best so far".
Pull out cells from the priority queue one by one. If a cell's distance is better than the current best, save it as such. Then, if the cell potentially contains a better solution that the current best (cell_max - best_dist > precision), split it into 4 children cells and put them in the queue.
Stop the algorithm when we have exhausted the queue and return the best cell's center as the pole of inaccessibility. It will be guaranteed to be a global optimum within the given precision.
assuming each polygon has only 4 sides it is simple
var L20 = [
[74.0995, -99.92615],
[74.14008, -99.4043],
[74.07691, -99.33838],
[74.03617, -99.86023]
];
using this example get max and min lat: 74.03617 and 74.14008 respectively so same for long: -99.92615 and 99.33838 respectively
Then get the middle value for each: (max - min) / 2 = 0.051955 and -0.293885 then add them to the minimum amount
gives you a centre of 74.088125, -99.632265
To move a polygon into view and center it use:
map.fitBounds(poly.getBounds())
This will also set the zoom level correctly.

Get coordinates of line-surrounding box

I've been working in JavaScript to code a line drawing system. I'd like the lines drawn to be selectable, so I've been attempting to implement line-highlighting. As you can see in the image below, I have a line (in black) with known coordinates and an equation in slope-intercept (y=mx+b). How can I calculate the corners' (circled in green) coordinates, knowing the box's radius?
This is easiest to think of in terms of vectors.
Start off by defining the point at the end of the line as A, and the other end as B
var A = new Vector(1, 1)
var B = new Vector(5, 3)
Now find the unit direction vector of the line (a vector of length 1 pointing from A to B), and its perpendicular:
var dir = B.minus(A).normalize();
var dir_perp = new Vector(dir.y, -dir.x)
And extend them to be of length thickness:
dir = dir.times(thickness);
dir_perp = dir_perp.times(thickness)
The four corners are then:
[
A.minus(dir).plus(dir_perp),
A.minus(dir).minus(dir_perp),
B.plus(dir).minus(dir_perp),
B.plus(dir).plus(dir_perp)
]
This obviously assumes you have some sort of vector math library. Here's one I made earlier

How to Draw line in 3D rectangle based on x,y and z?

I would like draw 3D points represented in image to 3D rectangle. Any idea how could I represent these in x,y and z axis
Here projection type is orthographic.
Thanks
Okay. Let's look at a simple example of what you are trying to accomplish it, and why this is such a complicated problem.
First, lets look a some projection functions. You need a way to mathematically describe how to transform a 3D (or higher dimensional) point into a 2D space (your monitor), or a projection.
The simpiest to understand is a very simple dimetric projection. Something like:
x' = x + z/2;
y' = y + z/4;
What does this mean? Well, x' is you x coordinate 2D projection: for every unit you move backwards in space, the projection will move that point half that many units to the right. And y' represents that same projection for your y coordinate: for every unit you move backwards in space, the projection will move that point a quarter unit up.
So a point at [0,0,0] will get projected to a 2d point of [0,0]. A point at [0,0,4] will get projected to a 2d point of [2,1].
Implemented in JavaScript, it would look something like this:
// Dimetric projection functions
var dimetricTx = function(x,y,z) { return x + z/2; };
var dimetricTy = function(x,y,z) { return y + z/4; };
Once you have these projection functions -- or ways to translate from 3D space into 2D space -- you can use them to start draw your image. A simple example of that using js canvas. First, some context stuff:
var c = document.getElementById("cnvs");
var ctx = c.getContext("2d");
Now, lets make a little helper to draw a 3D point:
var drawPoint = (function(ctx,tx,ty, size) {
return function(p) {
size = size || 3;
// Draw "point"
ctx.save();
ctx.fillStyle="#f00";
ctx.translate(tx.apply(undefined, p), ty.apply(undefined,p));
ctx.beginPath();
ctx.arc(0,0,size,0,Math.PI*2);
ctx.fill();
ctx.restore();
};
})(ctx,dimetricTx,dimetricTy);
This is pretty simple function, we are injecting the canvas context as ctx, as well as our tx and ty functions, which in this case our the dimetric functions we saw earlier.
And now a polygon drawer:
var drawPoly = (function(ctx,tx,ty) {
return function() {
var args = Array.prototype.slice.call(arguments, 0);
// Begin the path
ctx.beginPath();
// Move to the first point
var p = args.pop();
if(p) {
ctx.moveTo(tx.apply(undefined, p), ty.apply(undefined, p));
}
// Draw to the next point
while((p = args.pop()) !== undefined) {
ctx.lineTo(tx.apply(undefined, p), ty.apply(undefined, p));
}
ctx.closePath();
ctx.stroke();
};
})(ctx, dimetricTx, dimetricTy);
With those two functions, you could effectively draw the kind of graph you are looking for. For example:
// The array of points
var points = [
// [x,y,z]
[20,30,40],
[100,70,110],
[30,30,75]
];
(function(width, height, depth, points) {
var c = document.getElementById("cnvs");
var ctx = c.getContext("2d");
// Set some context
ctx.save();
ctx.scale(1,-1);
ctx.translate(0,-c.height);
ctx.save();
// Move our graph
ctx.translate(100,20);
// Draw the "container"
ctx.strokeStyle="#999";
drawPoly([0,0,depth],[0,height,depth],[width,height,depth],[width,0,depth]);
drawPoly([0,0,0],[0,0,depth],[0,height,depth],[0,height,0]);
drawPoly([width,0,0],[width,0,depth],[width,height,depth],[width,height,0]);
drawPoly([0,0,0],[0,height,0],[width,height,0],[width,0,0]);
ctx.stroke();
// Draw the points
for(var i=0;i<points.length;i++) {
drawPoint(points[i]);
}
})(150,100,150,points);
However, you should now be able to start to see some of the complexity of your actual question emerge. Namely, you asked about rotation, in this example we are using an extremely simple projection (our dimetric projection) which doesn't take much other than an oversimplified relationship between depth and its influences on x,y position. As the projections become more complex, you need to know more about your relationship/orientation in 3D space in order to create a reasonable 2D projection.
A working example of the above code can be found here. The example also includes isometric projection functions that can be swapped out for the dimetric ones to see how that changes the way the graph looks. It also does some different visualization stuff that I didn't include here, like drawing "shadows" to help "visualize" the actual orientation -- the limitations of 3D to 2D projections.
It's complicated, and even a superficial discussion is kind of beyond the scope of this stackoverflow. I recommend you read more into the mathematics behind 3D, there are plenty of resources, both online and in print form. Once you have a more solid understanding of the basics of how the math works then return here if you have a specific implementation question about it.
What you want to do is impossible to do using the method you've stated - this is because a box - when rotated in 3 dimensions won't look anything like that diagram of yours. It will also vary based on the type of projection you need. You can, however get started using three.js which is a 3D drawing library for Javascript.
Hope this helps.
How to Draw 3D Rectangle?
posted in: Parallelogram | updated on: 14 Sep, 2012
To sketch 3 - Dimensional Rectangle means we are dealing with the figures which are different from 2 – D figures, which would need 3 axes to represent them. So, how to draw 3D rectangle?
To start with, first make two lines, one vertical and another horizontal in the middle of the paper such that they represent a “t” letter of English. This is what we need to draw for temporary use and will be removed later after the construction of the 3 – D rectangle is complete. Next we draw a Square whose measure of each side is 1 inch. Square must be perfect in Geometry so that 90 degree angles that are formed at respective corners are exact in measure. Now starting from upper right corner of the square we draw a line segment that will be stretched to a measure of 2 inches in the direction at an angle of 45 degrees. Similarly, we repeat the procedure by drawing another Line Segment from the upper left corner of the square and stretching it to 2 inches length in the direction at an angle of 45 degrees. These 2 line segments are considered to be the diagonals with respect to the horizontal line that we drew temporarily in starting. Also these lines will be parallel to each other. Next we draw a line that joins the end Point of these two diagonals.
Next starting from the very right of the 2 inch diagonal end point, draw a line of measure 1 inch that is supposed to be perpendicular to the temporary horizontal line. Next we need to join the lower left corner of the square with end point of the last 1’’ line we drew in 4th step and finally we get our 3 - D rectangular. Now we can erase our initial “t”. This 3- D rectangle resembles a Cuboid.

Finding element nearest to clicked point

Need some help here. I'm a UI designer who isn't good at numbers doing an experimental web form design and I need to know which input element is closest to a clicked point on a web page. I know how to do nearest neighbor with points but the input elements are rectangles not points so I'm stuck.
I'm using jQuery. I just need help with this little algo. Once I'm done with my experiment I'll show you guys what I'm doing.
UPDATE
I thought about how it can work. Look at this diagram:
Each rectangle has 8 points (or rather 4 points and 4 lines) which are significant. Only the x value is significant for horizontal points (red dot) and only the y value is significant for vertical points (green dot). Both x and y are significant for the corners.
Orange crosses are the points to be measured against – mouse clicks in my use case. The light purple lines are the distances between the orange cross and it's possible nearest point.
So… for any given orange cross, loop through each of the 8 points n every rectangle to find the nearest edge or corner closest of each rectangle to the orange cross. The rectangle with the lowest value is the nearest one.
I can conceptualize and visualize it but can't put it into code. Help!
Your algorithm is correct. Since you need help in code, and not in the algorithm, here's the code:
It may not be the most efficient. But it works.
// Define the click
var click = Array(-1, -2); // coodinates in x,y
// Define the buttons
// Assuming buttons do not overlap
var button0 = Array(
Array(0, 0), // bottom-left point (assuming x is horizontal and y is vertical)
Array(6, 6) // upper-right point
);
var button1 = Array(
Array(10, 11),
Array(17, 15)
);
var button2 = Array(
Array(-8, -5),
Array(-3, -1)
);
// Which button to trigger for a click
i = which(click, Array(button0, button1, button2));
alert(i);
function which(click, buttons){
// Check if click is inside any of the buttons
for (i in buttons){
var button = buttons[i];
var bl = button[0];
var tr = button[1];
if ( (click[0] >= bl[0] && click[0] <= tr[0]) &&
(click[1] >= bl[1] && click[1] <= tr[1]) ){
return i;
}
}
// Now calculate distances
var distances = Array();
for (i in buttons){
var button = buttons[i];
var bl = button[0];
var tr = button[1];
if ( (click[0] >= bl[0] && click[0] <= tr[0])) {
distances[i] = Math.min( Math.abs(click[1]-bl[1]), Math.abs(click[1]-tr[1]) );
}
else if ( (click[1] >= bl[1] && click[1] <= tr[1])) {
distances[i] = Math.min( Math.abs(click[0]-bl[0]), Math.abs(click[0]-tr[0]) );
}
else{
distances[i] = Math.sqrt(
(Math.pow(Math.min( Math.abs(click[0]-bl[0]), Math.abs(click[0]-tr[0]) ), 2)) +
(Math.pow(Math.min( Math.abs(click[1]-bl[1]), Math.abs(click[1]-tr[1]) ), 2))
);
}
}
var min_id = 0;
for (j in distances){
if (distances[j] < distances[min_id]){
min_id = j;
}
}
return min_id;
}
The addition of the relatively new elementFromPoint() API lets us take an alternative, potentially lighter approach: we can hit test around the mouse cursor, going in larger circles until we find the nearest element.
I put together a quick, non-production example here: http://jsfiddle.net/yRhhs/ (Chrome/Safari only due to use of webkitMatchesSelector). The performance can get laggy due to the dots used in visualizing the algorithm.
The core of the code, outside of the light performance optimizations and event bindings, is this bit:
function hitTest(x, y){
var element, i = 0;
while (!element){
i = i + 7; // Or some other threshold.
if (i > 250){ // We do want some safety belts on our while loop.
break;
}
var increment = i/Math.sqrt(2);
var points = [
[x-increment, y-increment], [x+increment, y-increment],
[x+increment, y+increment], [x-increment, y+increment]
];
// Pop additional points onto the stack as the value of i gets larger.
// ...
// Perhaps prematurely optimized: we're using Array.prototype.some to bail-out
// early once we've found a valid hit target.
points.some(function(coordinates){
var hit = document.elementFromPoint.apply(document, coordinates);
// isValidHit() could simply be a method that sees whether the current
// element matches the kinds of elements we'd like to see.
if (isValidHit(hit)){
element = hit;
return true;
}
});
}
You could look for the nearest corner point of all rectangles. This works in the most cases, is fast and easy to implement. As long as your rectangles are aligned on a regular grid this method gives you the nearest rectangle.
The way I'd do it is not with numbers, but with logic.
I'm assuming that you want to end up with something that says, "if x is the nearest element then do something when I clicked elsewhere then do something to x"
You could do this if each of the elements you want to do something with were in simple <div> containers that were larger than the element you want to treat, but no larger than halfway between the object it contains and it's next nearest object. A grid in fact.
give all the containers the same class.
Then you could say, "if y is clicked go do something to x", you would already know which element is in each container.
I'd write the code but I'm leaving work...
If you want to find the distance between two points on a 2D grid, you can use the following formula:
(for 2D points A & B)
distanceX = A.x - B.x
distanceY = A.y - B.y
totalDistance = squareRoot ((distX * distX) + (distY * distY))
Once you can check the distance between two points you can pretty easily figure out which rectangle corner your mouse click is closest too. There are numerous things you can do to optimise your intended algorithm, but this should give you a good start.
lol, the question is why are you thinking of shapes?
your question really is "if i click a coordinate, find me the nearest node/point to my click" which is a matter of going through the various nodes and calculating distances.
If same X, use y difference
If same y, use x difference
otherwise use hypotheneuse
Once you find the nearest point you can get the parent shape right?
This will work because you're trying to snap to nearest point. So it'll even work with fancy shapes like stars.

Categories