PhotoShop中的自由变换UI
PS的自由变换UI如下
1. 旋转
如上图,旋转是在ps的自由变换中最常见的操作之一,灰色矩形经过旋转变为绿色矩形
1.1 数学建模求解
一个矩形的变换由以下参数记录左上角坐标(rectX, rectY),矩形宽高(rectW, rectH),矩形旋转角度(rectA,此处的旋转角是相对于B点的)。把图形旋转的变换进行数学建模,得到下面的图:
本节计算皆以B点作为被拖动点,即自由变换的左上角的点
其中:
- BDEF为要变换的矩形基准,点A是对角线的交点,B(rectX, rectY),BD=rectW,BF=rectH,∠BAM=rectA
- M(Xm, Ym)点是鼠标坐标(拖动B点围绕A点旋转rectA 度后的位置),连接AM交圆A于点C(A即旋转中心,C即旋转基准点)
转换为数学问题就是:
- 由图,已知 B ( r e c t X , r e c t Y ) B(rectX, rectY) B(rectX,rectY), B D = r e c t W BD=rectW BD=rectW, B F = r e c t H BF=rectH BF=rectH,$M(Xm, Ym) $。
- 求rectA,即求: ∠ B A C ∠BAC ∠BAC
1.2 求解过程
1.2.1 求A(Xa, Ya)
已知矩形顶点和宽高,求对角线交点
- X a = r e c t X + r e c t W / 2 Xa = rectX + rectW / 2 Xa=rectX+rectW/2
- Y a = r e c t Y + r e c t H / 2 Ya = rectY + rectH / 2 Ya=rectY+rectH/2
1.2.2 求rectA(∠BAC)
有三点ABC,求∠BAC,有公式:
c o s A = A B 2 + A B 2 − B C 2 2 ∗ A B ∗ A C cos A =\frac{AB^{2}+AB^{2}-BC^{2}}{2*AB*AC} cosA=2∗AB∗ACAB2+AB2−BC2
所以:
- A B 2 AB^{2} AB2 = ( X a − r e c t X ) 2 (Xa - rectX)^{2} (Xa−rectX)2 + ( Y a − r e c t Y ) 2 (Ya - rectY)^{2} (Ya−rectY)2
- A M 2 AM^{2} AM2 = ( X a − X m ) 2 (Xa - Xm)^{2} (Xa−Xm)2 + ( Y a − Y m ) 2 (Ya - Ym)^{2} (Ya−Ym)2
- B M 2 BM^{2} BM2 = ( X m − r e c t X ) 2 (Xm - rectX)^{2} (Xm−rectX)2 + ( Y m − r e c t Y ) 2 (Ym - rectY)^{2} (Ym−rectY)2
- c o s A = ( A B 2 + A B 2 − B M 2 ) / ( 2 ∗ A B 2 ∗ A M 2 ) cos A = (AB^{2}+AB^{2}-BM^{2})/(2*\sqrt{AB^{2}*AM^{2}}) cosA=(AB2+AB2−BM2)/(2∗AB2∗AM2)
- r e c t A = a c o s ( c o s A ) ∗ 180 / π rectA = acos(cosA) * 180 / \pi rectA=acos(cosA)∗180/π
判断象限
因为cos无法判断计算出来的角是否大于180度,所以需要根据鼠标位置判断角度
如图, ∠ B A C 1 = 150 ° ∠BAC_{1} = 150° ∠BAC1=150°, ∠ B A C 2 = 210 ° ∠BAC_{2} = 210° ∠BAC2=210°,但根据夹角公式算出来的cos是一样的,都是 3 / 2 \sqrt{3}/2 3/2
判断的方式实际上也很简单,根据M的坐标判断M在直线AB上面还是下面即可
- l A B : y = r e c t H r e c t W ( x − r e c t X ) + r e c t Y l_{AB}: y = \frac{rectH}{rectW}(x - rectX) + rectY lAB:y=rectWrectH(x−rectX)+rectY
- 把 M ( X m , Y m ) M(Xm, Ym) M(Xm,Ym)代入方程即可
if ((rectH / rectW)*(Xm - rectX) + rectY < Ym) {
rectA = 360 - rectA
}
1.2.3 求C(Xc, Yc)
点 ( x , y ) (x, y) (x,y)围绕点 ( x 0 , y 0 ) (x_{0}, y_{0}) (x0,y0)旋转 α α α度,有公式:
x ′ = ( x − x 0 ) c o s α − ( y − y 0 ) s i n α + x 0 y ′ = ( x − x 0 ) s i n α + ( y − y 0 ) c o s α + y 0 x' = (x - x_{0})cos α - (y - y_{0})sin α + x_{0} \\ y' = (x - x_{0})sin α + (y - y_{0})cos α + y_{0} x′=(x−x0)cosα−(y−y0)sinα+x0y′=(x−x0)sinα+(y−y0)cosα+y0
所以:
- A = r e c t A ∗ π / 180 A = rectA * \pi / 180 A=rectA∗π/180
- X c = ( r e c t X − X a ) ∗ c o s A − ( r e c t Y − Y a ) ∗ s i n A + X a Xc = (rectX - Xa)*cos A - (rectY - Ya)*sin A + Xa Xc=(rectX−Xa)∗cosA−(rectY−Ya)∗sinA+Xa
- Y c = ( r e c t X − X a ) ∗ s i n A + ( r e c t Y − Y a ) ∗ c o s A + Y a Yc = (rectX - Xa)*sin A + (rectY - Ya)*cos A + Ya Yc=(rectX−Xa)∗sinA+(rectY−Ya)∗cosA+Ya
1.3 对于D、E、F点的处理
从数学建模图可轻易看出,D、E、F三点与B共圆,所以计算这三点旋转后的坐标,只需要在计算前把rectA加上对角线的夹角即可
B: A = r e c t A A = rectA A=rectA
D: A = r e c t A − 2 ∗ a t a n ( r e c t W / r e c t H ) ∗ π / 180 A = rectA - 2*atan(rectW/rectH) * \pi / 180 A=rectA−2∗atan(rectW/rectH)∗π/180
E: A = r e c t A − 180 A = rectA - 180 A=rectA−180
F: A = r e c t A − 180 − 2 ∗ a t a n ( r e c t W / r e c t H ) ∗ π / 180 A = rectA - 180 - 2*atan(rectW/rectH) * \pi / 180 A=rectA−180−2∗atan(rectW/rectH)∗π/180
1.4 代码
//鼠标拖拽旋转,计算图片旋转角度
function getAngle(mx, my) {
var ox = rectX + rectW / 2
var oy = rectY + rectH / 2
var ax = rectX
var ay = rectY
var bx = mx
var by = my
// cosA = (AB^2+AC^2-BC^2)/(2*AB*AC) 三点求夹角
var AB2 = (ax - ox) * (ax - ox) + (ay - oy) * (ay - oy