Test if a point is approximately on a line segment formed by two other points

落爺英雄遲暮 提交于 2019-12-12 10:15:11

问题


I want to determine if a click is made between to points and roughly on the line segment that connects those two points.

My question is similar to How can you determine a point is between two other points on a line segment? but it defers in two points:

  1. Im working in javascript and coordinates are in integers (pixels)
  2. The point do not have to be exactly on the line segment. A tolerance is needed.

The following code is adapted from this answer, but I dont know how to insert a tolerance around the line segment

a and b are the end points of the line segment and c is the clicked point. I want to check if c is between a and b and roughly on the segment that connects a and b

PencilTool.prototype._isBetween = function(a,b,c){
    //test if a, b and c are aligned
    var crossproduct = (c.y - a.y) * (b.x - a.x) - (c.x - a.x) * (b.y - a.y);
    if(Math.abs(crossproduct) !== 0){ return false; }

    var dotproduct = (c.x - a.x) * (b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if(dotproduct < 0){ return false }

    var squaredlengthba = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y);
    if(dotproduct > squaredlengthba){ return false; }

    return true;
}

回答1:


Is probably the formula you want.

Here's the link to the wikipedia page for this formula.

This source is very thorough, but probably not the easiest to read. You can read the explanation on the wiki, but I'm going to explain it here another way in hopes I can help you and other readers visualize it.

x0 and y0 are the coordinates of your click point. x1 and y1 are the coordinates of your first line endpoint. x2 and y2 are the coordinates of your second endpoint on that same line.

This formula takes three sets of coordinates for three points as params.
The first two sets of coordinates form the line.
The third param is your click point. It returns a distance.

Okay, now let's try to visualize what this formula is doing. So you take the clickpoint and the two endpoints of the line, and you imagine a triangle. We've got three points, that's all we need to make a triangle.

so, to find the height of the triangle, you have a formula, which is a rearrangment of the familiar A = (1/2)bh

So when you find the height of the triangle like that, you are finding the distance between the clickpoint and the line. (That is the shortest distance between the clickpoint and the line to be precise)

The bigger distance formula above, is doing basically that. The difference here, and why it looks more complicated, is that the part where A is calculated is shown explicitly.

Concerning the tolerance you were mentioning, just set a variable for tolerance, and compare the distance to that tolerance. If you want something a little more "fuzzy" for the tolerance for clicks near the line you would need to do more math, but I'm assuming you only want to know whether the click was a certain distance from the line.

When you write this function make sure you do good bookkeeping and set the proper coordinates in the right place, other wise you will get back a distance but not the one you want. Since you mentioned you are using integers, you may not get a perfect int back from the distance formula, I mean, look at that square root, so if you don't get a perfect int back, no worries, just round up or down,




回答2:


You could overlay a div between the two points, rotated as needed, and as thick as needed. Attach hover CSS or click events to the div.

Use the distance formula to determine the div's width:

Use this formula to determine the angle to rotate in degrees:

Math.atan2((y1-y2),(x1-x2))*(180/Math.PI)

For simplicity, I'm using jQuery in my Snippet, but I could quickly rewrite in vanilla JavaScript:

var x1= 10+Math.random()*500/4,
    y1= 10+Math.random()*300/4,
    x2= Math.random()*500/2 + 500/4,
    y2= Math.random()*300/2 + 300/4,
    pt= $('.a').width()/2,
    dx= (x2-x1),
    dy= (y2-y1),
    angle= Math.atan2((y1-y2),(x1-x2))*(180/Math.PI),
    tolerance= 30;

$('.a').css({left: x1, top : y1});
$('.b').css({left: x2, top : y2});

$('div.c').css({
  width: Math.sqrt(dx*dx + dy*dy),
  height: tolerance,
  left: x2+pt,
  top: y2+pt,
  transformOrigin: '0px '+tolerance/4+'px',
  transform: 'rotate('+angle+'deg)'
});

$('div.c')
  .click(function() {
    alert('clicked!');
  });
body {
  margin: 0;
  padding: 0;
}

div.a, div.b, div.c {
  position: absolute;
  border-radius: 50%;
  height: 1.2em;
  width: 1.2em;
  text-align: center;
  background: orange;
}

div.c {
  border: 1px solid #eee;
  background: transparent;
  border-radius: 0;
}

div.c:hover ~ div.a, div.c:hover ~ div.b {
  background: lightgreen;
}

div.c hr {
  position: relative;
  top: 50%;
  transform: translateY(-50%);
  height: 1.2em;
  border: none;
  border-top: 1px solid orange;
}

div.c:hover hr {
  border-top: 1px solid green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="c"><hr></div>
<div class="a">A</div>
<div class="b">B</div>



回答3:


Here is the final javascript function I am now using.

The first part is using the distance formula as explained in Ashley Tharp's answer, we first test if c is inside a pre-defined distance (tolerance) of a line going through a and b.

The second part is taken from Cyrille Ka' answer from a similar question.

Then, to know if c is between a and b, you also have to check that the dot product of (b-a) and (c-a) is positive and is less than the square of the distance between a and b.

// a and b are vertices of the segment AB and c is the tested point (here from a mouseclick event (e))
var a={}, b={}, c={};
a.x = 100;
a.y = 100;
b.x = 200;
b.y = 200;
c.x = e.screenX
c.y = e.screeny

console.log(isBetween(a, b, c, 5));

function isBetween(a, b, c, tolerance) {

    //test if the point c is inside a pre-defined distance (tolerance) from the line
    var distance = Math.abs((c.y - b.y)*a.x - (c.x - b.x)*a.y + c.x*b.y - c.y*b.x) / Math.sqrt(Math.pow((c.y-b.y),2) + Math.pow((c.x-b.x),2));
    if (distance > tolerance) { return false; }

    //test if the point c is between a and b
    var dotproduct = (c.x - a.x) * (b.x - a.x) + (c.y - a.y)*(b.y - a.y);
    if (dotproduct < 0) { return false; }

    var squaredlengthba = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y);
    if (dotproduct > squaredlengthba) { return false; }

    return true;
};


来源:https://stackoverflow.com/questions/31346862/test-if-a-point-is-approximately-on-a-line-segment-formed-by-two-other-points

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!