巧用 QLineF 從 QTransform 提取角度

永不停转發表於2024-07-25

我們在對 QGraphicsItem 進行變換時,QT 提供了很多便捷的方法。但當我們想獲取當前變換的角度時卻有些困難,因為 QTransform 沒有提供獲取角度的方法。在文章Qt 從 QTransform 逆向解出 Translate/Scale/Rotate(平移/縮放/旋轉)分析 分析過,使用 QTransform 進行多種變換組合後,由於組合順序不能預知、組合數量不能預知,很難從 QTransform 矩陣中推算出當前變換的角度。即使推算出來,由於切變、縮放等變換的影響,推算出的角度也不準確。結合 QLineF 可以有個簡便的方式來倒推變換角度。

一、從 QGraphicsItem 的變換說起

對 QGraphicsItem 進行變換有三種方式:

1. 使用 setRotation() 或 setScale();
2. 使用 setTransform();
3. 使用 setTransformations();

如果同時使用多種方式,變換效果將進行疊加。變換按照固定的順序進行:

  • 第一步:應用 transform() 指定的變換;
  • 第二步:應用 transformations() 指定的變換;
  • 第三步:應用 rotation(), transformOriginPoint() 指定的變換;
  • 第四步:應用 scale(), transformOriginPoint() 指定的變換;

如果使用 setTransform() 指定旋轉角度 A,再使用 setRotation() 指定旋轉角度 B,那麼 QGraphicsItem 旋轉的角度就是 A + B。使用 rotation() 獲取的角度值是 B;角度 A 只能從 transform() 中推算。然後使用 resetTransform() 復位的是 transform(),rotation() 中的角度仍然保留,那麼此時 QGraphicsItem 旋轉的角度就是 B 。也就是說三種變換方式數值的變換相互不影響。

對於有父子關係的 QGraphicsItem 父專案的變換將累加到子專案(即使在父專案中使用的是setRotation()方式進行的變換,疊加到子專案時只能從transform中獲取該角度,並不會影響到子專案的 rotation()),子專案透過方法 sceneTransform() 獲取所有累加的變換,透過 scenePos() 獲取子專案在 scene 上的座標。

二、解析 QGraphicsItem 當前旋轉的角度

  1. 直接作用於 QGraphicsItem 的角度

不考慮父節專案對 QGraphicsItem 的影響,或者 QGraphicsItem 沒有父專案時,計算 QGraphicsItem 當前旋轉的角度,需要分別考慮 setRotation()、setTransform() 和 setTransformations() 設定的角度,分別計算後再進行累加即可。如果 QGraphicsItem 沒有父專案,可以使用 sceneTransform() 獲取所有累加變換後的 transform,從 transform 推算角度。

  1. 有父專案的 QGraphicsItem 的角度

考慮父專案對 QGraphicsItem 的變換影響,直接使用 sceneTransform() 獲取所有累加變換後的 transform,再推算角度。

三、使用 QLineF 從 transform 提取角度

QLineF 提供的方法 angleTo() 可以幫助我們方便的計算2條直線間的夾角。要從 transform 中計算角度,可以先構建一條直線,然後使用 QTransform 的 map() 方法對直線進行變換。變換後的直線與原直線間的夾角就是 transform 旋轉的角度。示例程式碼如下:

QTransform trans = item->sceneTransform();
QLineF line1{{0,0},{1,0}};
QLineF line2 = trans.map(line1);
qreal angle = line2.angleTo(line1)

注意:如果 transform 中執行過 shear 變換,該方法推算出來的角度不是原來設定的角度。

相關文章