从X到画布上绘制的形状边缘的距离

时间:2016-09-16 23:45:08

标签: javascript html5 canvas

<html>
<body style="margin:10px;">
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #d3d3d3;">
</canvas>
  
</body>
  
<script language="javascript" type="text/javascript">



      var canvas = document.getElementById('myCanvas');
      var context = canvas.getContext('2d');

      var centerX = 400;
      var centerY = 400;
      var radius = .5;

      context.beginPath(400,400);
      context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
      context.fillStyle = 'blue';
      context.fill();
      context.lineWidth = 1;
      context.strokeStyle = '#003300';
      context.stroke();



      context.beginPath();
      context.moveTo(50, 250);
      context.bezierCurveTo(50, 250, 50, 250, 50, 250);
      context.bezierCurveTo(90, 210, 90, 210, 90, 210);
      context.bezierCurveTo(130, 90, 130, 90, 130, 90);
      context.bezierCurveTo(260, 240, 260, 240, 260, 240);
      context.bezierCurveTo(255, 400, 255, 400, 255, 400);

      context.closePath();
      context.lineWidth = 1;
      context.strokeStyle = 'green';
      context.stroke();
 
</script>
	  
	  
	  
	  
</html>

第一次在这里发帖,但是这个网站对我已经拥有的其他灰色区域有很大的帮助!

我想确定从画布上的一个点到形状边缘的距离。形状将是不规则的,因此,例如,如果它是一个三角形,我不认为距离公式如果落在三角形的边缘之外就会起作用。

我有点沮丧,因为这可能是我项目的垮台。任何开放的想法将不胜感激!

谢谢

编辑:这是我正在谈论的一个例子,在这个例子中,我想知道从400-400,400到对象的最近点有多少像素。但对于我的项目,无论这个点在哪里,我都希望能够测量到对象的最近点。

我知道这有很多要问,但我想我会试一试!

感谢您提前回复!

1 个答案:

答案 0 :(得分:2)

这个问题的范围很大,我可以写一个解决方案。我将概述如何添加一些限制。

形状是一组弧,线,曲线(立方和二次)和可以连接或不连接的矩形。距离将是定义每个形状的线上的最近点,并且不包括线宽。形状都是相同的比例和方向。

最好的方法是使用MarkE指出的距离线功能(在他的评论“find the least distance from the point”中)并将所有路径对象转换为线。细分每条曲线的精确程度将决定位置的准确程度,直线不需要细分。但假设你只是在一个像素(鼠标分辨率)之后,那么代码处理的时间并不会太多。

您可以将线条定义为点集。由于您可能有未连接的路径,因此这些点集将位于二维数组中。

var lines = [];
var currentLine = null;
function startLine(){
   if(currentLine.length > 0){
       currentLine = []; // new array
       lines.push(cuurentLine); // push onto the line array
    }
}
function moveToPoint(x,y){
   startLine();
   addPoint(x,y);
}
function addPoint(x,y){
   currentLine.push(x:x,y:y);
}
function closePath(){
   currentLine.push(currentLine[0]);
}

然后,您可以匹配所有渲染调用以及对上述函数的调用

最简单的一组线

ctx.beginPath();
ctx.moveTo(x,y); // to save me time x and y are points of some coordinate.
ctx.lineTo(x,y);
ctx.lineTo(x,y);
ctx.lineTo(x,y);
ctx.closePath();

应与

匹配
startLine(); // you really only need moveToPoint
moveToPoint(x,y); 
addPoint(x,y);
addPoint(x,y);
addPoint(x,y);
closePath();

您的数组应该看起来像

// p represents a point {x :x,y: y}
lines [[p,p,p,p,p]]; // An array inside lines with 5 points. The last point is 
                     // back to the start 

如果您添加其他路径

ctx.moveTo(x,y); // to save me time x and y are points of some coordinate.
ctx.lineTo(x,y);
ctx.lineTo(x,y);
ctx.lineTo(x,y); 
// note I did not close path

您必须将其与

匹配
moveToPoint(x,y); 
addPoint(x,y);
addPoint(x,y);
addPoint(x,y);

数组现在看起来像

// p represents a point {x :x,y: y}
lines [[p,p,p,p,p],[p,p,p,p]]; // not second path is not closed and has one less point

并包含两个路径。

然后您可以使用线功能(在答案顶部链接)检查以找到最接近形状的点

function findclosestPoint(px,py){ // the point px,py
    var minDist = Infinity; 
    var pf = {x:0,y:0};  // the closest point
    for(i = 0; i < lines.length; i+=1){
        for(j = 0; j < lines[i].length-1; j+=1){
            var p = lines[i][j];
            var p1 = lines[i][j+1];
            // get closet point
            var cp = getClosestPointOnLine({x0 : p.x, y0 : p.y, x1 : p1.x, y1 : p1.y},px,py);
            // then get the distance.
            var x = cp.x - px;
            var y = cp.y - py;
            x *= x;
            y *= y;
            var dist = Math.sqrt(x + y);  

            // is it the closest?
            if(dist < minDist){
                minDist = dist; // yes remember this distance
                pf.x = cp.x;
                pf.y = cp.y;
            }
        }
    }
    return {dist : minDist, point : pf};
}        

对于您只需要调用该函数的形状,您就可以获得解决方案。

var closest = findclosestPoint(400,400);
console.log(closest.point) ;// >> {x:?,y:?}
console.log(closest.dist) ;// >> ? in pixels

要使其适用于弧,请创建一个将弧分成较小线段的函数。贝塞尔曲线和三次曲线相同。相距约2至3个像素的间隔点应该足够多。有很多例子可以在StackOverflow中找到弧和贝塞尔曲线上的点。

以矩形为例

ctx.rect(x,y,w,h); 

创建一个函数来添加定义矩形的线条。

function addRect(x,y,w,h)
    addPoint(x,y)  // rects start at top left  
    addPoint(x+w,y)  
    addPoint(x+w,y+h) 
    addPoint(x,y+h) 
    addPoint(x,y)  
}

请记住,fillrect和strokeRect以moveTo开头,因此如果需要,可以添加moveToPoint函数调用。

对弧和贝塞尔曲线执行相同的操作。

这就是它的全部内容。

如果您计划使用不同的比例和旋转,则在向线阵列添加点时必须添加这些转换。

如果要包含线宽,则必须为线条的每个边创建线条。对于beziers来说这可能很棘手,但是stackoverflow中的每个子问题都有解决方案。从基础知识开始,并进一步完善,直到达到目标。