問題引入
在標註尺寸時,我們會在雙向箭頭中心處的旁邊,繪製對應的文本提示。要繪製的文字位置看起來自然舒服,文本的繪製位置並不是簡單地從線段中心點偏移固定值。
計算過程分析
這裏,提供一種計算標註文本位置的計算方式。
如上圖,綠色的點是文本位置的起點,經觀察發現,文本左側中心點相對於線段中心點的位置遵循一定的規律:
線段起點終點向量角度 V 爲 (0,180]:
[提示文字左側中心點 P1 ]、[線段中點]、[P1在V在中點法向量投影點]成直角三角形:
短直角邊 a = text height
長直角邊 b = text length/2
直角斜邊 c
向量 V 右側法向量角度 Vf=(V-90)
a 和 c 的動態角度 k = (V-90)/90 * atan(b/a) 注:這裏k值粗略按等比計算
直角斜邊c =a/cos(k)
文字左側中心點 P1 向量 Vt = Vf + k
----k值初步分析----
V 爲 (0,90] 時,k <= 0
V 爲 (90,180] 時,k > 0
----特殊值驗證----
V 爲 90 時,k=0, Vt=Vf, c=a
V 爲 180 時,k= atan(b/a), Vt=Vf+atan(b/a), c=a/cos(atan(b/a))
最終座標:
P1.x = mx + cos(Vt)*c
P1.y = my + sin(Vt)*c
文字繪製座標:
Ptext.x = P1.x
Ptext.y = P1.y - a/2
代碼實現(參考)
//計算繪製文本時相對於線段(x1,y1),(x2,y2)中點的偏移點 void calculateTextOffsetForMiddlePt(double x1, double y1, double x2, double y2, int textLength, int textHeight, int& ptOffsetX, int& ptOffsetY) { /* 線段起點終點向量角度 V 爲 (0,180]: [提示文字左側中心點 P1 ]、[線段中點]、[P1在V在中點法向量投影點]成直角三角形: 短直角邊 a = text height 長直角邊 b = text length/2 直角斜邊 c 向量 V 右側法向量角度 Vf=(V-90) a 和 c 的動態角度 k = (V-90)/90 * atan(b/a) 注:這裏k值粗略按等比計算 直角斜邊c =a/cos(k) 文字左側中心點 P1 向量 Vt = Vf + k ----k值初步分析---- V 爲 (0,90] 時,k <= 0 V 爲 (90,180] 時,k > 0 ----特殊值驗證---- V 爲 90 時,k=0, Vt=Vf, c=a V 爲 180 時,k= atan(b/a), Vt=Vf+atan(b/a), c=a/cos(atan(b/a)) 最終座標: P1.x = mx + cos(Vt)*c P1.y = my + sin(Vt)*c 文字繪製座標: Ptext.x = P1.x Ptext.y = P1.y - a/2 */ //特殊情況 if (ZMath_IsEqual(x1, x2) && ZMath_IsEqual(y1, y2)) x2 -= 1.0; //重合時,把 (x1,y1),(x2,y2) 作爲 180度線段來計算 //確保:線段起點終點向量角度 V 爲(0, 180]: double V = 0.0; ZMath_GetVectorAngle(x1, y1, x2, y2, &V); if (ZMath_IsEqual(V, 0.0) || (V > 180.0 && !ZMath_IsEqual(V, 180.0))) std::swap(x1, x2), std::swap(y1, y2); ZMath_GetVectorAngle(x1, y1, x2, y2, &V); double Vradian = ZMath_AngleToRadian(V); //短直角邊 a = text height ;長直角邊 b = text length / 2 double a = textHeight; double b = textLength / 2; //向量 V 右側法向量角度 Vf=(V-90) double Vf = V - 90.0; double Vfradian = ZMath_AngleToRadian(Vf); //a 和 c 的動態角度 k = (V-90)/90 * atan(b/a) 注:這裏k值粗略按等比計算 double k = (V - 90) / 90 * atan(b / a); //直角斜邊c =a/cos(k) double c = a / cos(k); //文字左側中心點 P1 向量 Vt = Vf + k double Vtarget = Vfradian + k; //最終座標: //P1.x = mx + cos(Vt)*c //P1.y = my + sin(Vt)*c //所以偏移值爲 double offsetX = cos(Vtarget) * c; double offsetY = sin(Vtarget) * c * -1; /* 繪製時是以左上角爲原點,向上Y爲負,向下Y爲正,所以這裏 Y 值乘以 -1 */ //文字繪製座標: //Ptext.x = P1.x //Ptext.y = P1.y - a/2 ptOffsetX = (int)offsetX; ptOffsetY = (int)(offsetY + a / 2); /* 繪製時是以左上角爲原點,向上Y爲負,向下Y爲正,所以這裏 Y 值加上 a/2 */ }
上述代碼中:
ZMath_IsEqual 爲判斷兩個浮點數是否相等,精度爲 0.01
ZMath_GetVectorAngle 爲獲得線段的向量角度
ZMath_AngleToRadian 爲角度轉弧度