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)
var AC2 = (ox - bx) * (ox - bx) + (oy - by) * (oy - by)
var BC2 = (ax - bx) * (ax - bx) + (ay - by) * (ay - by)
var cosA = (AB2 + AC2 - BC2) / (2 * Math.sqrt(AB2 * AC2))
var A = Math.acos(cosA) * 180 / Math.PI
// y = k(x-x0)+y0 判断在直线上面还是下面
var k = rectW / rectH
if ((bx - rectX) / k + rectY < by) {
A = 360 - A
}
return A
}
//topLeft_RotateHandle
onMouseMove(mouse_x, mouse_y)
{
var A = getAngle(mouse_x, mouse_y)
rectA = rectW>0? A: -A
}
//topRight_RotateHandle
onMouseMove(mouse_x, mouse_y)
{
var A = getAngle(mouse_x, mouse_y)
var p = 2 * Math.atan2(rectW, rectH) * 180 / Math.PI
rectA = rectW>0? A - p: -A - p
}
//bottomLeft_RotateHandle
onMouseMove(mouse_x, mouse_y)
{
var A = getAngle(mouse_x, mouse_y)
var p = 2 * Math.atan2(rectW, rectH) * 180 / Math.PI + 180
rectA = rectW>0? A - p: -A - p
}
//bottomRight_RotateHandle
onMouseMove(mouse_x, mouse_y)
{
var A = getAngle(mouse_x, mouse_y)
A -= 180
rectA = rectW>0? A: -A
}
2 对角缩放&等比缩放
在PS中,按住shift拖动四角的控制点,可以进行等比缩放,旋转角度不变
2.1 数学建模求解
在上一节中,我们已经求出了旋转角rectA(
∠
B
A
C
∠BAC
∠BAC),以及旋转后的B点坐标$ C(Xc, Yc) $。现在根据可以鼠标坐标M,计算出拖移控制点P的坐标,然后算出缩放比例,从而算出缩放后的矩形基准(矩形B’D’E’F’为缩放后的矩形基准),及求B’坐标(rectX’, rectY’)及B’D’(rectW’)和B’F’(rectH’)
如图,紫色矩形是绿色矩形缩放后的图形,紫色矩形旋转-rectA度后即为矩形B’D’E’F’:
本节计算皆以B点作为锚点,对角点E点作为被拖动点,即缩放自由变换的右下角的点
转换为数学问题就是:
- 由图,已知 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, ∠ B A C = r e c t A ∠BAC=rectA ∠BAC=rectA, C ( X c , Y c ) C(Xc, Yc) C(Xc,Yc), M ( X m , Y m ) M(Xm, Ym) M(Xm,Ym)。
- 紫色矩形围绕A’逆时针旋转rectA度后即为矩形B’D’E’F’
- 过M点作AC的垂线,垂足为P。作A’为线段PC的中点,缩放比例 t = A ′ C A C t=\frac{A'C}{AC} t=ACA′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 ′ ) B'D'(rectW') B′D′(rectW′)和 B ′ F ′ ( r e c t H ′ ) B'F'(rectH') B′F′(rectH′)
2.2 求解过程
2.2.1 B’是直线BC上一点
由条件可知:
∠
B
A
C
=
∠
B
′
A
′
C
=
r
e
c
t
A
,
A
B
=
A
C
=
2
A
′
C
=
2
A
′
B
∠BAC = ∠B'A'C=rectA,AB=AC=2A'C=2A'B
∠BAC=∠B′A′C=rectA,AB=AC=2A′C=2A′B
所以,
△
A
B
C
∼
△
A
′
B
′
C
\triangle ABC \sim \triangle A'B'C
△ABC∼△A′B′C
可得:B’是直线BC上一点
因此,
t
=
A
′
C
A
C
=
B
′
C
B
C
t= \frac{A'C}{AC} = \frac{B'C}{BC}
t=ACA′C=BCB′C
易证:
△
C
B
F
∼
△
C
B
′
F
′
,
△
C
B
D
∼
△
C
B
′
D
′
\triangle CBF \sim \triangle CB'F',\triangle CBD \sim \triangle CB'D'
△CBF∼△CB′F′,△CBD∼△CB′D′
可得:
t
=
A
′
C
A
C
=
B
′
C
B
C
=
B
′
F
B
F
=
B
′
D
B
D
t= \frac{A'C}{AC} = \frac{B'C}{BC} = \frac{B'F}{BF} = \frac{B'D}{BD}
t=ACA′C=BCB′C=BFB′F=BDB′D
2.2.2 求P(Xp, Yp)
求直线
l
1
:
y
=
k
1
x
+
b
1
l_{1}:y = k_{1}x+b_{1}
l1:y=k1x+b1 与直线
l
2
:
y
=
k
2
x
+
b
2
l_{2}:y = k_{2}x+b_{2}
l2:y=k2x+b2 的交点
P
(
X
p
,
Y
p
)
P(Xp, Yp)
P(Xp,Yp),有公式:
X
p
=
b
2
−
b
1
k
1
−
k
2
Y
p
=
k
1
X
p
+
b
1
Xp = \frac{ b_{2} - b_{1}}{ k_{1}- k_{2}}\\ Yp = k_{1}Xp+b_{1}
Xp=k1−k2b2−b1Yp=k1Xp+b1
所以:
- k 1 = ( Y a − Y c ) / ( X a − X c ) k1 = (Ya - Yc) / (Xa - Xc) k1=(Ya−Yc)/(Xa−Xc)
- b 1 = Y c − k 1 ∗ X c b1 = Yc - k1 * Xc b1=Yc−k1∗Xc
- k 2 = − 1 / k 1 k2 = -1 / k1 k2=−1/k1
- b 2 = Y p − k 2 ∗ X p b2 = Yp - k2 * Xp b2=Yp−k2∗Xp
- X p = ( b 2 − b 1 ) / ( k 1 − k 2 ) Xp = (b2 - b1) / (k1 - k2) Xp=(b2−b1)/(k1−k2)
- Y p = k 1 ∗ X p + b 1 Yp = k1 * Xp + b1 Yp=k1∗Xp+b1
2.2.3 计算缩放比例t
因为
t
=
C
G
C
P
=
C
P
2
∗
A
C
=
X
p
−
X
c
2
∗
(
X
a
−
X
c
)
t = \frac{CG}{CP} = \frac{CP}{2*AC} = \frac{Xp-Xc}{2*(Xa-Xc)}
t=CPCG=2∗ACCP=2∗(Xa−Xc)Xp−Xc
- t = ( X p − X c ) / ( X a − X c ) / 2 t = (Xp - Xc) / (Xa - Xc) / 2 t=(Xp−Xc)/(Xa−Xc)/2
2.2.4 计算结果
由2.2.1和2.2.3可知: $ t= \frac{A’C}{AC} = \frac{B’C}{BC} = \frac{B’F}{BF} = \frac{B’D}{BD} = \frac{CG}{CP}= \frac{Xp-Xc}{2*(Xa-Xc)}$
所以:
B
′
(
r
e
c
t
X
′
,
r
e
c
t
Y
′
)
=
t
∗
(
C
−
B
)
+
C
B
′
D
′
=
t
∗
B
D
B
′
F
′
=
t
∗
B
F
B'(rectX', rectY') = t*(C-B) + C \\ B'D' = t*BD \\ B'F' = t*BF
B′(rectX′,rectY′)=t∗(C−B)+CB′D′=t∗BDB′F′=t∗BF
即
- r e c t X ′ = t ∗ ( r e c t X − X c ) + X c rectX' = t * (rectX - Xc) + Xc rectX′=t∗(rectX−Xc)+Xc
- r e c t Y ′ = t ∗ ( r e c t Y − Y c ) + Y c rectY' = t * (rectY - Yc) + Yc rectY′=t∗(rectY−Yc)+Yc
- r e c t W ′ = t ∗ r e c t W rectW' = t * rectW rectW′=t∗rectW
- r e c t H ′ = t ∗ r e c t H rectH' = t * rectH rectH′=t∗rectH
2.3 对于其他三点的处理
和旋转一样,对其他三点进行角度偏转就好了
B:
A
=
r
e
c
t
A
+
180
A = rectA + 180
A=rectA+180
D:
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
E:
A
=
r
e
c
t
A
A = rectA
A=rectA
F:
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
2.4 代码
//鼠标拖拽对角线缩放,计算图片缩放比例
function setScaleDiagonal(Xm, Ym, angle) {
//计算缩放基准点坐标C(旋转中心为A, B围绕A点旋转angle度到C)
var A = angle * Math.PI / 180
var Xa = rectX + rectW / 2
var Ya = rectY + rectH / 2
var Xc = (rectX - Xa) * Math.cos(A) - (rectY - Ya) * Math.sin(A) + Xa
var Yc = (rectX - Xa) * Math.sin(A) + (rectY - Ya) * Math.cos(A) + Ya
//鼠标坐标修正(过鼠标坐标对对角线作垂线,计算垂足P)
var k1 = (Ya - Yc) / (Xa - Xc)
var b1 = Yc - k1 * Xc
var k2 = -1 / k1
var b2 = Ym - k2 * Xm
var Xp = (b2 - b1) / (k1 - k2)
var Yp = k1 * Xp + b1
//计算缩放比例
var t = (Xp - Xc) / (Xa - Xc) / 2
//console.log("setScaleDiagonal", Xc, Yc, Xp ,Yp, A, t)
//应用到变换
rectX = t * (rectX - Xc) + Xc
rectY = t * (rectY - Yc) + Yc
rectW = t * rectW
rectH = t * rectH
}
//topLeft_ScaleHandle
onMouseMove(mouse_x, mouse_y)
{
setScaleDiagonal(mouse_x, mouse_y, rectA + 180)
}
//topRight_ScaleHandle
onMouseMove(mouse_x, mouse_y)
{
var A = 180 + rectA + (2 * Math.atan2(rectW, rectH) * 180 / Math.PI)
setScaleDiagonal(mouse_x, mouse_y, A)
}
//bottomLeft_ScaleHandle
onMouseMove(mouse_x, mouse_y)
{
var A = rectA + (2 * Math.atan2(rectW, rectH) * 180 / Math.PI)
setScaleDiagonal(mouse_x, mouse_y, A)
}
//bottomRight_ScaleHandle
onMouseMove(mouse_x, mouse_y)
{
setScaleDiagonal(mouse_x, mouse_y, rectA)
}
3 上下左右的缩放&不等比缩放
在PS中,按住shift拖动上下左右四边的控制点,可以进行不等比缩放,旋转角度不变
3.1 数学建模求解
在第一节中,我们已经求出了旋转角rectA(
∠
B
A
C
∠BAC
∠BAC),以及旋转后的B点坐标$ C(Xc, Yc) $。现在根据可以鼠标坐标M,计算出拖移控制点P的坐标,然后算出缩放比例t,从而算出缩放后的矩形基准(矩形B’D’E’F’为缩放后的矩形基准),及求B’坐标(rectX’, rectY’)及B’D’(rectW’)和B’F’(rectH’)
如图,紫色矩形是绿色矩形缩放后的图形,紫色矩形围绕A’旋转-rectA度后即为矩形B’D’E’F’:
本节计算皆以L点作为锚点,对称点L’点作为被拖动点,即缩放自由变换的右边的点
转换为数学问题就是:
- 由图,已知 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, ∠ B A C = r e c t A ∠BAC=rectA ∠BAC=rectA, C ( X c , Y c ) C(Xc, Yc) C(Xc,Yc), M ( X m , Y m ) M(Xm, Ym) M(Xm,Ym)。
- 紫色矩形围绕A’逆时针旋转rectA度后即为矩形B’D’E’F’
- 作CG中点L,过AL作直线交JK于L’
- 过M点作AL的垂线,垂足为P。作A’为线段PL的中点,缩放比例 t = L P L L ′ t=\frac{LP}{LL'} t=LL′LP
- 作直线BG、CD相交于H点,过点H作HI⊥BD,垂足为I,交B’D’于I’,连接FI’
- 求 B ′ ( r e c t X ′ , r e c t Y ′ ) B'(rectX', rectY') B′(rectX′,rectY′), B ′ D ′ ( r e c t W ′ ) B'D'(rectW') B′D′(rectW′)和 B ′ F ′ ( r e c t H ′ ) B'F'(rectH') B′F′(rectH′)
3.2 求解过程
3.2.1 B’是直线BH上一点
证明略(不知道怎么证,总之是对的)
所以, t = L P L L ′ = B ′ D ′ B D = B ′ F ′ B F t = \frac{LP}{LL'} = \frac{B'D'}{BD} = \frac{B'F'}{BF} t=LL′LP=BDB′D′=BFB′F′
3.2.2 L是直线HI上一点,求L(Xl, Yl)
证明略(不知道怎么证,总之是对的)
点L相当于BF的中点R围绕A旋转rectA度后的位置
所以
$ Xr = rectX - Xa \
Yr = Ya \
Xl = Xa + (Xr - Xa) * Math.cos(A) - (Yr - Ya)*sin A \
Yl = Ya + (Xr - Xa) * Math.sin(A) + (Yr - Ya)*cos A
$
化简后:
- X l = X a + ( r e c t X − X a ) ∗ M a t h . c o s ( A ) Xl = Xa + (rectX - Xa) * Math.cos(A) Xl=Xa+(rectX−Xa)∗Math.cos(A)
- Y l = Y a + ( r e c t X − X a ) ∗ M a t h . s i n ( A ) Yl = Ya + (rectX - Xa) * Math.sin(A) Yl=Ya+(rectX−Xa)∗Math.sin(A)
3.2.3 计算缩放比例t
参见2.2.2和2.2.3
求得
P
(
X
p
,
Y
p
)
P(Xp, Yp)
P(Xp,Yp)和
t
t
t
特别的:
当Ya=Yl时(rect=0或rect=180)
t
=
(
X
m
−
X
l
)
/
(
X
a
−
X
l
)
/
2
t = (Xm - Xl) / (Xa - Xl) /2
t=(Xm−Xl)/(Xa−Xl)/2
当Xa=Xl时(rect=90或rect=270)
t
=
(
Y
m
−
Y
l
)
/
(
Y
a
−
Y
l
)
/
2
t = (Ym - Yl) / (Ya - Yl) /2
t=(Ym−Yl)/(Ya−Yl)/2
3.2.4 计算结果
计算BI的长:
B
I
=
X
l
−
X
b
=
X
l
−
r
e
c
t
X
BI = Xl - Xb = Xl - rectX
BI=Xl−Xb=Xl−rectX
计算HI的长:
H
I
=
B
I
∗
t
a
n
(
∠
B
H
I
)
=
B
I
∗
t
a
n
(
π
−
r
e
c
t
A
2
)
HI = BI * tan(∠BHI) = BI * tan(\frac{\pi - rectA}{2})
HI=BI∗tan(∠BHI)=BI∗tan(2π−rectA)
所以:
- r e c t X ′ = r e c t X + ( 1 − t ) ∗ B I rectX' = rectX + (1 - t) * BI rectX′=rectX+(1−t)∗BI
- r e c t Y ′ = r e c t Y − ( 1 − t ) ∗ H I rectY' = rectY - (1 - t) * HI rectY′=rectY−(1−t)∗HI
- r e c t W ′ = r e c t W ∗ t rectW' = rectW * t rectW′=rectW∗t
- r e c t H ′ = r e c t H rectH' = rectH rectH′=rectH
3.3 左缩放、上缩放和下缩放
如下图,计算方法是一样的,但是基准点有变化,但都是为了计算BI和HI的长度
3.4 代码
var direction_left = 0
var direction_right = 1
var direction_top = 2
var direction_bottom = 3
//鼠标拖拽水平缩放,计算图片缩放比例
function setScaleXY(Xm, Ym, angle, direction) {
//计算缩放基准点坐标C(旋转中心为A, B围绕A点旋转angle度到C)
var A = angle * Math.PI / 180
var Xa = rectX + rectW / 2
var Ya = rectY + rectH / 2
//缩放基准点基准点
var Xl = 0
var Yl = 0
if(direction === direction_left)
{
Xl = Xa + (rectX + rectW - Xa) * Math.cos(A)
Yl = Ya + (rectX + rectW - Xa) * Math.sin(A)
}
else if(direction === direction_right)
{
Xl = Xa + (rectX - Xa) * Math.cos(A)
Yl = Ya + (rectX - Xa) * Math.sin(A)
}
else if(direction === direction_top)
{
Xl = Xa - (rectY + rectH - Ya) * Math.sin(A)
Yl = Ya + (rectY + rectH - Ya) * Math.cos(A)
}
else if(direction === direction_bottom)
{
Xl = Xa - (rectY - Ya) * Math.sin(A)
Yl = Ya + (rectY - Ya) * Math.cos(A)
}
//计算缩放比例
var t = 0
if(Xa != Xl && Ya != Yl)
{
//鼠标坐标修正(过鼠标坐标对对角线作垂线,计算垂足P)
var k1 = (Ya - Yl) / (Xa - Xl)
var b1 = Yl - k1 * Xl
var k2 = -1 / k1
var b2 = Ym - k2 * Xm
var Xp = (b2 - b1) / (k1 - k2)
//var Yp = k1 * Xp + b1
if(Xp === Xl || Xa === Xl) return
t = (Xp - Xl) / (Xa - Xl) /2
}
else if(Ya === Yl)
{
if(Xm === Xl || Xa === Xl) return
t = (Xm - Xl) / (Xa - Xl) /2
}
else if(Xa === Xl)
{
if(Ym === Yl || Ya === Yl) return
t = (Ym - Yl) / (Ya - Yl) /2
}
//应用变换
var BI = 0
if(direction === direction_left)
{
if(Ya === Yl)
{
rectX = rectX + (1 - t) * rectW
rectW = t * rectW
}
else
{
BI = (1 - t) * (Xl - rectX)
rectY = rectY + BI * Math.tan(A / 2)
rectX = rectX + BI
rectW = t * rectW
}
}
else if(direction === direction_right)
{
if(Ya === Yl)
{
rectW = t * rectW
}
else
{
BI = (1 - t) * (Xl - rectX)
rectY = rectY - BI * Math.tan((Math.PI - A) / 2)
rectX = rectX + BI
rectW = t * rectW
}
}
else if(direction === direction_top)
{
if(Xa === Xl)
{
rectY = rectY + (1 - t) * rectH
rectH = t * rectH
}
else
{
BI = (1 - t) * (Yl - rectY)
rectX = rectX - BI * Math.tan(A / 2)
rectY = rectY + BI
rectH = t * rectH
}
}
else if(direction === direction_bottom)
{
if(Xa === Xl)
{
rectH = t * rectH
}
else
{
BI = (1 - t) * (Yl - rectY)
rectX = rectX + BI * Math.tan((Math.PI - A) / 2)
rectY = rectY + BI
rectH = t * rectH
}
}
}
//四个方向共用,只需改变第三个参数即可
onMouseMove(mouse_x, mouse_y)
{
setScaleXY(mouse_x, mouse_y, rectA, direction_left)
}
4 完整实现代码
4.1 效果预览
4.2 代码
这是在QT qml界面实现的代码
import QtQuick 2.1
Item {
id: item
anchors.fill: parent
rotation: 0
property real widthScale: 1.0
property real heightScale: 1.0
property real aspectRatio: 0.0
property int handleSize: 10
property int borderSize: 2
property alias uiRectangle: uiRectangle
property color uiColor: Qt.rgba(255, 255, 255, 1)
property color handleColor: 'transparent'
//property color handleColor: Qt.rgba(0, 255, 0, 0.5)
property real rectX: 0
property real rectY: 0
property real rectW: 0
property real rectH: 0
property real rectA: 0
property int coursor: 0
property real mouse_x: 0
property real mouse_y: 0
property int direction_left: 0
property int direction_right: 1
property int direction_top: 2
property int direction_bottom: 3
property int state_none: 0
property int state_drag: 1
property int state_control_point: 2
property int state_rotate: 3
signal rectChanged()
signal updateCursor()
function image(path) {
return "file:///" + RESOURCE_PATH + "/icon/" + path
}
function transformPointX(x, y, sA, cA) {
return x * cA - y * sA + rectX + rectW / 2
}
function transformPointY(x, y, sA, cA) {
return x * sA + y * cA + rectY + rectH / 2
}
//更新UI
function updateRect() {
if(rectW > 0){uiRectangle.x = rectX; uiRectangle.width = rectW}
else{uiRectangle.x = rectX + rectW; uiRectangle.width = -rectW}
if(rectH > 0){uiRectangle.y = rectY; uiRectangle.height = rectH}
else{uiRectangle.y = rectY + rectH; uiRectangle.height = -rectH}
uiRectangle.rotation = rectA
//rectChanged() //发送信号通知上层更新滤镜参数
}
//更新控制点坐标
function updateHandle() {
var cx = rectW / 2
var cy = rectH / 2
var hs = handleSize
var hs2 = handleSize / 2
var xhs = handleSize
var xhs2 = handleSize / 2
var yhs = handleSize
var yhs2 = handleSize / 2
var A = rectA * Math.PI / 180
var sA = Math.sin(A)
var cA = Math.cos(A)
if(rectW < 0){xhs = -xhs; xhs2 = -xhs2}
if(rectH < 0){yhs = -yhs; yhs2 = -yhs2}
topLeft_RotateHandle.x = transformPointX(-cx - xhs, -cy - yhs, sA, cA) - hs
topLeft_RotateHandle.y = transformPointY(-cx - xhs, -cy - yhs, sA, cA) - hs
topRight_RotateHandle.x = transformPointX(cx + xhs, -cy - yhs, sA, cA) - hs
topRight_RotateHandle.y = transformPointY(cx + xhs, -cy - yhs, sA, cA) - hs
bottomLeft_RotateHandle.x = transformPointX(-cx - xhs, cy + yhs, sA, cA) - hs
bottomLeft_RotateHandle.y = transformPointY(-cx - xhs, cy + yhs, sA, cA) - hs
bottomRight_RotateHandle.x = transformPointX(cx + xhs, cy + yhs, sA, cA) - hs
bottomRight_RotateHandle.y = transformPointY(cx + xhs, cy + yhs, sA, cA) - hs
topLeft_ScaleHandle.x = transformPointX(-cx + xhs2, -cy + yhs2, sA, cA) - hs2
topLeft_ScaleHandle.y = transformPointY(-cx + xhs2, -cy + yhs2, sA, cA) - hs2
topRight_ScaleHandle.x = transformPointX(cx - xhs2, -cy + yhs2, sA, cA) - hs2
topRight_ScaleHandle.y = transformPointY(cx - xhs2, -cy + yhs2, sA, cA) - hs2
bottomLeft_ScaleHandle.x = transformPointX(-cx + xhs2, cy - yhs2, sA, cA) - hs2
bottomLeft_ScaleHandle.y = transformPointY(-cx + xhs2, cy - yhs2, sA, cA) - hs2
bottomRight_ScaleHandle.x = transformPointX(cx - xhs2, cy - yhs2, sA, cA) - hs2
bottomRight_ScaleHandle.y = transformPointY(cx - xhs2, cy - yhs2, sA, cA) - hs2
left_ScaleHandle.x = transformPointX(-cx + xhs2, 0, sA, cA) - hs2
left_ScaleHandle.y = transformPointY(-cx + xhs2, 0, sA, cA) - hs2
right_ScaleHandle.x = transformPointX(cx - xhs2, 0, sA, cA) - hs2
right_ScaleHandle.y = transformPointY(cx - xhs2, 0, sA, cA) - hs2
top_ScaleHandle.x = transformPointX(0, -cy + yhs2, sA, cA) - hs2
top_ScaleHandle.y = transformPointY(0, -cy + yhs2, sA, cA) - hs2
bottom_ScaleHandle.x = transformPointX(0, cy - yhs2, sA, cA) - hs2
bottom_ScaleHandle.y = transformPointY(0, cy - yhs2, sA, cA) - hs2
rectChanged() //发送信号通知上层更新滤镜参数
}
//初始化数据
function setHandles(rect, angle) {
rectX = Math.round(rect.x * widthScale)
rectY = Math.round(rect.y * heightScale)
rectW = Math.round(rect.width * widthScale)
rectH = Math.round(rect.height * heightScale)
rectA = angle
updateRect()
updateHandle()
}
//鼠标拖拽旋转,计算图片旋转角度
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)
var AC2 = (ox - bx) * (ox - bx) + (oy - by) * (oy - by)
var BC2 = (ax - bx) * (ax - bx) + (ay - by) * (ay - by)
var cosA = (AB2 + AC2 - BC2) / (2 * Math.sqrt(AB2 * AC2))
var A = Math.acos(cosA) * 180 / Math.PI
// y = k(x-x0)+y0 判断在直线上面还是下面
var k = rectW / rectH
if ((bx - rectX) / k + rectY < by) {
A = 360 - A
}
return A
}
//鼠标光标控制
function setCoursor(mx, my, state) {
var ox = rectX + rectW / 2
var oy = rectY + rectH / 2
var angle = mx === x? Math.PI / 2: Math.atan2(my - oy, mx - ox)
angle = angle*180 / Math.PI
angle += 22.5
if(state === state_control_point) angle += 90
angle = angle<0? angle + 360: angle
if(state === state_none) coursor = Qt.ArrowCursor
else if(state === state_drag) coursor = Qt.SizeAllCursor
else if(state === state_rotate || state === state_control_point)
{
if(0 < angle && angle <= 45){ coursor = Qt.SizeVerCursor }
else if(45 < angle && angle <= 90){ coursor = Qt.SizeBDiagCursor }
else if(90 < angle && angle <= 135){ coursor = Qt.SizeHorCursor }
else if(135 < angle && angle <= 180){ coursor = Qt.SizeFDiagCursor }
else if(180 < angle && angle <= 225){ coursor = Qt.SizeVerCursor }
else if(225 < angle && angle <= 270){ coursor = Qt.SizeBDiagCursor }
else if(270 < angle && angle <= 315){ coursor = Qt.SizeHorCursor }
else if(angle > 315 || angle <= 0){ coursor = Qt.SizeFDiagCursor }
else {coursor = Qt.ArrowCursor}
}
else coursor = Qt.ArrowCursor
positionMouseArea.cursorShape = coursor
topLeft_RotateHandle_MouseArea.cursorShape = coursor
topRight_RotateHandle_MouseArea.cursorShape = coursor
bottomLeft_RotateHandle_MouseArea.cursorShape = coursor
bottomRight_RotateHandle_MouseArea.cursorShape = coursor
topLeft_ScaleHandle_MouseArea.cursorShape = coursor
topRight_ScaleHandle_MouseArea.cursorShape = coursor
bottomLeft_ScaleHandle_MouseArea.cursorShape = coursor
bottomRight_ScaleHandle_MouseArea.cursorShape = coursor
left_ScaleHandle_MouseArea.cursorShape = coursor
right_ScaleHandle_MouseArea.cursorShape = coursor
top_ScaleHandle_MouseArea.cursorShape = coursor
bottom_ScaleHandle_MouseArea.cursorShape = coursor
updateCursor() //发送信号通知上层更新光标图标
}
//鼠标拖拽对角线缩放,计算图片缩放比例
function setScaleDiagonal(Xm, Ym, angle) {
//计算缩放基准点坐标C(旋转中心为A, B围绕A点旋转angle度到C)
var A = angle * Math.PI / 180
var Xa = rectX + rectW / 2
var Ya = rectY + rectH / 2
var Xc = (rectX - Xa) * Math.cos(A) - (rectY - Ya) * Math.sin(A) + Xa
var Yc = (rectX - Xa) * Math.sin(A) + (rectY - Ya) * Math.cos(A) + Ya
//鼠标坐标修正(过鼠标坐标对对角线作垂线,计算垂足P)
var k1 = (Ya - Yc) / (Xa - Xc)
var b1 = Yc - k1 * Xc
var k2 = -1 / k1
var b2 = Ym - k2 * Xm
var Xp = (b2 - b1) / (k1 - k2)
var Yp = k1 * Xp + b1
//计算缩放比例
var t = (Xp - Xc) / (Xa - Xc) / 2
//console.log("setScaleDiagonal", Xc, Yc, Xp ,Yp, A, t)
//应用到变换
rectX = t * (rectX - Xc) + Xc
rectY = t * (rectY - Yc) + Yc
rectW = t * rectW
rectH = t * rectH
}
//鼠标拖拽水平缩放,计算图片缩放比例
function setScaleXY(Xm, Ym, angle, direction) {
//计算缩放基准点坐标C(旋转中心为A, B围绕A点旋转angle度到C)
var A = angle * Math.PI / 180
var Xa = rectX + rectW / 2
var Ya = rectY + rectH / 2
//缩放基准点基准点
var Xl = 0
var Yl = 0
if(direction === direction_left)
{
Xl = Xa + (rectX + rectW - Xa) * Math.cos(A)
Yl = Ya + (rectX + rectW - Xa) * Math.sin(A)
}
else if(direction === direction_right)
{
Xl = Xa + (rectX - Xa) * Math.cos(A)
Yl = Ya + (rectX - Xa) * Math.sin(A)
}
else if(direction === direction_top)
{
Xl = Xa - (rectY + rectH - Ya) * Math.sin(A)
Yl = Ya + (rectY + rectH - Ya) * Math.cos(A)
}
else if(direction === direction_bottom)
{
Xl = Xa - (rectY - Ya) * Math.sin(A)
Yl = Ya + (rectY - Ya) * Math.cos(A)
}
//计算缩放比例
var t = 0
if(Xa != Xl && Ya != Yl)
{
//鼠标坐标修正(过鼠标坐标对对角线作垂线,计算垂足P)
var k1 = (Ya - Yl) / (Xa - Xl)
var b1 = Yl - k1 * Xl
var k2 = -1 / k1
var b2 = Ym - k2 * Xm
var Xp = (b2 - b1) / (k1 - k2)
//var Yp = k1 * Xp + b1
if(Xp === Xl || Xa === Xl) return
t = (Xp - Xl) / (Xa - Xl) /2
}
else if(Ya === Yl)
{
if(Xm === Xl || Xa === Xl) return
t = (Xm - Xl) / (Xa - Xl) /2
}
else if(Xa === Xl)
{
if(Ym === Yl || Ya === Yl) return
t = (Ym - Yl) / (Ya - Yl) /2
}
//应用变换
var BI = 0
if(direction === direction_left)
{
if(Ya === Yl)
{
rectX = rectX + (1 - t) * rectW
rectW = t * rectW
}
else
{
BI = (1 - t) * (Xl - rectX)
rectY = rectY + BI * Math.tan(A / 2)
rectX = rectX + BI
rectW = t * rectW
}
}
else if(direction === direction_right)
{
if(Ya === Yl)
{
rectW = t * rectW
}
else
{
BI = (1 - t) * (Xl - rectX)
rectY = rectY - BI * Math.tan((Math.PI - A) / 2)
rectX = rectX + BI
rectW = t * rectW
}
}
else if(direction === direction_top)
{
if(Xa === Xl)
{
rectY = rectY + (1 - t) * rectH
rectH = t * rectH
}
else
{
BI = (1 - t) * (Yl - rectY)
rectX = rectX - BI * Math.tan(A / 2)
rectY = rectY + BI
rectH = t * rectH
}
}
else if(direction === direction_bottom)
{
if(Xa === Xl)
{
rectH = t * rectH
}
else
{
BI = (1 - t) * (Yl - rectY)
rectX = rectX + BI * Math.tan((Math.PI - A) / 2)
rectY = rectY + BI
rectH = t * rectH
}
}
}
Rectangle {
id: uiRectangle
color: 'transparent' //Qt.rgba(255, 0, 0, 0.5)
border.width: borderSize
border.color: uiColor
rotation: 0
MouseArea {
id: positionMouseArea
anchors.fill: parent
hoverEnabled: true
drag.target: uiRectangle
onPositionChanged: {
if(pressedButtons === Qt.LeftButton)
{
rectX = rectW > 0? uiRectangle.x: uiRectangle.x - rectW
rectY = rectH > 0? uiRectangle.y: uiRectangle.y - rectH
updateRect()
}
setCoursor(0, 0, state_drag)
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onExited:{setCoursor(0, 0, state_none)}
}
Image {
source: image("rotate_arrow_tl.png")
x: handleSize - this.width
y: handleSize - this.height
}
Image {
source: image("rotate_arrow_tr.png")
x: uiRectangle.width - handleSize
y: handleSize - this.height
}
Image {
source: image("rotate_arrow_bl.png")
x: handleSize - this.width
y: uiRectangle.height - handleSize
}
Image {
source: image("rotate_arrow_br.png")
x: uiRectangle.width - handleSize
y: uiRectangle.height - handleSize
}
Rectangle {
color: uiColor
x: (uiRectangle.width - handleSize) / 2
y: (uiRectangle.height - handleSize) / 2
width: handleSize
height: handleSize
radius: width / 2
}
Rectangle {
color: uiColor
x: 0
y: 0
width: handleSize
height: handleSize
}
Rectangle {
color: uiColor
x: uiRectangle.width - handleSize
y: 0
width: handleSize
height: handleSize
}
Rectangle {
color: uiColor
x: 0
y: uiRectangle.height - handleSize
width: handleSize
height: handleSize
}
Rectangle {
color: uiColor
x: uiRectangle.width - handleSize
y: uiRectangle.height - handleSize
width: handleSize
height: handleSize
}
Rectangle {
color: uiColor
x: (uiRectangle.width - handleSize) / 2
y: 0
width: handleSize
height: handleSize
}
Rectangle {
color: uiColor
x: (uiRectangle.width - handleSize) / 2
y: uiRectangle.height - handleSize
width: handleSize
height: handleSize
}
Rectangle {
color: uiColor
x: 0
y: (uiRectangle.height - handleSize) / 2
width: handleSize
height: handleSize
}
Rectangle {
color: uiColor
x: uiRectangle.width - handleSize
y: (uiRectangle.height - handleSize) / 2
width: handleSize
height: handleSize
}
}
Rectangle {
id: topLeft_RotateHandle
color: handleColor
width: handleSize * 2
height: handleSize * 2
MouseArea {
id: topLeft_RotateHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_rotate)
if(pressedButtons === Qt.LeftButton)
{
var A = getAngle(mouse_x, mouse_y)
rectA = rectW>0? A: -A
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onExited:{setCoursor(0, 0, state_none)}
}
}
Rectangle {
id: topRight_RotateHandle
color: handleColor
width: handleSize * 2
height: handleSize * 2
MouseArea {
id: topRight_RotateHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_rotate)
if(pressedButtons === Qt.LeftButton)
{
var A = getAngle(mouse_x, mouse_y)
var p = 2 * Math.atan2(rectW, rectH) * 180 / Math.PI
rectA = rectW>0? A - p: -A - p
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onExited:{setCoursor(0, 0, state_none)}
}
}
Rectangle {
id: bottomLeft_RotateHandle
color: handleColor
width: handleSize * 2
height: handleSize * 2
MouseArea {
id: bottomLeft_RotateHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_rotate)
if(pressedButtons === Qt.LeftButton)
{
var A = getAngle(mouse_x, mouse_y)
var p = 2 * Math.atan2(rectW, rectH) * 180 / Math.PI + 180
rectA = rectW>0? A - p: -A - p
updateRect()
}
}
onReleased: {
updateHandle()
setCoursor(0, 0, state_none)
}
onExited:{setCoursor(0, 0, state_none)}
}
}
Rectangle {
id: bottomRight_RotateHandle
color: handleColor
width: handleSize * 2
height: handleSize * 2
MouseArea {
id: bottomRight_RotateHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_rotate)
if(pressedButtons === Qt.LeftButton)
{
var A = getAngle(mouse_x, mouse_y)
A -= 180
rectA = rectW>0? A: -A
updateRect()
}
}
onReleased: {
//rectA = 90
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onExited:{setCoursor(0, 0, state_none)}
}
}
Rectangle {
id: topLeft_ScaleHandle
color: handleColor
width: handleSize
height: handleSize
MouseArea {
id: topLeft_ScaleHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
if(pressedButtons === Qt.LeftButton)
{
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setScaleDiagonal(mouse_x, mouse_y, rectA + 180)
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onEntered: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_control_point)
}
}
}
Rectangle {
id: topRight_ScaleHandle
color: handleColor
width: handleSize
height: handleSize
MouseArea {
id: topRight_ScaleHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
if(pressedButtons === Qt.LeftButton)
{
var A = 180 + rectA + (2 * Math.atan2(rectW, rectH) * 180 / Math.PI)
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setScaleDiagonal(mouse_x, mouse_y, A)
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onEntered: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_control_point)
}
}
}
Rectangle {
id: bottomLeft_ScaleHandle
color: handleColor
width: handleSize
height: handleSize
MouseArea {
id: bottomLeft_ScaleHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
if(pressedButtons === Qt.LeftButton)
{
var A = rectA + (2 * Math.atan2(rectW, rectH) * 180 / Math.PI)
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setScaleDiagonal(mouse_x, mouse_y, A)
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onEntered: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_control_point)
}
}
}
Rectangle {
id: bottomRight_ScaleHandle
color: handleColor
width: handleSize
height: handleSize
MouseArea {
id: bottomRight_ScaleHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
if(pressedButtons === Qt.LeftButton)
{
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setScaleDiagonal(mouse_x, mouse_y, rectA)
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onEntered: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_control_point)
}
}
}
Rectangle {
id: left_ScaleHandle
color: handleColor
width: handleSize
height: handleSize
MouseArea {
id: left_ScaleHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
if(pressedButtons === Qt.LeftButton)
{
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setScaleXY(mouse_x, mouse_y, rectA, direction_left)
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onEntered: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_control_point)
}
}
}
Rectangle {
id: right_ScaleHandle
color: handleColor
width: handleSize
height: handleSize
MouseArea {
id: right_ScaleHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
if(pressedButtons === Qt.LeftButton)
{
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setScaleXY(mouse_x, mouse_y, rectA, direction_right)
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onEntered: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_control_point)
}
}
}
Rectangle {
id: top_ScaleHandle
color: handleColor
width: handleSize
height: handleSize
MouseArea {
id: top_ScaleHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
if(pressedButtons === Qt.LeftButton)
{
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setScaleXY(mouse_x, mouse_y, rectA, direction_top)
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onEntered: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_control_point)
}
}
}
Rectangle {
id: bottom_ScaleHandle
color: handleColor
width: handleSize
height: handleSize
MouseArea {
id: bottom_ScaleHandle_MouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
if(pressedButtons === Qt.LeftButton)
{
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setScaleXY(mouse_x, mouse_y, rectA, direction_bottom)
updateRect()
}
}
onReleased: {
updateRect()
updateHandle()
setCoursor(0, 0, state_none)
}
onEntered: {
mouse_x = mouseX + parent.x
mouse_y = mouseY + parent.y
setCoursor(mouse_x, mouse_y, state_control_point)
}
}
}
}