我想确定鼠标点击点是否位于SVG折线上.我找到了这个Python代码来确定一个点是否位于另外两个点之间,并在JavaScript中重新实现它.
function isOnLine(xp, yp, x1, y1, x2, y2){ var p = new Point(x, y); var epsilon = 0.01; var crossProduct = (yp - y1) * (x2 - x1) - (xp - x1) * (y2 - y1); if(Math.abs(crossProduct) > epsilon) return false; var dotProduct = (xp - x1) * (x2 - x1) + (yp - y1)*(y2 - y1); if(dotProduct < 0) return false; var squaredLengthBA = (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1); if(dotProduct > squaredLengthBA) return false; return true; }
但它不按我想要的方式工作,因为我永远不会将鼠标指针准确地放在线上.所以我需要像"虚构粗线"这样的东西来获得一些误差:
有任何想法吗?
所述叉积通过该线的长度划分给出从线的点的距离.所以只需将其与某个阈值进行比较:
function isOnLine (xp, yp, x1, y1, x2, y2, maxDistance) { var dxL = x2 - x1, dyL = y2 - y1; // line: vector from (x1,y1) to (x2,y2) var dxP = xp - x1, dyP = yp - y1; // point: vector from (x1,y1) to (xp,yp) var squareLen = dxL * dxL + dyL * dyL; // squared length of line var dotProd = dxP * dxL + dyP * dyL; // squared distance of point from (x1,y1) along line var crossProd = dyP * dxL - dxP * dyL; // area of parallelogram defined by line and point // perpendicular distance of point from line var distance = Math.abs(crossProd) / Math.sqrt(squareLen); return (distance <= maxDistance && dotProd >= 0 && dotProd <= squareLen); }
PS.上面的代码基本上会将线段扩展为一个框,方法是maxDistance
在任一侧加宽它,并接受该框中的任何点击.如果您要将其应用于折线(即多个线段以端对端方式连接),您可能会发现这些框之间存在间隙,其中两个线段以一定角度相交:
解决此问题的一种简单而自然的方法是接受maxDistance
来自端点半径范围内任何位置的任何点击,实质上用(半)圆形端盖填充虚构框:
以下是实现此目的的一种方法:
function isOnLineWithEndCaps (xp, yp, x1, y1, x2, y2, maxDistance) { var dxL = x2 - x1, dyL = y2 - y1; // line: vector from (x1,y1) to (x2,y2) var dxP = xp - x1, dyP = yp - y1; // point: vector from (x1,y1) to (xp,yp) var dxQ = xp - x2, dyQ = yp - y2; // extra: vector from (x2,y2) to (xp,yp) var squareLen = dxL * dxL + dyL * dyL; // squared length of line var dotProd = dxP * dxL + dyP * dyL; // squared distance of point from (x1,y1) along line var crossProd = dyP * dxL - dxP * dyL; // area of parallelogram defined by line and point // perpendicular distance of point from line var distance = Math.abs(crossProd) / Math.sqrt(squareLen); // distance of (xp,yp) from (x1,y1) and (x2,y2) var distFromEnd1 = Math.sqrt(dxP * dxP + dyP * dyP); var distFromEnd2 = Math.sqrt(dxQ * dxQ + dyQ * dyQ); // if the point lies beyond the ends of the line, check if // it's within maxDistance of the closest end point if (dotProd < 0) return distFromEnd1 <= maxDistance; if (dotProd > squareLen) return distFromEnd2 <= maxDistance; // else check if it's within maxDistance of the line return distance <= maxDistance; }