将新Point添加到Points of Points中的正确位置

时间:2012-12-24 20:28:48

标签: javascript jquery algorithm graphics canvas

我有一个带有多个点的多边形,并且必须添加一个新点。现有的点存储在一个数组中:

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

如何确定应将newPoint添加到数组的哪个位置?

尝试:我遍历所有现有点并计算newPoint与它们的距离,并将现有点排序为包含这些点索引的数组,按顺序排列与newPoint的距离越来越远。

按照我当前尝试的方法,下一步将检查2个最近点是否相邻。如果是,请在newPoint数组中添加points。如果它们不相邻,那么我有点卡在这里:)你如何检查2点是否相邻?

任何帮助都非常感谢!

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


订单之所以重要的原因是因为形状通常以顺时针方式绘制。这是一个jsfiddle,其中蓝色多边形在正确的位置添加了一个点,并且在points数组的末尾添加了一个点的红色多边形。

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

4 个答案:

答案 0 :(得分:4)

正如我之前所说,你并不需要使用sort来增加程序的复杂性:只需存储两个初始点,将它们视为最接近的点,然后在迭代另一点时替换它们。

但是,您的问题有多种可能的解决方案;你必须为它定义更多的约束。

例如,考虑形成正方形的四个点:

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

现在,在多边形内的随机位置添加一个点:

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

有两个以上可能的顺序仍会保持多边形凸起:

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

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

答案 1 :(得分:3)

好的,让我们仔细看看多边形。

Ted

我们会叫他泰德。当Ted(或一般多边形)看到一个点时,他试图在自己内部吸收它。并且为了同化,他决定给予他的许多中的一个荣耀,以及抓住要点。现在,特德忙于处理更重要的事情,所以需要自己决定哪一个会做好事。我会再说一遍;的

enter image description here

我们走了,泰德附近有一点,幸福地意识不到它很快就会消耗殆尽。那么,特德的双方如何决定哪一个会抓住这一点呢?嗯,这需要公平的游戏。感觉到垂直距离从它到最短点的那一侧将是这样做的一方。的最短即可。的垂直即可。的距离

enter image description here

同化完成!

要记住的另一件事是像Ted这样的多边形值比同化更有价值。距离点最短垂直距离的一侧只有在没有穿过另一侧时才会开始同化。因此,只考虑有效同化的方。否则,不好的事情发生

enter image description here

我不必要的间谍中有两点重要;

  • 我们添加点的应该具有最短垂直距离
  • 除了与该点之间的垂直距离最短之外,此侧点的添加应有效。毕竟,这很重要,我们不想进行大规模的多边形谋杀。

所以,这里有一些伪代码;

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

轻松完成与侧面的垂直距离;

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

现在,让我们来看看有效性。如果在将点添加到多边形之后,新的边穿过任何其他边,那么小家伙就会结束。我们需要确保我们的算法能够解决这个问题。

我想到了两个版本;一个便宜,一个贵。

方法1

如果多边形中几乎没有凹陷,则此方法可以完美地工作。此方法仅检查新边与旧边相邻的边不相交。

假设我们有一个方S。它的两个相邻边是SprevSnext。这两个新方面是S1pS2pS1pSprev共有点S.p1S2pSnext共同点S.p2

根据这种方法,当且仅当在:

之间没有交叉时,加法才有效
  • SprevS2p
  • SnextS1p

所以,这种方法适用于拒绝。如果我们找不到任何交叉点,那么边S可以有效地添加该点。

<强> Method 1 Demo

方法2

方法1失败,因为多边形的凹度增加。因此,我们需要进一步检查以确保有效的点加法。

此方法实际上是方法1的扩展。在发现对SprevS2p以及SnextS1p不相交之后,我们检查是否新边与多边形的所有其他边相交(当然除了SprevSnext之外)。

如果在检查完所有方面后没有拒绝添加,我们完全确定此添加有效。

问题是,虽然拒绝速度足够快,但接受验证需要很长时间,这使得这种方法非常昂贵。

此外,复杂性取决于多边形的边数。随着多边形复杂度的增加,检查有效性将花费越来越多的时间。

<强> Method 2 Demo

我必须注意,我用于检查线段交叉的算法取自How do you detect where two line segments intersect?。这真是太棒了。

这就是我的全部。我必须说,这是一种方法。可能还有另一种更好的方式还没有打动我。我希望你不要特别介意泰德。而且,感谢您提出一个好问题,我很喜欢。

答案 2 :(得分:2)

我相信您的计算无法解决您遇到的问题。例如,采用以下两个多边形:

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

最近的点不一定是下一个应该绘制的点。

答案 3 :(得分:0)

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

你刚回答了你的问题。添加新点的位置取决于您想要的形状,因此与距离无关。您只需在要绘制的位置添加点即可。

说,您是否已经知道形状应该是什么,或者您必须根据用户点击鼠标的位置来猜测形状?如果您必须猜测,则可能必须设置默认行为。否则,请为您的用户提供其他参数。