前言
确定穿过矩形中心点的线条与矩形的相交点 --- 算法小白在线头秃👩🏻🦲,找了好久才找到个算法:# 算法-如何找到直线和矩形之间的交点?
前提条件:
1、矩形是对称的,即规则的形状:正方形或长方形。
2、线条的矩形是且是相交的,并且只有一个交点
3、线条的一端是矩形的中心点
—— 通常用于两个矩形或圆之间的连线相关的业务场景。
在满足以上三点条件,再加上捡起来一些数学知识的话,其实也挺简单的。(数学老师狂喜)
如果是不是以上三种条件,那就是另一种算法了👩🏻🦲👩🏻🦲👩🏻🦲👩🏻🦲👩🏻🦲👩🏻🦲。
本文的定义
先确定下本文的所有参数。
h:矩形的高
w:矩形的宽
m:斜率
(x1,y1):线条位于矩形外的点
(x2,y2):线条位于矩形内的点
斜率
这个比较重要,先了解斜率是什么
在数学上,直线的斜率(slope)或称梯度(gradient),是描述与度量该线“方向”和“陡度”的数字,常用 m 表示;斜率也用来计算斜坡的“斜度”(倾斜程度)。透过代数和几何能计算出直线的斜率。
另一个相关概念是倾角(angle of inclination)或斜角,即直线与水平轴(x 轴)所夹的最小角,以 θ表示,-90°<θ<90° 。倾角 θ 的正切函数值为直线的斜率,即 m = tanθ 而 θ = arctan(m),arctan是反正切函数。
——— 维基百科
如图上,θ是斜角(也叫倾角),斜率 m = tan(θ) = (y2-y1) / (x2 - x1)。
此外,维基百科说斜率通常使用m来表示,注意是通常,这个可以随意定义,并没有规范。
确定相交方向
设矩形的四个点是ABCD
先列出所有极端情况,就是以下四条不同颜色的线。
- 判断左右相交
左右相交就是线段的角度分别在两块灰色圆形内(灵魂画手,意思懂就行😎)
此时:
B和C所在的红线和蓝线的斜率是: 矩形的高/矩形的宽
A和D所在的黄线的绿线斜率是:-矩形的高/矩形的宽
所以当前的不等式是 m * w >= -h && m * w <= h
左相交x1<x2
右相交x1>x2
- 判断上下相交
同左右相交一样,上下相交的角度在这两块灰色区域内,但是因为斜角最大是90°,如图,还会有一条90°的辅助虚线,来帮我们找到不等式
此时的斜率要将虚线看作x轴,才能求的相应的角度:
A和B所在的红线和绿线的斜率是: 矩形的宽/矩形的高
C和D所在的黄线的蓝线斜率是:-矩形的宽/矩形的高
所以当前的不等式是h / m >= -w && h / m <= w
上相交y1>y2
下相交y1<y2
求相交的点
在知道了位于哪边相交后,就可以知道一些坐标了,如图的情况,设相交点为(x,y),x点很容易就得出为x2 + w / 2,再通过斜率公示得出y = (m * w )/ 2 + y2,
其他情况也可以依次得出
完整代码
<template>
<div>
<svg width="600" height="600">
<rect :width="rect.width" :height="rect.height" :x="rect.x" :y="rect.y" fill="#ccc"></rect>
<line :x1="start.x" :y1="start.y" :x2="end.x" :y2="end.y" stroke="#000" stroke-width="2" />
<circle r="2" :cx="targetPoint.x" :cy="targetPoint.y" fill="red" />
</svg>
</div>
</template>
<script>
export default {
data() {
return {
rect: {
width: 80,
height: 60,
x: 100,
y: 100
},
start: {},
end: {
x: 300,
y: 100
},
targetPoint: {}
}
},
mounted() {
// 起点在矩形中心
this.start = {
x: this.rect.x + this.rect.width / 2, // 140
y: this.rect.y + this.rect.height / 2 // 130
}
this.init()
},
methods: {
init() {
if (this.end.y === this.start.y) {
console.log('水平相交')
if (this.start.x < this.end.x) {
this.targetPoint = {
x: this.start.x + this.rect.width / 2,
y: this.start.y
}
} else {
this.targetPoint = {
x: this.start.x - this.rect.width / 2,
y: this.start.y
}
}
return
} else if (this.end.x === this.start.x) {
console.log('垂直相交')
if (this.start.y > this.end.y) {
this.targetPoint = {
x: this.start.x,
y: this.start.y - this.rect.height / 2
}
} else {
this.targetPoint = {
x: this.start.x,
y: this.start.y + this.rect.height / 2
}
}
}
const m = (this.end.y - this.start.y) / (this.end.x - this.start.x)
const h = this.rect.height
const w = this.rect.width
// 左右相交
if (m * w >= -h && m * w <= h) {
if (this.start.x > this.end.x) {
console.log('左边相交')
this.targetPoint = {
x: this.start.x - w / 2,
y: this.start.y - (m * w) / 2
}
} else {
console.log('右边相交')
this.targetPoint = {
x: this.start.x + w / 2,
y: this.start.y + (m * w) / 2
}
}
}
// 上下相交
if (h / m >= -w && h / m <= w) {
if (this.start.y > this.end.y) {
console.log('上边相交')
this.targetPoint = {
x: this.start.x - h / 2 / m,
y: this.start.y - h / 2
}
} else {
console.log('下边相交')
this.targetPoint = {
x: this.start.x + h / 2 / m,
y: this.start.y + h / 2
}
}
}
}
}
}
</script>
红色的点就是求的的交点