標籤:style blog http ar color os 使用 sp for
最近用到了圖形旋轉,花了不少時間尋找材料,編碼測試。而且還用到了20年前老師教給的三角函數,還有大學裡面早已淡忘的矩陣運算。
呵呵,整理一下把,希望對大家有些協助。
功能: 已知向量OP,順時針旋轉α度,求P2點的座標。
根據三角函數,我們可以很自然的寫出:
P2.X = O.X + (int)(Math.Cos(alpha) * r) ;
P2.Y = O.Y + (int)(Math.Sin(alpha) * r) ; //哦,代碼的顏色怎麼不變呢? 噢,因為我剛開始在部落格園寫部落格,所有不會使,呵呵,懶得理它了。
可惜,這樣寫是不對的。 原因嘛,一共有四個象限啊,當P1在不同的象限時候,結果是不同的。 論證過程。。。省掉N字。
嗯,先說個定理,可能大家會用到:
如上,半徑 r 知道了,旋轉角也知道了,P1P2的長度是多少?
呵呵,獻醜了,手繪的。
這個定理,可以幫我們得到邊長,或者得到某邊對應角的大小。 在我的項目裡面用到了。
那麼上面的P2點,究竟是多少? 哦,我就省去複雜的推理過程了,直接出代碼:
private Point GetNewPoint(double Rate, Point cirPos, Point startPos) { double Rage2 = Rate;// / 180 * Math.PI; //B點繞A點轉R度得到C點座標,flag: 順時針1,反時針-1:B是轉的點,A是圓心 //C.X=(B.X-A.X)*COS(R*flag)-(B.Y-A.Y)*Sin(R*flag); //C.Y= (B.Y-A.Y)*COS(R*flag)+(B.X-A.X)*sin(R*flag); //轉的點座標-圓心座標 //圓心座標+計算座標=新位置的座標 double t1 = (startPos.X - cirPos.X) * Math.Cos(Rage2); double t2 = (startPos.Y - cirPos.Y) * Math.Sin(Rage2); int newx = (int)(t1 - t2); int newy = (int)((startPos.Y - cirPos.Y) * Math.Cos(Rage2) + (startPos.X - cirPos.X) * Math.Sin(Rage2)); Point newpoint = new Point(cirPos.X + newx, cirPos.Y + newy); return newpoint; }
上面這個公式,不是個人推出來的,是教科書上的結論:
x0=|R|*cosA y0=|R|*sinA x1 =|R|*cos(A +B) y1=|R|*sin(A+B)所以將x1,y1展開,有:x1=|R|*(cosAcosB-sinAsinB)y1=|R|*(sinAcosB+cosAsinB) 把 cosA = x0/|R| sinA = y0/|R| 代入上面的式子,得到 x1 = |R|*(x0*cosB/|R|-y0*sinB/|R|) y1 = |R|*(y0*cosB/|R|+x0*sinB/|R|)最終結果:x1 = x0 * cosB - y0 * sinBy1 = x0 * sinB + y0 * cosB 呵呵,三角函數方式,代碼與原理交代完畢。 下面就是矩陣了。通常在二維中,我們使用的是三階矩陣,為什麼呢? 因為二階不夠使啊,為什麼呢? 因為二階只能實現:縮放,旋轉,對稱變換,無法實現平移啊,為什麼呢? 因為,二階不是萬能神啊,為什麼呢?萬能神是不問為什麼的哈。。無語。 程式中矩陣的樣子是這樣的:我們敲一個C#中的矩陣看看:
namespace System.Drawing.Drawing2D{ // // 摘要: // 封裝表示幾何變換的 3 x 3 仿射矩陣。此類不能被繼承。 public sealed class Matrix : MarshalByRefObject, IDisposable { // // 摘要: // 將 System.Drawing.Drawing2D.Matrix 類的一個新執行個體初始化為單位矩陣。 public Matrix(); public Matrix(RectangleF rect, PointF[] plgpts); // // 摘要: // 使用指定的元素初始化 System.Drawing.Drawing2D.Matrix 類的新執行個體。 // // 參數: // m11: // 新的 System.Drawing.Drawing2D.Matrix 的第一行和第一列中的值。 // // m12: // 新的 System.Drawing.Drawing2D.Matrix 的第一行和第二列中的值。 // // m21: // 新的 System.Drawing.Drawing2D.Matrix 的第二行和第一列中的值。 // // m22: // 新的 System.Drawing.Drawing2D.Matrix 的第二行和第二列中的值。 // // dx: // 新的 System.Drawing.Drawing2D.Matrix 的第三行和第一列中的值。 // // dy: // 新的 System.Drawing.Drawing2D.Matrix 的第三行和第二列中的值。 public Matrix(float m11, float m12, float m21, float m22, float dx, float dy);
就是說,微軟的matrix,我們可以定義為 x1 y1,x2 y2,x3 y3。 而微軟會自動給我們補充第三列:0,0,1。這個查一下msdn就看到了。
哦,還是先上代碼把,看看我們用matrix如何?這種旋轉。
private void MyTest() { Point pa = new Point(100, 100); Point pb = new Point(200, 100); DrawPoint(Brushes.Red, pa); DrawPoint(Brushes.Black, pb); DrawLine(Pens.Red, pa, pb); double ang = Math.PI * 30 / 180; Matrix m = new Matrix(); m.Translate(-pa.X, -pa.Y); m.Rotate(30, MatrixOrder.Append); m.Translate(pa.X, pa.Y, MatrixOrder.Append); Point[] ps = new Point[1]; ps[0] = pb; m.TransformPoints(ps); DrawPoint(Brushes.Blue, ps[0]); Graphics g = Graphics.FromHwnd(this.Handle); Rectangle rect = new Rectangle(pa, new Size(0, 0)); rect.Inflate(100, 100); g.DrawArc(Pens.Red, rect, 0, 30); DrawPoint(Brushes.Green, ps[0]); DrawLine(Pens.Red, pa, ps[0]); } private void DrawPoint(Brush b, Point p) { Rectangle rect = new Rectangle(p, new Size(5, 5)); Graphics g = Graphics.FromHwnd(this.Handle); g.FillRectangle(b, rect); } private void DrawLine(Pen pen, Point p1, Point p2) { Graphics g = Graphics.FromHwnd(this.Handle); g.DrawLine(pen, p1, p2); }
這段代碼就可以實現第一幅圖的效果了。
我們在百度上查資料,可以看到:縮放矩陣,旋轉矩陣,平移矩陣。 但是,如果進行一個點,繞另外一個點進行旋轉,那麼這個操作就是一個複合矩陣操作,是組合變換方式。
在實際應用過程中,我們在使用word、ppt、photoshop,經常會把圖片旋轉一下,可能是以左上方為圓心,旋轉20度,或者以圖片中心點為圓心旋轉。問題來了,我們用的圓心,不同於螢幕左上方的圓心哦,圖片的四個角點的座標,都是基於螢幕左上方圓心的座標。這個怎麼整?
是的,從O,到O1(哈哈,我上面畫了O撇,IME輸不上,幹嘛不畫個O1啊,砸到自己腳啦),我們是平移了一個位移。所以我們如果想旋轉P1後得到P2(呵呵,你懂的),
我們應當這樣做:第一步:將O1移到O。 第二步:將座標系旋轉。第三步:將O1移回原來位置。 這樣P2就完成了在座標系 X1O1Y1中,從P1點轉到P2點的任務。
第一步:平移變換:
第二步:旋轉變換:
第三步:平移變換:
注意,矩陣乘法是講究先後次序的,如同領導人的排名,不可亂來哦。 呼。。。小工告成,同志們吉祥。
向量旋轉,分別以三角變換,矩陣變換實現,C#源碼。