netlib.narod.ru< Назад | Оглавление | Далее >

Дуги и секторы

Дуга (по крайней мере в случае Windows Forms) — это сегмент эллипса. Чтобы определить дугу, нужно указать те же сведения, что и для определения эллипса, плюс углы для начальной и конечной точек дуги. В силу этих причин каждая из четырех версий метода DrawArc требует на два аргумента больше, чем их нужно для DrawEllipse:


Методы DrawArc класса Graphics



void DrawArc(Pen pen, int x, int y, int cx, int cy,
             int iAngleStart, int iAngleSweep)
void DrawArc(Pen pen, float x, float y, float cx, float cy,
             float fAngleStart, float fAngleSweep)
void DrawArc(Pen pen, Rectangle rect,
             float fAngleStart, float fAngleSweep)
void DrawArc(Pen pen, RectangleF rectf,
             float fAngleStart, float fAngleSweep)


Пара дополнительных аргументов задает утлы, определяющие начало дуги и ее длину. Эти углы (которые могут быть как положительными, так и отрицательными) измеряются по часовой стрелке в градусах и откладываются от горизонтальной оси справа от центра эллипса (стрелка часов в этом положении покажет 3 часа):


Рис. 5.10.

Вот программа, рисующая эллипс с пунктирным контуром. Угловой размер штрихов пунктира составляет 10o, промежутков между штрихами — 5o.

DashedEllipse.cs

  //----------------------------------------------
  // DashedEllipse.cs (C) 2001 by Charles Petzold
  //----------------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class DashedEllipse: PrintableForm
  {
      public new static void Main()
      {
          Application.Run(new DashedEllipse());
      }
      public DashedEllipse()
      {
          Text = "Dashed Ellipse Using DrawArc";
      }
      protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
      {
          Pen       pen  = new Pen(clr);
          Rectangle rect = new Rectangle(0, 0, cx - 1, cy - 1);

          for (int iAngle = 0; iAngle < 360; iAngle += 15)
              grfx.DrawArc(pen, rect, iAngle, 10);
      }
  }

Полученный эллипс выглядит так:


Рис. 5.11.

В Win32 API есть функция RoundRect, рисующая прямоугольник с закругленными углами. Эта функция принимает 4 аргумента, указывающих координаты верхнего левого и правого нижнего углов прямоугольника плюс еще два, определяющие ширину и высоту эллипса, используемого для скругления углов прямоугольника.

В классе Graphics метода RoundRect нет, но мы вполне можем попытаться имитировать его.

RoundRect.cs

  //------------------------------------------
  // RoundRect.cs (C) 2001 by Charles Petzold
  //------------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class RoundRect: PrintableForm
  {
      public new static void Main()
      {
          Application.Run(new RoundRect());
      }
      public RoundRect()
      {
          Text = "Rounded Rectangle";
      }
      protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
      {
          RoundedRectangle(grfx, Pens.Red, 
                           new Rectangle(0, 0, cx - 1, cy - 1),
                           new Size(cx / 5, cy / 5));
      }
      void RoundedRectangle(Graphics grfx, Pen pen, Rectangle rect, Size size)
      {
          grfx.DrawLine(pen, rect.Left  + size.Width / 2, rect.Top,
                             rect.Right - size.Width / 2, rect.Top);

          grfx.DrawArc(pen, rect.Right - size.Width, rect.Top, 
                            size.Width, size.Height, 270, 90);

          grfx.DrawLine(pen, rect.Right, rect.Top + size.Height / 2,
                             rect.Right, rect.Bottom - size.Height / 2);

          grfx.DrawArc(pen, rect.Right - size.Width, 
                            rect.Bottom - size.Height,
                            size.Width, size.Height, 0, 90);

          grfx.DrawLine(pen, rect.Right - size.Width / 2, rect.Bottom,
                             rect.Left + size.Width / 2, rect.Bottom);

          grfx.DrawArc(pen, rect.Left, rect.Bottom - size.Height,
                            size.Width, size.Height, 90, 90);

          grfx.DrawLine(pen, rect.Left, rect.Bottom - size.Height / 2,
                             rect.Left, rect.Top + size.Height / 2);

          grfx.DrawArc(pen, rect.Left, rect.Top,
                            size.Width, size.Height, 180, 90);
      }
  }

Написанному мной методу RoundedRectangle требуются такие аргументы: Rectangle, указывающий расположение и размер прямоугольника и Size, задающий ширину и высоту эллипса для скругления углов. Я написал этот метод, чтобы сохранить согласованность с размерами прямоугольника, нарисованного методом DrawRectangle. To есть, когда ширина и высота фигуры устанавливаются равными соответствующим измерениям клиентской области минус 1, будет видима вся фигура. Мой метод по очереди вызывает DrawLine и DrawArc, прорисовывает сначала верхний контур фигуры и продолжает рисовать остальные части контура по часовой стрелке.


Рис. 5.12.

