netlib.narod.ru | < Назад | Оглавление | Далее > |
Выше я сказал, что в языке PostScript кривые Безье применяются для рисования эллиптических дуг. Как станет ясно в главе 15, Windows Forms поступают аналогично, по крайней мере при сохранении дуг и эллипсов в виде графических контуров.
В литературе можно найти пару статей, посвященных аппроксимации эллиптических дуг кривыми Безье 1. В первой описана довольно простая методика рисования круговых сегментов. Допустим, при помощи кривой Безье нужно нарисовать дугу окружности с заданным радиусом и угловой шириной α. Ясно, что p0 и p3 будут точками начала и конца дуги, но как задать p1 и p2? Как показывает диаграмма, проблема сводится к нахождению расстояния между конечными и управляющими точками, т.е. длины отрезка L:
Углы между отрезками, соединяющими конечные и управляющие точки, и радиусами сегмента обозначены на диаграмме как прямые. Откуда это следует? Из условия коллинеарности, которое должно выполняться, чтобы кривая была плавной. Если вы собираетесь использовать кривую Безье, чтобы пририсовать к уже нарисованной дуге еще одну с центром в той же точке и с таким же радиусом, то общая конечная точка этих дуг и две соседние управляющие точки должны быть коллинеарными. Это значит, что отрезок, соединяющий конечную точку с управляющей, будет перпендикулярен радиусу окружности.
Если известна длина отрезка L, то для расчета координат p1 и p2 достаточно элементарной тригонометрии. Посмотрите, как просто рассчитать координаты точек p1 и p2, если радиусы сегмента находятся под прямым углом друг к другу и параллельны осям координат:
В случае угла 180° вычисление координат p1 и p2 также тривиально.
Выкладки первой из упомянутых мной статей показывают, что получается неплохая аппроксимация, если взять для радиуса множитель:
При помощи этой аппроксимации программа 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 | < Назад | Оглавление | Далее > |