测试一个点是否大约在由另外两个点形成的线段上
Test if a point is approximately on a line segment formed by two other points
我想确定是否在点之间以及在连接这两个点的线段上进行了点击。
我的问题与How can you determine a point is between two other points on a line segment?类似,但有两点不同:
- 我在 javascript 工作,坐标是整数(像素)
- 该点不必正好在线段上。需要一个宽容。
以下代码改编自this answer,但我不知道如何在线段周围插入公差
a
和b
是线段的端点,c
是点击的点。我想检查 c
是否在 a
和 b
之间以及大致在连接 a
和 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;
}
大概就是你想要的公式。
这是此公式的 link 维基百科页面。
此来源非常详尽,但可能不是最容易阅读的。您可以阅读 wiki 上的解释,但我将在这里以另一种方式进行解释,希望能帮助您和其他读者形象化地理解它。
x0 和 y0 是您点击点的坐标。
x1 和 y1 是第一条线端点的坐标。
x2 和 y2 是同一直线上第二个端点的坐标。
该公式以三个点的三组坐标为参数。
前两组坐标构成直线
第三个参数是您的点击点。
它returns一段距离。
好的,现在让我们试着想象一下这个公式在做什么。
所以你拿点击点和直线的两个端点,你想象一个三角形。我们得到了三个点,这就是我们需要的三角形。
所以,要找到三角形的高度,您有一个公式,它是熟悉的 A = (1/2)bh
的重新排列
所以当你找到这样的三角形的高度时,你就是在找到点击点和直线之间的距离。 (准确的说是点击点到直线的最短距离)
上面更大的距离公式,基本上就是这样做的。这里的区别,以及为什么它看起来更复杂,是明确显示了计算 A 的部分。
关于你提到的公差,只需设置一个公差变量,然后将距离与该公差进行比较。如果您想要更多 "fuzzy" 在线附近的点击容差,您需要做更多的数学计算,但我假设您只想知道点击是否与线有一定距离。
当你写这个函数的时候,一定要好好记账,在正确的地方设置正确的坐标,否则你会返回一段距离,而不是你想要的。
既然你提到你使用的是整数,你可能无法从距离公式中得到一个完美的整数,我的意思是,看看那个平方根,所以如果你没有得到一个完美的整数,不用担心,只需四舍五入或向下,
您可以在两点之间叠加 div
,根据需要旋转,并根据需要加厚。将 hover
CSS 或单击事件附加到 div。
使用距离公式确定div的宽度:
使用此公式确定要旋转的角度:
Math.atan2((y1-y2),(x1-x2))*(180/Math.PI)
为简单起见,我在我的代码片段中使用了 jQuery,但我可以在原版中快速重写 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>
这是我现在使用的最终 javascript 函数。
第一部分使用 中解释的距离公式,我们首先测试 c
是否在经过的直线的预定义距离 (tolerance
) 内a
和 b
.
第二部分摘自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;
};
我想确定是否在点之间以及在连接这两个点的线段上进行了点击。
我的问题与How can you determine a point is between two other points on a line segment?类似,但有两点不同:
- 我在 javascript 工作,坐标是整数(像素)
- 该点不必正好在线段上。需要一个宽容。
以下代码改编自this answer,但我不知道如何在线段周围插入公差
a
和b
是线段的端点,c
是点击的点。我想检查 c
是否在 a
和 b
之间以及大致在连接 a
和 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;
}
大概就是你想要的公式。
这是此公式的 link 维基百科页面。
此来源非常详尽,但可能不是最容易阅读的。您可以阅读 wiki 上的解释,但我将在这里以另一种方式进行解释,希望能帮助您和其他读者形象化地理解它。
x0 和 y0 是您点击点的坐标。 x1 和 y1 是第一条线端点的坐标。 x2 和 y2 是同一直线上第二个端点的坐标。
该公式以三个点的三组坐标为参数。
前两组坐标构成直线
第三个参数是您的点击点。
它returns一段距离。
好的,现在让我们试着想象一下这个公式在做什么。 所以你拿点击点和直线的两个端点,你想象一个三角形。我们得到了三个点,这就是我们需要的三角形。
所以,要找到三角形的高度,您有一个公式,它是熟悉的 A = (1/2)bh
的重新排列所以当你找到这样的三角形的高度时,你就是在找到点击点和直线之间的距离。 (准确的说是点击点到直线的最短距离)
上面更大的距离公式,基本上就是这样做的。这里的区别,以及为什么它看起来更复杂,是明确显示了计算 A 的部分。
关于你提到的公差,只需设置一个公差变量,然后将距离与该公差进行比较。如果您想要更多 "fuzzy" 在线附近的点击容差,您需要做更多的数学计算,但我假设您只想知道点击是否与线有一定距离。
当你写这个函数的时候,一定要好好记账,在正确的地方设置正确的坐标,否则你会返回一段距离,而不是你想要的。 既然你提到你使用的是整数,你可能无法从距离公式中得到一个完美的整数,我的意思是,看看那个平方根,所以如果你没有得到一个完美的整数,不用担心,只需四舍五入或向下,
您可以在两点之间叠加 div
,根据需要旋转,并根据需要加厚。将 hover
CSS 或单击事件附加到 div。
使用距离公式确定div的宽度:
使用此公式确定要旋转的角度:
Math.atan2((y1-y2),(x1-x2))*(180/Math.PI)
为简单起见,我在我的代码片段中使用了 jQuery,但我可以在原版中快速重写 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>
这是我现在使用的最终 javascript 函数。
第一部分使用 c
是否在经过的直线的预定义距离 (tolerance
) 内a
和 b
.
第二部分摘自Cyrille Ka' answer from a similar question。
Then, to know if
c
is betweena
andb
, 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 betweena
andb
.
// 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;
};