Однако я не могу без колебаний рекомендовать этот способ как общий подход к рисованию прямоугольников с закругленными углами. Отдельные линии и дуги здесь рисуют отдельные вызовы методов DrawLine и DrawArc. Это значит, что у каждой из восьми частей контура фигуры будут прорисованы концы, а соединения между ними — нет. Правильный путь объединения прямых и кривых в цельную фигуру состоит в использовании графического контура (path). Как это делается, я покажу в главе 15.

У методов DrawPie те же аргументы, что и у DrawArc, но они отличаются тем, что соединяют концы дуги с центром эллипса линиями, создавая замкнутую область.


Методы DrawPie класса Graphics



void DrawPie(Pen pen, int x, int y, int cx, int cy,
             int iAngleStart, int iAngleSweep)
void DrawPie(Pen pen, float x, float y, float cx, float cy,
             float fAngleStart, float fAngleSweep)
void DrawPie(Pen pen, Rectangle rect,
             float fAngleStart, float fAngleSweep)
void DrawPie(Pen pen, RectangleF rectf,
             float fAngleStart, float fAngleSweep)


В мире деловой графики очень уважают круговые диаграммы — это факт. Проблема в том, что если уж пришлось программировать круговую диаграмму, хорошо бы украсить ее трехмерными и другими эффектами. Здесь у метода DrawPie меньше возможностей, чем вы думаете. Так или иначе, вот программа, строящая круговую диаграмму на основе массива значений (хранимого как поле), который я создаю специально для этой цели.

PieChart.cs

  //-----------------------------------------
  // PieChart.cs (C) 2001 by Charles Petzold
  //-----------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class PieChart: PrintableForm
  {
      int[] aiValues = { 50, 100, 25, 150, 100, 75 };

      public new static void Main()
      {
          Application.Run(new PieChart());
      }
      public PieChart()
      {
          Text = "Pie Chart";
      }
      protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
      {
          Rectangle rect   = new Rectangle(50, 50, 200, 200);
          Pen       pen    = new Pen(clr);
          int       iTotal = 0;
          float     fAngle = 0, fSweep;

          foreach(int iValue in aiValues)
              iTotal += iValue;

          foreach(int iValue in aiValues)
          {
              fSweep = 360f * iValue / iTotal;
              DrawPieSlice(grfx, pen, rect, fAngle, fSweep);
              fAngle += fSweep;
          }
      }
      protected virtual void DrawPieSlice(Graphics grfx, Pen pen, 
                                          Rectangle rect,
                                          float fAngle, float fSweep)
      {
          grfx.DrawPie(pen, rect, fAngle, fSweep); 
      }
  }

Обратите внимание на определение Rectangle в методе DoPage. Это единственная программа в данной главе, использующая абсолютные координаты и размеры, так как круговые диаграммы в форме эллипса не слишком привлекательны. Метод DoPage суммирует значения массива и рассчитывает угол для каждого сектора, деля значение для этого сектора на сумму значений всех секторов и умножая частное на 360o. Вот результат:


Рис. 5.13.

Простите, но я просто не могу позволить вам думать, что это лучшая диаграмма, которую я способен нарисовать! К счастью, я оказался достаточно прозорливым, чтобы поместить вызов DrawPie в виртуальную функцию в классе PieChart. Это позволяет легко переопределить метод в программе BetterPieChart.

BetterPieChart.cs

  //-----------------------------------------------
  // BetterPieChart.cs (C) 2001 by Charles Petzold
  //-----------------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class BetterPieChart: PieChart
  {
      public new static void Main()
      {
          Application.Run(new BetterPieChart());
      }
      public BetterPieChart()
      {
          Text = "Better " + Text;
      }
      protected override void DrawPieSlice(Graphics grfx, Pen pen, 
                                           Rectangle rect,
                                           float fAngle, float fSweep)
      {
          float fSlice = (float)(2 * Math.PI * (fAngle + fSweep / 2) / 360);

          rect.Offset((int)(rect.Width  / 10 * Math.Cos(fSlice)),
                      (int)(rect.Height / 10 * Math.Sin(fSlice)));

          base.DrawPieSlice(grfx, pen, rect, fAngle, fSweep);
      }
  }

Переменная fSlice — это угол между горизонтальной осью и центром сектора, преобразованный в радианы. Это значение понадобилось мне для расчета смещения по осям X и Y прямоугольников, определяющих размер и положение секторов диаграммы. В результате все секторы отошли от центра, и на изображении диаграммы они не соприкасаются:


Рис. 5.14.

На этом коллекция методов класса Graphics, рисующих линии, далеко не исчерпана. Методы DrawBezier, DrawBeziers, DrawCurve и DrawClosedCurve позволяют рисовать кривые посложнее, чем эллиптические дуги. Описание этих методов вы найдете в главе 13. Можно объединить набор прямых и кривых в графический контур и визуализировать его при помощи метода DrawPath. К этой теме мы обратимся в главе 15.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz