代码之家  ›  专栏  ›  技术社区  ›  Виталий Ефимов

绘制穿过3个点的椭圆

  •  0
  • Виталий Ефимов  · 技术社区  · 7 年前

    我有下一个任务-通过3点绘制椭圆(如图所示)。 ellipse .
    用户可以拖动这些点来更改椭圆大小。位于大轴边缘的两个点。在我的解决方案中,我通过图形类使用GDI。 现在我的解决方案是计算对应于椭圆的矩形,并使用 Druellipse方法和旋转变换(如果需要)。但是这个解有很多数学计算。也许有最简单的方法来解决这项任务?

    2 回复  |  直到 7 年前
        1
  •  2
  •   diomonogatari    7 年前

    下面是如何使用 DrawEllipse 方法从旋转、短轴和两个顶点。

    首先,我们计算 Size 边界的 Rectangle :

    鉴于 Points A and B 坐在长度较短的一侧 smallSize 我们用一个小毕达哥拉斯得到了长边:

    int longSide = (int)(Math.Sqrt((A.Y - B.Y) * (A.Y - B.Y) + (B.X - A.X) * (B.X - A.X)));
    

    因此:

    Size size = new System.Drawing.Size(longSide, smallSize);
    

    接下来我们需要旋转角度:

    float angle = -(float)(Math.Atan2(A.Y - B.Y, B.X - A.X) * 180f / Math.PI);
    

    这将使事情更容易得到中心 Point C :

    Point C = new Point((A.X + B.X)/ 2, (A.Y + B.Y)/ 2);
    

    我们最不想要的是一个例程,它绘制给定形状的椭圆 大小 ,旋转 C 从一个角度来看:

    void DrawEllipse(Graphics G, Pen pen, Point center, Size size, float angle)
    {
        int h2 = size.Height / 2;
        int w2 = size.Width / 2;
        Rectangle rect = new Rectangle( new Point(center.X - w2, center.Y - h2), size );
    
        G.TranslateTransform(center.X, center.Y);
        G.RotateTransform(angle);
        G.TranslateTransform(-center.X, -center.Y);
        G.DrawEllipse(pen, rect);
        G.ResetTransform();
    }
    

    [![在此处输入图像描述][1]][1]

    这里有一个小试验台,将所有这些结合在一起:

    Point A = new Point(200, 200); // *
    Point B = new Point(500, 250);
    int smallSize = 50;
    
    
    void doTheDraw(PictureBox pb)
    {
        Bitmap bmp = new Bitmap(pb.Width, pb.Height);
    
        float angle = -(float)(Math.Atan2(A.Y - B.Y, B.X - A.X) * 180f / Math.PI);
        int longSide = (int)(Math.Sqrt((A.Y - B.Y) * (A.Y - B.Y) + (B.X - A.X) * (B.X - A.X)));
        Point C = new Point((A.X + B.X) / 2, (A.Y + B.Y) / 2);
        Size size = new System.Drawing.Size((int)longSide, smallSize);
    
        using (Pen pen = new Pen(Color.Orange, 3f))
        using (Graphics g = Graphics.FromImage(bmp))
        {
            // a nice background grid (optional):
            DrawGrid(g, 0, 0, 100, 50, 10,
                Color.LightSlateGray, Color.DarkGray, Color.Gainsboro);
    
            // show the points we use (optional):
            g.FillEllipse(Brushes.Red, A.X - 4, A.Y - 4, 8, 8);
            g.FillRectangle(Brushes.Red, B.X - 3, B.Y - 3, 7, 7);
            g.FillEllipse(Brushes.Red, C.X - 5, C.Y - 5, 11, 11);
    
            // show the connection line (optional):
            g.DrawLine(Pens.Orange, A, B);
    
            // here comes the ellipse:
            DrawEllipse(g, pen, C, size, angle);
        }
        pb.Image = bmp;
    }
    

    网格是一个很好的助手:

    void DrawGrid(Graphics G, int ox, int oy, 
                  int major, int medium, int minor, Color c1, Color c2, Color c3)
    {
        using (Pen pen1 = new Pen(c1, 1f))
        using (Pen pen2 = new Pen(c2, 1f))
        using (Pen pen3 = new Pen(c3, 1f))
        {
            pen2.DashStyle = DashStyle.Dash;
            pen3.DashStyle = DashStyle.Dot;
            for (int x = ox; x < G.VisibleClipBounds.Width; x += major)
                G.DrawLine(pen1, x, 0, x, G.VisibleClipBounds.Height);
            for (int y = oy; y < G.VisibleClipBounds.Height; y += major)
                G.DrawLine(pen1, 0, y, G.VisibleClipBounds.Width, y);
    
            for (int x = ox; x < G.VisibleClipBounds.Width; x += medium)
                G.DrawLine(pen2, x, 0, x, G.VisibleClipBounds.Height);
            for (int y = oy; y < G.VisibleClipBounds.Height; y += medium)
                G.DrawLine(pen2, 0, y, G.VisibleClipBounds.Width, y); 
    
            for (int x = ox; x < G.VisibleClipBounds.Width; x += minor)
                G.DrawLine(pen3, x, 0, x, G.VisibleClipBounds.Height);
            for (int y = oy; y < G.VisibleClipBounds.Height; y += minor)
                G.DrawLine(pen3, 0, y, G.VisibleClipBounds.Width, y);
        }
    }
    

    注意,我做了 A, B, smallSide 类级别的变量,以便我可以在测试期间修改它们,(我做到了 * )..

    如你所见,我添加了一个 TrackBar 使 smallside 动态为了更有趣,我添加了这个 MouseClick 事件:

    private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button.HasFlag(MouseButtons.Left)) A = e.Location;
        else B = e.Location;
        doTheDraw(pictureBox1);
    }
    

    我希望我的一些逻辑可以帮助你实现你想要实现的目标。你们提出的下一个问题是尝试添加更多信息,不要太笼统。

        2
  •  0
  •   Yves Daoust    7 年前

    计算相似度变换(平移和旋转),使前两点 (-a, 0) (a, 0) . [这可以通过矩阵变换或复数来实现。]

    对第三个点应用相同的变换。现在,简化椭圆的方程是

    x² / a² + y² / b² = 1.
    

    您只需确定参数 b 通过插入第三个点的简化坐标,

    b = y / √(1 - x²/a²).
    

    现在你有了

    • 中心,
    • 轴的方向,
    • 长轴的长度,
    • 短轴的长度。