• 矢量旋转,分别以三角变换,矩阵变换实现,C#源码。


    最近用到了图形旋转,花了不少时间查找材料,编码测试。而且还用到了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 * sinB
    y1 = 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撇,输入法输不上,干嘛不画个O1啊,砸到自己脚啦),我们是平移了一个位移。所以我们如果想旋转P1后得到P2(呵呵,你懂的),

    我们应当这样做:第一步:将O1移到O。  第二步:将坐标系旋转。第三步:将O1移回原来位置。 这样P2就完成了在坐标系 X1O1Y1中,从P1点转到P2点的任务。

    第一步:平移变换:

    第二步:旋转变换:

    第三步:平移变换:

    注意,矩阵乘法是讲究先后次序的,如同领导人的排名,不可乱来哦。 呼。。。小工告成,同志们吉祥。

  • 相关阅读:
    Captura
    食用Win系统自带的PowerShell登录服务器
    uTools
    图片镜像缓存服务
    博客园自定义网站ico
    超星图床
    教程翻译-理解基于矢量场寻路算法
    CentOS防火墙命令
    CentOS7的vsftpd安装和配置
    Unity网路编程-TCP实现细节备忘
  • 原文地址:https://www.cnblogs.com/hongsheng001/p/4127458.html
Copyright © 2020-2023  润新知