最近在开发gis系统,需要用到在线段上添加点的功能,线段是由很多点连接起来的,所以就要判断点在那一段线段里面,然后插入点。
点是否在线段上可以转换为三点是否共线的问题:
三点是否共线可以通过判断斜率来判断:
设有 p1,p2,q三点,判断三点是否共线:
公式:
k1 = (p2.y - p1.y)/(p2.x - p1.x)
k2 = (q.y - p1.y)/(q.x - p1.x)
如果k1 === k2就表示三点共线
但是在计算机中,点的计算是浮点型,需要设定一个阈值
这里阈值设置为0.1,可以根据实际情况进行设置
于是 (k2 - k1 - 0.1) <= EPSILON 时显然成立
JS代码:
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>
<input type="button" value="画线段" onclick="drawLine()">
<input type="button" value="画点">
<canvas id="canvas" style="border: 1px solid red; position:absolute;">
浏览器不兼容
</canvas>
<style>
*{
margin:0;
padding:0;
}
</style>
<script>
let canvas = document.getElementById('canvas')
canvas.width = 500
canvas.height = 500
let ctx = canvas.getContext('2d')
let flag = false
let points = []
let previousX = -1, previousY = -1
function drawLine () {
flag = !flag
}
canvas.onmousedown = (e) => {
if (flag) {
points.push([e.offsetX, e.offsetY])
if (previousX === -1 && previousY === -1)
{
previousX = e.offsetX
previousY = e.offsetY
return
}
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = 'red';
ctx.moveTo(previousX, previousY);
ctx.lineTo(e.offsetX, e.offsetY);
previousX = e.offsetX
previousY = e.offsetY
ctx.lineCap = 'round';
ctx.stroke();
ctx.closePath();
console.log(points)
} else { // 判断点是否在线段上面
console.log(pointInSegments([e.offsetX, e.offsetY], points))
}
}
function pointInSegments (p, segments) {
for (let i = 1; i < segments.length; i++ ){
let pi = segments[i-1]
let pj = segments[i]
if (onSegment(pi, pj, p)) {
return [pi, pj]
}
}
return null
}
function onSegment (p1, p2, q){
p1 = {x: p1[0], y: p1[1]}
p2 = {x: p2[0], y: p2[1]}
q = {x: q[0], y: q[1]}
let k1 = ((p2.y - p1.y)/(p2.x-p1.x)).toFixed(3)
let k2 = ((q.y-p1.y)/(q.x-p1.x)).toFixed(3)
let error = Math.abs(k2 - k1)
if (error - 0.1 <= Number.EPSILON) {
return [p1,p2]
} else {
return null
}
}
</script>
</body>
</html>
结果: