Нарисуйте эллипс заданной толщины между двумя точками

У меня есть растровый объект С#, и я могу нарисовать линию из точки А в точку Б.

У меня есть 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);
    }

person user230910    schedule 01.05.2016    source источник
comment
Вы должны использовать Graphics.RotateTransform(), чтобы получить главную ось эллипса под углом.   -  person Hans Passant    schedule 01.05.2016


Ответы (2)


Вот как использовать метод 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)));

So :

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; надо, конечно..!

person TaW    schedule 01.05.2016
comment
Отлично, это именно то, что я искал! - person user230910; 02.05.2016
comment
Обратите внимание на небольшое исправление: (я забыл сбросить графику после RotatTransform!) - person TaW; 02.05.2016

Если вы хотите использовать 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 безье с повернутыми точками рамки эллипса.

person Razko    schedule 01.05.2016