【问题标题】:Draw an ellipse with a specified "fatness" between 2 points在 2 点之间绘制一个具有指定“胖度”的椭圆
【发布时间】:2016-08-26 06:53:50
【问题描述】:

我有一个 C# 位图对象,我可以画一条从 A 点到 B 点的线。

我在图表的边缘有 2 个点,我想画一个从 A 到 B 的椭圆。基本的 g.DrawEllipse() 只能完美地水平或垂直地绘制椭圆,但是我需要椭圆从图像的一端到另一端是一种对角线。

My bitmap:    200 tall by 500 wide
Point A:      Column 0, Row 20   (0,20)
Point B:      Column 499, Row 60 (499, 60)
Widest Point: 30  - Narrow Radius of the ellipse

这是我目前所拥有的,绘制椭圆没有我需要的重载,所以请帮助:

    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.DrawLine(pen, new Point(20,0), new Point(499,60));
        g.DrawEllipse(pen, 20, 0, someWidth, someHeight);
    }

【问题讨论】:

  • 您必须使用 Graphics.RotateTransform() 来获取椭圆的长轴角度。

标签: c# graphics gdi


【解决方案1】:

如果你想使用 Graphics 创建一个对角线椭圆,也许你可以使用 DrawBezier() 方法。 下面是一些代码:

// Draws an ellipse using 2 beziers.
private void DrawEllipse(Graphics g, PointF center, float width, float height, double rotation)
{
  // Unrotated ellipse frame
  float left   = center.X - width / 2;
  float right  = center.X + width / 2;
  float top      = center.Y - height / 2;
  float bottom   = center.Y + height / 2;
  PointF p1 = new PointF(left, center.Y);
  PointF p2 = new PointF(left, top);
  PointF p3 = new PointF(right, top);
  PointF p4 = new PointF(right, center.Y);
  PointF p5 = new PointF(right, bottom);
  PointF p6 = new PointF(left, bottom);

  // Draw ellipse with rotated points.
  g.DrawBezier(Pens.Black, Rotate(p1, center, rotation), Rotate(p2, center, rotation), Rotate(p3, center, rotation), Rotate(p4, center, rotation));
  g.DrawBezier(Pens.Black, Rotate(p4, center, rotation), Rotate(p5, center, rotation), Rotate(p6, center, rotation), Rotate(p1, center, rotation));
}

// Rotating a given point by given angel around a given pivot.
private PointF Rotate(PointF point, PointF pivot, double angle)
{
  float x = point.X - pivot.X;
  float y = point.Y - pivot.Y;
  double a = Math.Atan(y / x);
  if (x < 0)
  {
    a += Math.PI;
  }
  float size = (float)Math.Sqrt(x * x + y * y);

  double newAngel = a + angle;
  float newX = ((float)Math.Cos(newAngel) * size);
  float newY = ((float)Math.Sin(newAngel) * size);
  return pivot + new SizeF(newX, newY);
}

上面的代码在点 p1, p2, ..., p6 处计算椭圆的框架(与旋转相近)。然后,用椭圆框旋转点将椭圆绘制为 2 个贝塞尔曲线。

【讨论】:

    【解决方案2】:

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

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

    鉴于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);
    

    我们最不想要的就是绘制一个给定Size 的椭圆并围绕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();
    }
    

    这里有一个小测试平台,将所有内容结合在一起:

    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);
    }
    

    请注意,我不喜欢处理旧的Bitmap;你当然应该......!

    【讨论】:

    • 太好了,这正是我想要的!
    • 注意小修正:(我忘了在RotatTransform之后重置图形!)
    猜你喜欢
    • 2023-01-18
    • 2015-12-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    相关资源
    最近更新 更多