繁体   English   中英

在2点之间绘制具有指定“脂肪”的椭圆

[英]Draw an ellipse with a specified “fatness” between 2 points

我有一个C#位图对象,并且能够从A点到B点画一条线。

我在图的边缘上有两个点,我想从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);
    }

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

首先,我们计算边界RectangleSize

smallSize 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 你当然应该..!

如果希望使用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个贝塞尔曲线。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM