8

I have a polygon with several points and a new point has to be added. The existing points are stored in an array:

var points = [
    {x: 0, y:0},
    {x: 100, y: 0},
    {x: 100, y: 100},
    {x: 0, y: 100}
];

How do you determine which position of the array this newPoint should be added into?

Attempt: I iterated through all the existing points and calculate newPoint's distance from them, and sorted the existing points into an array containing the index of these points, in order of increasing distance from newPoint.

Following my current attempt's method, the next step will be to check whether the 2 closest points are adjacent. If they are, add the newPoint between them in the points array. If they are not adjacent, well I'm kind of stuck here :) How do you check whether the 2 points are adjacent?

Any help greatly appreciated!

jsfiddle: http://jsfiddle.net/y3kmm/


The reason why the order matters is because Shapes are usually drawn in a clockwise manner. Here's a jsfiddle where the blue polygon had a point added at the correct place, and a red polygon with a point added at the end of the points array.

jsfiddle: http://jsfiddle.net/TyQXV/

Nyxynyx
  • 61,411
  • 155
  • 482
  • 830
  • Sorry I forgot to mention that the order matters because the polygon is drawn in a clockwise direction. So if a point thats supposed to be drawn earlier, is drawn at the end, the polygon gets distorted. – Nyxynyx Dec 24 '12 at 20:33
  • you don't really have to grow up the complexity of the program using sort: simply stored two initial points, considering them to be the closest, and then replace them as you iterate through the other points – Rubens Dec 24 '12 at 20:35
  • I got something working; http://jsfiddle.net/hK3Kh/73/. I'm not entirely sure where the current algorithm fails and where it doesn't. I'll do some more tests and then I will be able to complete my answer. – Rikonator Dec 25 '12 at 11:50
  • This is generally stable, but does break at certain points. I assume as the polygon complexity and concavity increases, the algorithm will become less and less efficient. http://jsfiddle.net/qmBWV/ – Rikonator Dec 25 '12 at 12:23

4 Answers4

4

As I said before, you don't really have to grow up the complexity of the program using sort: simply stored two initial points, considering them to be the closest, and then replace them as you iterate through the other point.

However, your problem have more than one possible solution; you must define more constraints to it.

For example, think of four points forming a square:

·-------·
|       |
|       |
|       |
·-------·

Now, add a point to a random position inside the polygon:

·-------·
|       |
| ·     |
|       |
·-------·

There are more than two possible orders that will, still, maintain your polygon convex:

·-------·
 \      |
  ·     |
 /      |
·-------·

·   ____·
|\ /    |
| ·     |
|       |
·-------·
Rubens
  • 14,478
  • 11
  • 63
  • 92
3

Alright, let's take a closer look at polygons.

Ted

We'll call him Ted. When Ted (or a polygon in general) sees a point, he tries to assimilate it within himself. And for assimilation, he decides to give one of his many sides the honor of reaching out and grabbing the point. Now, Ted is busy with more important stuff, so the sides need to decide among themselves which one will do the good deed. I'll say it again; sides.

enter image description here

There we go, there's a point in Ted's vicinity, blissfully unaware that soon it's going to be consumed viciously. So, how do Ted's sides decide which one will snatch the point? Well, it needs to be fair game. The side which senses that the perpendicular distance from it to the point is the shortest will be the one to do it. Shortest. Perpendicular. Distance.

enter image description here

Assimilation complete!

Another thing to keep in mind is that polygons like Ted value survival over assimilation. The side with the shortest perpendicular distance from the point commences the assimilation only if it does not cut through the other sides. So, sides with only valid assimilation are considered. Otherwise, bad things happen.

enter image description here

There are two important points within my unnecessary spiel;

  • The side to which we add the point should have the shortest perpendicular distance from the point.
  • Apart from having the shortest perpendicular distance from the point, the addition of the point to this side should be valid. This is important, after all, we don't want to perpetrate mass polygon murder.

So, here's some pseudo-code;

For each side S in polygon P
Do
    d := perpendicularDistanceFromSide(S, point);
    If d is less than shortestPerpendicularDistance
    Do
        If additionIsValid(S, point, P)
        Do
            shortestPerpendicularDistance := d;
            index = S.index;
        End If
    End If
End For

Finding the perpendicular distance from the side is easily done;

var perpendicularDistance = function(side, point) {
    //Find direction vector of side
    var dir = {x: side.p2.x - side.p1.x, y: side.p2.y - side.p1.y};
    var m = Math.sqrt(dir.x*dir.x + dir.y*dir.y);
    if(m !== 0) {
        d.x = d.x/m;
        d.y = d.y/m;
    }

    //Find position vector of point, from side
    var pVec = {x: point.x - side.p1.x, y: point.y - side.p1.y};

    //Absolut of cross product of dir and pVec. 
    //It's essentially |pVec|*Sin(q), q is the angle between
    //dir and pVec.
    return Math.abs(dir.x*pVec.y - dir.y*pVec.x);
};

Now, let's look at validity. If, after adding the point to the polygon, the new sides cut through any of the other sides, it's over for the little guy. We need to ensure that our algorithm accounts for this.

I thought up two versions for this; one cheap and one expensive.

Method 1:

If there is little to none concavity in the polygon, this method works perfectly. This method only checks that the new sides do not intersect the sides adjacent to the old side.

Say we have a side S. It's two adjacent sides are Sprev and Snext. The two new sides are S1p and S2p. S1p and Sprev have the point S.p1 in common. S2p and Snext have the point S.p2 in common.

According to this method, the addition is valid if and only if there is no intersection between:

  • Sprev and S2p
  • Snext and S1p

So, this method works on rejection. If we can't find any intersection, the side S can add the point validly.

Method 1 Demo

Method 2:

Method 1 fails as the concavity of the polygon increases. So, we need to do further checking to ensure valid point addition.

This method is really an extension of Method 1. After finding that the pairs Sprev and S2p, and Snext and S1p are not intersecting, we check if the new sides are intersecting with all the other sides of the polygon (apart from Sprev and Snext, of course).

If the addition is not rejected after all the sides are checked, we are completely sure that this addition is valid.

The problem is, that while rejection is quick enough, reaching acceptance takes a long time, making this method quite expensive.

Furthermore, the complexity depends on the number of sides of the polygon. As the polygon complexity increases, checking validity will take more and more time.

Method 2 Demo

I must note, the algorithm I used for checking line-segment intersection is taken from How do you detect where two line segments intersect?. It's totally awesome.

And that's all I have. This is one way to do it, I must say. There might be another, better way which has not struck me yet. I hope you didn't terribly mind Ted. And also, thanks for asking a good question, I enjoyed it.

Community
  • 1
  • 1
Rikonator
  • 1,830
  • 16
  • 27
2

I believe your calculation won't solve the issue you are experiencing. For example, take the following two polygons:

var poly3 = new Kinetic.Polygon({
    x: 200,
    y: 100,
    points: [0,25,25,0,150,100,75,125],
    fill: 'green',
    stroke: 'black',
    strokeWidth: 0,
    name: 'poly',
    draggable: false
});
group.add(poly3);

var poly4 = new Kinetic.Polygon({
    x: 300,
    y: 100,
    points: [0,25,25,0,75,125,150,100],
    fill: 'yellow',
    stroke: 'black',
    strokeWidth: 0,
    name: 'poly',
    draggable: false
});
group.add(poly4);

The closest point is not necessarily the one that should be drawn next.

Jason Whitted
  • 4,059
  • 1
  • 16
  • 16
0

The reason why the order matters is because Shapes are usually drawn in a clockwise manner.

You just answered your question. Where you add the new point depends on what shape you want and so has nothing to do with distance. You simple add the point at the location you want it to be drawn.

Say, do you already know what the shape should be or do you have to guess the shape based on where a user clicks the mouse? If you must guess, you may have to set a default behavior. Otherwise, give your user an additional parameter.

kasavbere
  • 5,873
  • 14
  • 49
  • 72