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

Рисование окружностей и дуг кривыми Безье

Выше я сказал, что в языке PostScript кривые Безье применяются для рисования эллиптических дуг. Как станет ясно в главе 15, Windows Forms поступают аналогично, по крайней мере при сохранении дуг и эллипсов в виде графических контуров.

В литературе можно найти пару статей, посвященных аппроксимации эллиптических дуг кривыми Безье 1. В первой описана довольно простая методика рисования круговых сегментов. Допустим, при помощи кривой Безье нужно нарисовать дугу окружности с заданным радиусом и угловой шириной α. Ясно, что p0 и p3 будут точками начала и конца дуги, но как задать p1 и p2? Как показывает диаграмма, проблема сводится к нахождению расстояния между конечными и управляющими точками, т.е. длины отрезка L:


Рис. 13.8.

Углы между отрезками, соединяющими конечные и управляющие точки, и радиусами сегмента обозначены на диаграмме как прямые. Откуда это следует? Из условия коллинеарности, которое должно выполняться, чтобы кривая была плавной. Если вы собираетесь использовать кривую Безье, чтобы пририсовать к уже нарисованной дуге еще одну с центром в той же точке и с таким же радиусом, то общая конечная точка этих дуг и две соседние управляющие точки должны быть коллинеарными. Это значит, что отрезок, соединяющий конечную точку с управляющей, будет перпендикулярен радиусу окружности.

Если известна длина отрезка L, то для расчета координат p1 и p2 достаточно элементарной тригонометрии. Посмотрите, как просто рассчитать координаты точек p1 и p2, если радиусы сегмента находятся под прямым углом друг к другу и параллельны осям координат:


Рис. 13.9.

В случае угла 180° вычисление координат p1 и p2 также тривиально.

Выкладки первой из упомянутых мной статей показывают, что получается неплохая аппроксимация, если взять для радиуса множитель:


Рис. 13.10.

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

BezierCircles.cs

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

  class BezierCircles: PrintableForm
  {
      public new static void Main()
      {
          Application.Run(new BezierCircles());
      }
      public BezierCircles()
      {
          Text = "Bezier Circles";
      }
      protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
      {
          int iRadius = Math.Min(cx - 1, cy - 1) / 2;

          grfx.DrawEllipse(new Pen(clr), cx / 2 - iRadius, cy / 2 - iRadius, 
                                         2 * iRadius, 2 * iRadius);

          // Двухсегментная аппроксимация (180 градусов)

          int L = (int) Math.Round(iRadius * 4f / 3 * Math.Tan(Math.PI / 4));

          Point[] apt = {
                              new Point(cx / 2,     cy / 2 - iRadius),
                              new Point(cx / 2 + L, cy / 2 - iRadius),
                              new Point(cx / 2 + L, cy / 2 + iRadius), 
                              new Point(cx / 2,     cy / 2 + iRadius),
                              new Point(cx / 2 - L, cy / 2 + iRadius),
                              new Point(cx / 2 - L, cy / 2 - iRadius),
                              new Point(cx / 2,     cy / 2 - iRadius)
                        };
          grfx.DrawBeziers(Pens.Blue, apt);

          // Четырехсегментная аппроксимация (90 градусов)

          L = (int) Math.Round(iRadius * 4f / 3 * Math.Tan(Math.PI / 8));

          apt = new Point[] 
                         {
                              new Point(cx / 2,           cy / 2 - iRadius),
                              new Point(cx / 2 + L,       cy / 2 - iRadius),
                              new Point(cx / 2 + iRadius, cy / 2 - L),
                              new Point(cx / 2 + iRadius, cy / 2),
                              new Point(cx / 2 + iRadius, cy / 2 + L),
                              new Point(cx / 2 + L,       cy / 2 + iRadius), 
                              new Point(cx / 2,           cy / 2 + iRadius),
                              new Point(cx / 2 - L,       cy / 2 + iRadius),
                              new Point(cx / 2 - iRadius, cy / 2 + L),
                              new Point(cx / 2 - iRadius, cy / 2),
                              new Point(cx / 2 - iRadius, cy / 2 - L),
                              new Point(cx / 2 - L,       cy / 2 - iRadius),
                              new Point(cx / 2,           cy / 2 - iRadius)
                         };
          grfx.DrawBeziers(Pens.Red, apt);
      }
  }

Эта программа наглядно демонстрирует отличие аппроксимации кривыми Безье от метода DrawEllipse. Программа начинает обработку метода DoPage с вызова DrawEllipse для рисования эллипса с черным контуром. Контур фигуры, полученной в результате аппроксимации двумя кривыми Безье, рисуется синим цветом, а контур фигуры, образованной четырьмя кривыми, — красным. Помните: аргументы тригонометрических функций класса Math выражаются в радианах, поэтому вместо того, чтобы поделить угол на 4, как в формуле для расчета L, я использовал выражение на основе константы Math.PI.



1 Тор Доккен и др. «Good Approximation of Circles by Curvature-Continuous Buzier Curves», Computer Aided Geometric Design 7 (1990), стр. 33–41; Майкл Голдэпп «Approximation of Circular Arcs by Cubic Polynomials», Computer Aided Geometric Design 8 (1991), стр. 227–238.


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

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