今天在项目中看到了“点到线段最短距离”的函数:
float GetDistanceToLine(Vector2 point, Vector2 p0, Vector2 p1) { //2D Vector2 v = p1 - p0; Vector2 w = point - p0; float c1 = Vector2.Dot(w,v); if ( c1 <= 0 ) return w.Length(); float c2 = Vector2.Dot(v); if ( c2 <= c1 ) return Vector2.Distance(point,p1); Vector2 pb = Vector2.Lerp(p0,p1,c1 / c2); //垂直交点 return Vector2.Distance(point,pb); }
上边代码是2D,3D代码也一样,稍作修改即刻。其中点在线段两端,即没有垂直交点的情况看懂了(主要用到了点乘的夹角等)。反而在垂直情况下却没看明白(c1 / c2)。
因为数学基础不好(图形学就更甭说了)。平时只会调用API的我,决定要弄明白点乘的几何意义。
首先是查了查书,然后又请教了同事,同事研究了一回后说这是物理上的做功!
我们先看看书中的介绍:
书中说的几何意义只有两点:
1.向量的相似度
2.向量a在向量b上的投影(注意:投影向量a需要b的单位向量,稍作推导即可,[点乘符合交换率])
再说一下点乘的定义: a•b = |a||b|cosθ
虽然说是定义,但我固执地认为这是推导出来的(事实上我错了),所以请教了同事:已知向量a,b,和其夹角θ.如何求cosθ?
他给了一个答案:cosθ = a•b / (|a||b|)
。。。三观崩裂了, a•b = |a||b|cosθ,然后再cosθ = a•b / (|a||b|)。。。 这是无法证明的死循环啊~~可能当时他的世界观被我带进坑去了,暂时没转出这个弯~
然后在请教了我哥(他数学比我好吧=m=),然后他说这是定义,无法推导 =。=
以我的脾气肯定不会放过他~然后问他cosθ怎么求,然后他跟我说自己看余弦定理。。。(我擦,这初中的知识啊!!!我都忘了!!当然还有正弦定理也忘了!!!)
然后他给我一个更简单但运算起来更复杂的方法:求a与x轴的夹角再减去b与x轴的夹角,就得到θ的角度。。。(好吧,还是好好回去补习余弦定理好了)
补习了余弦定理后再相应补了正弦定理,此时,已经是下班的节奏了。然后我决定推导成功后再回家!当然最后是失败了(那些过程略掉)
回家途中梳理了下思路,为什么我哥说这是定义,无法推导?后来发现这是一个简单的逻辑错误!!
图1中点乘运算法则:(先假设成定义1)
图2中的几何解释:a•b = |a||b|cosθ(假设成定义2)
其实我想要表达的意思是:定义1 和定义2 为何能划上等号!!??
也就是:a1b1+a2b2+...anbn = |a||b|cosθ 这个简化过程是怎么推导出来的!
所以a1b1+a2b2+...anbn 是定义(a•b = |a||b|cosθ)的简化过程!
(在回家路上想到了推导方法,就是两边平方)
推导过程:
a•b=axbx+ayby
根据余弦定理: c2=a2+b2-2ab•cosθ → cosθ =(a2+b2-c2)/2ab
又因为:c = (a-b) -> c2 = (a-b)2 所以:cosθ2=(a2+b2-(a-b)2)2/4a2b2
=> (|a||b|cosθ)2 = a2b2•cosθ2 = (a2+b2-(a-b)2)2/4 = (a2+b2-(a2+b2-2a•b))2/4 = (a2+b2-a2-b2+2a•b)2/4 = (a•b)2
注意:(a•b)中的“•”,不能忽略,因为是向量相乘,这里可能会想到是一种“死循环”,其实我们直接换成坐标来计算就知道了:(假设是2D)
a•b=axbx+ayby 为何能→ (a•b)2=(axbx+ayby)2 ?
上文推导到:
a2b2•cosθ2
= (a2+b2-(a-b)2)2/4
= ((ax2+ay2)+(bx2+by2) - ((ax-bx)2+(ay-by)2))2/4 {注意c(ax-bx,ay-by)=>c2=(ax-bx)2+(ay-by)2}
= ((ax2+ay2)+(bx2+by2) - ((ax2+bx2-2axbx)+(ay2+by2-2ayby)))2/4
= (ax2+ay2+bx2+by2 - (ax2+bx2-2axbx+ay2+by2-2ayby)2/4
= (ax2+ay2+bx2+by2 - (ax2+bx2-2axbx+ay2+by2-2ayby)2/4