假设我有一系列我用来绘制折线的点。当我点击折线时,我想在点击发生的行上添加一个新点。我需要弄清楚在数组中插入新点的位置,以便正确绘制线条。一条线可以向任何方向前进:向上,向下,向左,向右,向上和向上左,下&对等等,所以我最初的想法是弄清楚线路的行进方向和斜率,然后从那里进行比较。考虑到这一点,我提出了这个代码:
/**
* 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;
}
答案 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),并将其平方范数作为衡量输入点实际距离当前段的距离的度量。迭代所有线段并记住最好的线条,你就完成了。