2. 在鼠标按下位置插入一个点,拖动鼠标时,改变该点的位置,线的第一个点和最后一个点不动,中间的点跟着移动。
第一种方案有用,实现起来很简单,我们不做讨论。
我们讨论第二种方案,这种方案,难点在于确定中间的点怎么移动。
我们只看起始点和拖动的点之间的导线(因为两边模型是一样的,算完一边,另一边套用相同的方法就可以了),我们可以把这段导线看做一个整体,以起点为坐标原点,先让导线沿起点和拖动的点确定的方向缩放(垂直方向不变),然后旋转,最终到达目标点。
效果如图:
拖动后起点为O,拖动的点为p0',中间的点为pi',(O、p0')长度为d',(O,p0')与x轴的夹角为θ'。
假设向右为x正方向,向上为y轴正方向。
方法1
对每一个点执行如下操作:
· 顺时针旋转θ
· 沿x方向放大d'/d倍
· 逆时针旋转θ'
方法2
别人的方法,不推荐,如下图。
已知起点是OP、拖动点是curP,中间点是itemP,拖动后的点是toP。求拖动后itemP对应的点P。
· 先求出itemP在直线(OP,curP)上的垂足itemPG
· 再求出矩形(OP,itemPG,itemP,OP2)中的OP2
· 再求出OP2逆时针旋转θ后的点rOP2_ (其中θ=∠(toP,OP,curP))
· 再求出itemP逆时针旋转θ后的点ritemP
· 线段(rOP2_,ritemP)放大d'/d倍,得到点P
相对于第一种方法,太复杂,不容易理解,计算量大,中间过程隐含了很多容易出错的地方。
方法3
其实从起始位置到目标位置,相当于每一个点都做了一个变换,点做变换相当于左乘一个变换矩阵,对于所有的点,变换矩阵是相同的,不用每个点都算一遍,只需要算一遍,然后对每个点都应用变换就行。
当然可以把方法1中的变换联立,求出总的变换矩阵。这样需要算两次角度,做两次矩阵的乘法。
还可以直接列方程求解。
假设变换矩阵是(只有旋转缩放,没有平移)
假设已经转换为以O为坐标原点的坐标。
p0(x0,y0)变换之后是p0'(x1,y1),即A · p0 = p0'。可以列出两个方程:
a * x0 + b * y0 = x1 ①
c * x0 + d * y0 = y1 ②
取与(x0,y0)垂直的向量(-y0,x0),变换之后的方向与(-y1,x1)一致,长度不变。
设变换之后为(-y1',x1'),有:
y1' = y1 * d / d'
x1' = x1 * d / d'
其中d、d'分别为变换前后的长度。
这样,可以得到另外两个方程:
-a * y0 + b * x0 = -y1' ③
-c * y0 + d * x0 = x1' ④
求解
① * y0 + ③ * x0 消掉a 得:
b = (x1 * y0 - y1' * x0) / (x0 * x0 + y0 * y0) ⑤
② * y0 + ④ * x0 消掉c 得:
d = (y1 * y0 + x1' * x0) / (x0 * x0 + y0 * y0) ⑥
⑤带入① 求出a
a = (x1 - b * y0) / x0
⑥带入② 求出c
c = (y1 - d * y0) / x0
变换矩阵参数abcd全求出来了。
x0 等于0的时候,我们可以用消掉bd来求出ac。
① * x0 - ③ * y0 消掉b 得:
a = (x1 * x0 + y1' * y0) / (x0 * x0 + y0 * y0)
② * x0 - ④ * y0 消掉d 得:
c = (y1 * x0 - x1' * y0) / (x0 * x0 + y0 * y0)
可以结合第一种和第三种方法,用第一种方法的逻辑找到变换矩阵,然后应用于所有的点。
一个小问题,认真分析,可以找到好多种方法。好的方法应该容易看懂,效率高,适应性强,易扩展。
看别人的代码,或者是自己,也经常会遇到第二种方法。虽然也解决了问题,但是不可取。
把问题抽象出来,经常会发现一些完全不相关的问题,其实是同一类问题。解决一个问题,其实是解决了一类问题。
做的多了,遇到的大多数问题都是解决过的,只需要套之前的模型就可以。省下来的精力用来学习新的知识,解决那些没有遇到过的问题。