如何在点数组的正确位置插入一个点(折线)

时间:2014-02-19 01:14:35

标签: vector geometry 2d lines polyline

假设我有一系列我用来绘制折线的点。当我点击折线时,我想在点击发生的行上添加一个新点。我需要弄清楚在数组中插入新点的位置,以便正确绘制线条。一条线可以向任何方向前进:向上,向下,向左,向右,向上和向上左,下&对等等,所以我最初的想法是弄清楚线路的行进方向和斜率,然后从那里进行比较。考虑到这一点,我提出了这个代码:

 /**
 * Determines direction line is traveling in
 * @param {Object} point1 - starting point
 * @param {Object} point2 - ending point
 * @param {Object} offset - position offset
 * @return {Number} direction traveling (0 - straight up, 1 - up & right, 2 - straight right, 3 - down & right, etc.)
 */
getLineDirection: function(point1, point2, offset){
    var rise, run, abs1, abs2, dirX, dirY,
        slope, direction, ceil = {}, floor = {};

    //get normalized positions
    abs1 = this.getPointPosition(null,offset,point1);
    abs2 = this.getPointPosition(null,offset,point2);

    dirX = abs1.x > abs2.x ? -1 : 1;
    dirY = abs1.y > abs2.y ? 1 : -1;

    ceil.x = abs2.x > abs1.x ? abs2.x : abs1.x;
    ceil.y = abs2.y > abs1.y ? abs2.y : abs1.y;

    floor.x = abs2.x > abs1.x ? abs1.x : abs2.x;
    floor.y = abs2.y > abs1.y ? abs1.y : abs2.y;

    run = ceil.x - floor.x;
    rise = ceil.y - floor.y

    if(abs2.y > abs1.y){
        rise = -rise;
    }

    if(abs2.x < abs1.x){
        run = -run;
    }

    if(rise !== 0){
        slope = rise/run;
        if(slope === 0){
            //horizontal
            direction = dirX > 0 ? 2 : 6;
        } else {
            //angle
            if(dirX === 1){
                direction = dirY === 1 ? 1 : 3;
            } else {
                direction = dirY === -1 ? 5 : 7;
            }
        }
    } else {
        //vertical
        direction = dirY > 0 ? 0 : 4;
    }

    return direction;
}

有了这个,我想我可以遍历构成线的点,然后确定是否应该在相应点之前插入新点。这几乎可行,但有些事情已经结束。

findInsertionIndex : function(x,y){
    var count = this.points.length-1;
    var p1 = this.points[0];
    var p2 = { x : p1.x, y : p1.y };
    var offset = this.offset();
    var direction, pos;

    for(i = 1; i < count;i++){
        p2.x += this.points[i].x;
        p2.y += this.points[i].y;
        direction = this.getLineDirection(p1,p2,offset);
        pos = this.getPointPosition(null,offset,p2);

        //TODO: handle vertical lines
        if(direction > 0 && direction < 4){
            if(x < pos.x){
                break;
            }
        } else if (direction > 4){
            if(x > pos.x){
                break;
            }
        }

        p1 = p2;
    }

    return i;
}

我很擅长数学,但这似乎是一种合乎逻辑的方法。我想可能有更好的方法来做到这一点。如果有人能够指向我/我正确的方向,我将不胜感激。

好的,没有更便宜的双关语:)

修改

感谢@MvG,这是完成的findInsertionIndex块供参考:

findInsertionIndex : function(x,y){
    var count = this.points.length;
    var offset = this.offset();
    var p1 = this.getPointPosition(0,offset), p2;
    var direction, pos, ox, oy, dx, dy, t, ex, ey, errSq,best,bestIndex = count-1;

    for(var i = 1; i < count;i++){
        p2 = this.getPointPosition(i,offset);
        ox = x - p1.x;
        oy = y - p1.y;
        dx = p2.x - p1.x;
        dy = p2.y - p1.y;

        t = (ox*dx + oy*dy)/(dx*dx + dy*dy);
        if (t < 0) t = 0;
        if (t > 1) t = 1;
        ex = t*dx - ox;
        ey = t*dy - oy;
        errSq = ex*ex + ey*ey;
        if (!best || errSq < best) {
          best = errSq;
          bestIndex = i;
        }

        p1 = p2;
    }

    return bestIndex;

}

1 个答案:

答案 0 :(得分:0)

删除斜率概念,因为它会强制您为垂直线做特殊情况。相反,你可以计算这样的东西:

ox = x - p1.x;
oy = y - p1.y;
dx = p2.x - p1.x;
dy = p2.y - p1.y;
t = (ox*dx + oy*dy)/(dx*dx + dy*dy);
if (t < 0) t = 0;
if (t > 1) t = 1;
ex = t*dx - ox;
ey = t*dy - oy;
errSq = ex*ex + ey*ey;
if (errSq < best) {
  best = errSq;
  bestIndex = i;
}

这会查看每个段最接近输入点的点。它首先确定一个参数 t ,它告诉您沿着该段最近点出现的位置。这是行(dx,dy)的方向向量和从起始点到鼠标点(ox,oy)的向量的点积,除以方向向量的平方范数。将此 t 限制在[0,1]范围内,您可以将自己重新调整为段而不是整行。然后,您可以计算此最近点与输入点之间的差值向量(ex,ey),并将其平方范数作为衡量输入点实际距离当前段的距离的度量。迭代所有线段并记住最好的线条,你就完成了。