
Lection11_C#.ppt
- Количество слайдов: 40
Лекция 11 Линии, кривые, заливка областей (продолжение)
Прямоугольник • Прямоугольники являются одной из самых распространенных форм графических объектов. • Прямоугольник можно нарисовать при помощи методов Draw. Line или Draw. Lines, но проще сделать это методом Draw. Rectangle. • В каждой из трех его версий прямоугольник определяется точкой, задающей положение верхнего левого угла, шириной и высотой. Аналогично определяется структура Rectangle, которую использует одна из версий Draw. Rectangle:
• void Draw. Rectangle(Pen pen, int x, int y, int сx, int cy) • void Draw. Rectangle(Pen pen, float x, float y, float сx, float cy) • void Draw. Rectangle(Pen pen, Rectangle rect) Как ни странно, метод Draw. Rectangle, использующий структуру Rectangle. F, отсутствует. Ширина и высота прямоугольника должны быть больше 0. Прямоугольники с отрицательной шириной и высотой не вызовут исключения, но и не будут нарисованы.
• Чтобы очертить клиентский прямоугольник снаружи видимым контуром, можно попытаться поместить свойство формы Client. Rectangle прямо в вызов Draw. Rectangle : • grfx. Draw. Rectangle(pen, Client. Rectangle); • // Неправильно • Однако это работать не будет, поскольку правая и нижняя стороны прямоугольника станут невидимыми.
protected override void Do. Page(Graphics grfx, Color clr, int cx, int cy) { grfx. Draw. Rectangle(Pens. Red, 0, 0, cx - 1, cy - 1); } Если попробовать указать в качестве последних двух аргументов метода Draw. Rectangle просто значения сх и су, не вычитая из них 1, и то можно увидеть, что правая и нижняя стороны прямоугольника станут невидимыми в клиентской области. Это следствие той же проблемы, что возникает при использовании свойства Client. Rectangle и вызове Draw. Rectangle.
• Класс Graphics также включает два метода, позволяющих рисовать несколько прямоугольников: Методы Draw. Rectangles класса Graphics void Draw. Rectangles (Pen pen, Rectangle[] arect) void Draw. Rectangles (Pen pen, Rectangle. F[] arectf) • Пользы от них намного меньше, чем от Draw. Lines. Но предположим, что есть структура Rectangle. F с именем, скажем, rectf. На основе этой структуры нужно нарисовать один прямоугольник. • Помня об отсутствии перегруженных версий метода Draw. Rectangle, можно решить поставленную задачу методом Draw. Rectangles: • grfx. Draw. Rectangles(pen, new Rectangle. F[] { rectf });
Многоугольники • С точки зрения математики, многоугольник — это замкнутая фигура с тремя и более сторонами, например, треугольники, квадраты, пяти-, шестиугольники и т. д. • Вот два метода класса Graphics, рисующих многоугольники: • void Draw. Polygon(Pen pen, Point[] point) • void Draw. Polygon(Pen pen, Point. F[] point) • По функциональности Draw. Polygon очень похож на Draw. Lines за исключением того, что он автоматически замыкает контур фигуры, соединяя ее первую и последнюю точки линией.
Рассмотрим, например, следующий массив структур Point: Point[] apt = {new Point (0, 0), new Point (50, 100), new Point (100, 0)}; Вызов: grfx. Draw. Lines(pen, apt); рисует две линии в виде буквы V, а вызов: grfx. Draw. Polygon(pen, apt); рисует треугольник Иногда можно имитировать вызов Draw. Polygon с помощью методов Draw. Lines и Drawline: Draw. Lines(pen, apt); Draw. Line(pen, apt[apt. Length-1], apt[0]); • Однако если приходится иметь дело с толстыми линиями с прорисованными концами и соединениями, такого же эффекта, как при использовании Draw. Polygon, не получится.
Эллипсы • В прошлой лекции рассматривался пример, как нарисовать эллипс при помощи метода Draw. Lines, но есть и более простой подход, использующий те же аргументы, что и Draw. Rectangle: • Методы Draw. Ellipse класса Graphics • void Draw. Ellipse(Pen pen, int x, int y, int cx, int cy) • void Draw. Ellipse(Pen pen, float, x, float y, float сx, float cy) • void Draw. Ellipse(Pen pen, Rectangle rect) • void Draw. Ellipse(Pen pen, Rectangle. F rectf)
• Если задать ширину и высоту равными 1, в результате будет нарисован закрашенный квадрат со стороной 2 пиксела. • Это означает (как и в случае Draw. Rectangle}, что нужно уменьшить ширину и высоту эллипса на 1, чтобы он уместился в области размером сх на су. protected override void Do. Page(Graphics grfx, Color clr, int cx, int cy) { grfx. Draw. Ellipse(new Pen(clr), 0, 0, cx-1, cy-1); }
Дуги и секторы • Дуга - это сегмент эллипса. Чтобы определить дугу, нужно указать те же сведения, что и для определения эллипса, плюс углы для начальной и конечной точек дуги. • Поэтому каждая из четырех версий метода Draw. Arc требует на два аргумента больше, чем их нужно для Draw. Ellipse: • Пара дополнительных аргументов задает утлы, определяющие начало дуги и ее длину. Эти углы (которые могут быть как положительными, так и отрицательными) измеряются по часовой стрелке, в градусах и откладываются от горизонтальной оси справа от центра эллипса (стрелка часов в этом положении покажет 3 часа):
• Методы Draw. Arc класса Graphics • void Draw. Arc(Pen pen, int x, int y, int сx, int cy, int i. Angle. Start, int i. Angle. Sweep) • void Draw. Arc(Pen pen, float x, float y, float сx, float cy, float f. Angle. Start, float f. Angle. Sweep) • void Draw. Arc(Pen pen, Rectangle rect, float f. Angle. Start, float f. Angle. Sweep) • void Draw. Arc(Pen pen, Rectangle. F rectf, float f. Angle. Start, float f. Angle. Sweep)
Вот программа, рисующая эллипс с пунктирным контуром. Угловой размер штрихов пунктира составляет 10 o, промежутков между штрихами - 5. protected override void Do. Page(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 i. Angle = 0; i. Angle < 360; i. Angle +=15) grfx. Draw. Arc(pen, rect, i. Angle, 10); }
• В Win 32 API есть функция Round. Rect, рисующая прямоугольник с закругленными углами. Эта функция принимает 4 аргумента, указывающих координаты верхнего левого и правого нижнего углов прямоугольника плюс еще два, определяющие ширину и высоту эллипса, используемого для скругления углов прямоугольника. • В классе Graphics метода Round. Rect нет, но в следующий пример попытается имитировать его.
protected override void Do. Page( Graphics grfx, Color clr, int ex, int cy){ Rounded. Rectangle(grfx, Pens. Red, new Rectangle(0, 0, cx - 1, cy - 1), new Size(cx / 5, cy /5)); } void Rounded. Rectanle(Graphics grfx, Pen pen, Rectangle rect, Size size) { grfx. Draw. Line(pen, rect. Left + size. Width / 2, rect. Top, rect. Right - size. Width / 2, rect. Top);
grfx. Draw. Arc(pen, rect. Right - size. Width, rect. Top, size. Width, size. Height, 270, 90); grfx. Draw. Line(pen, rect. Right, rect. Top * size. Height / 2, rect. Right, rect, Bottom - size. Height / 2); grfx. Draw. Arc(pen, rect. Right - size, Width, rect. Bottom size. Height, size. Width, size. Height, 0, 90); grfx. Draw. Line(pen, rect. Right - size. Width / 2, rect. Bottom, rect. Left + size. Width / 2, rect. Bottom);
grfx, Draw. Arc(pen, rect. Left, rect. Bottom - size. Height, size. Width, size. Height, 90. 90); grfx. Draw. Line(pen, rect. Left, rect, Bottom – size. Height / 2, rect. Left, rect. Top + size. Height / 2); grfx. Вraw. Arc(pen, rect. Left, rect. Top, size. Width, size, Height, 180, 90); }
• Написанному методу Rounded. Rectangle требуются такие аргументы: • Rectangle, указывающий расположение и размер прямоугольника и Size, задающий ширину и высоту эллипса для скругления углов. • Метод, сохраняет согласованность с размерами прямоугольника, нарисованного методом Draw. Rectangle. • To есть, когда ширина и высота фигуры устанавливаются равными соответствующим измерениям клиентской области минус 1, будет видима вся фигура. Метод по очереди вызывает Draw. Line и Draw. Arc, прорисовывает сначала верхний контур фигуры и продолжает рисовать остальные части контура по часовой стрелке.
Методы Draw. Pie имеют те же аргументы, что и у Draw. Arc, но они отличаются тем, что соединяют концы дуги с центром эллипса линиями, создавая замкнутую область. Методы Draw. Pie класса Graphics void Draw. Pie(Pen pen, int x, int у, int cx, int су, int i. Angle. Start, int i. Angle. Sweep); void Draw. Pie(Pen pen, float x, float y, float cx, float cy, float f. Angle. Start, float f. Angle. Sweep); void Draw. Pie(Pen pen, Rectangle rect, float f. Angle. Start, float f. Angle. Sweep); void Draw. Pie(Pen pen, Rectangle. F rectf, float f. Angle. Start, float f. Angle. Sweep);
программа, строящая круговую диаграмму на основе массива значений protected override void Do. Page(Graphics grfx, Color clr, int cx, int cy){ Rectangle rect = new Rectangle(50, 200, 200); Pen pen = new Pen(clr); int[] ai. Values = { 50, 100, 25, 150, 100, 75 }; int i. Total = 0; float f. Angle = 0, f. Sweep; foreach(int i. Value in ai. Values) i. Total += i. Value; foreach(int i. Value in ai. Values) { f. Sweep = 360 f * i. Value / i. Total; Draw. Pie. Slice(grfx, pen, rect, f. Angle, f. Sweep); f. Angle += f. Sweep; }}
protected virtual void Draw. Pie. Slice(Graphics grfx, Pen pen, Rectangle rect, float f. Angle, float f. Sweep) { grfx. Draw. Pie(pen, rect, f. Angle, f. Sweep); } • Обратите внимание на определение Rectangle в методе Do. Page. Это программа, использует абсолютные координаты и размеры, так круговые диаграммы в форме эллипса не слишком привлекательны. Метод Do. Page суммирует значения массива и рассчитывает угол для каждого сектора, деля значение для этого сектора на сумму значений всех секторов и умножая частное на З 60°.
Заливка фигур • Хотя некоторые из обсуждавшихся выше методов класса Graphics определяют замкнутые области, эти методы лишь прорисовывают заданным пером контуры области, не заполняя при этом ее внутреннюю часть. • Этим методам, имена которых начинаются префиксом Draw, соответствуют методы с префиксом Fill, выполняющие заливку оконтуренных областей. • Первый аргумент этих методов — объект Brush, используемый для заливки области.
Методы Fill. Rectangle класса Graphics • void Fill. Rectangle(Brush brush, int x, int y, int сx, int cy) • void Fill. Rectangle(Brush brush, float x, float y, float cx, float cy) • void Fill. Rectangle(Brush brush, Rectangle rect) • void Fill. Rectangle(Brush brush, Rectangle. F rectf) • Ширина и высота результирующей фигуры определяется значениями аргументов метода.
• Например, если ширина и высота равны 3, вызов Fill. Rectangle нарисует закрашенный квадрат со стороной 3 пиксела, верхний левый угол которого находится в пикселе с координатами (х, у). Если требуется нарисовать и залить некоторый прямоугольник, первым надо вызвать метод Fill. Rectangle, чтобы при заливке не были затерты никакие линии.
• Класс Graphics также включает два метода Fill. Rectangles. • Методы Fill. Rectangles класса Graphics • void Fill. Rectangles(Brush brush, Rectangle[] arect) • void Fill. Rectangles(Brush brush, Rectangle. F[] arect) • Вызов метода Fi. URectangles дает тот же результат, что несколько вызовов Fill. Rectangle.
• Имеется четыре версии метода Fill. Ellipse, их аргументы не отличаются от аргументов Draw. Ellipse. • Методы Fill. EIIipse класса Graphics • void Fill. Ellipse(Brush brush, int x, int y, int cx, int cy) • void Fill. Ellipse(Brush brush, float x, float y, float cx, float cy) • void Fill. Ellipse(Brush brush, Rectangle rect) • void Fill. Ellipse(Brush brush, Rectangle. F rectf)
• Метод Fill. Ellipse работает несколько иначе, чем все методы, о которых говорилось до сих пор. • Предположим, что задан эллипс с центром в точке (0, 0), высотой и шириной 20. Как известно, Draw. Ellipse нарисует фигуру; состоящую по горизонтали и вертикали из пикселов от 0 до 20, Таким образом, действительная высота и ширина фигуры составит 21 пиксел.
• Область, закрашиваемая Fill. Ellipse, по сути включает в себя пикселы с 1 по 19 по горизонтали и вертикали, таким образом, эффективный размер закрашиваемой области равен 19 пикселам. «По сути» , поскольку считается, что слева остается пиксел, занимающий позицию 0. • Кроме того, эллипс, нарисованный Draw. Ellipse, всегда немного перекрывается с областью, закрашенной Fill. Ellipse. Если нужно получить закрашенный эллипс с контуром, сначала надо вызывать метод Fill. Ellipse, а затем — Draw. Ellipse.
• Также имеются три метода Fill. Pie: • Методы Fil. IPie класса Graphics • void Fill. Pie(Brush brush, int x, int y, int ex, int cy, int i. Angle, int i. Sweep) • void Fill. Pie. Brush brush, float x, float y, float cx, float cy, float f. Angle, float f. Sweep) • void Fill. Pie. Brush brush, Rectangle rect, float f. Angle, float f. Sweep)
• Многоугольники и режим заливки • В завершение нам осталось рассмотреть метод Fill. Polygon. От других закрашиваемых областей многоугольники отличаются тем, что определяющие их линии могут пересекаться и накладываться одна на другую. • Это усложняет дело, так как есть два разных способа заливки многоугольников.
• Существует четыре метода Fill. Polygon; • • Методы Fill. Polygon класса Graphics void Fill. Polygon(Brush brush, Point[] apt) void Fill. Polygon(Brush brush, Point. F[] apt) void Fill. Polygon(Brush brush, Point[] apt, Fill. Mode fm) • void Fill. Polygon(Brush brush, Point. F[] apt, Fill. Mode fm)
• Они во всем похожи на метод Draw. Polygon, кроме одного необязательного аргумента. Fill. Mode — это перечисление, определяемое в пространстве имен System. Drawing 2 D. • У него всего два возможных значения: Член Значение Комментарий Alternate 0 Задано по умолчанию, чередует закрашенные и незакрашенные области фигуры. Winding 1 Закрашивает максимально возможное число внутренних областей фигуры,
• Режим заливки имеет значение, лишь когда линии, определяющие многоугольник, пересекаются. Он определяет, какие из замкнутых областей будут закрашены, а какие — нет. Если режим заливки в методе Fill. Polygon не задан, по умолчанию используется Fill. Mode. Alternate. • При этом замкнутая область заполняется, только если она отделена от бесконечности нечетным числом границ. • Классический пример — пятиконечная звезда. Ее внутренний пятиугольник закрашивается в режиме Winding, но остается свободным в режиме Alternate,
protected override void Do. Page( Graphics grfx, Color clr, int cx, int cy) { Brush brush = new Solid. Brush(clr); Point[] apt = new Point[5]; for (int i = 0; i < apt. Length; i++) { double d. Angle = (i * 0, 8 - 0. 5) * Math. PI; apt[i] = new Point( (int)(cx -(0. 25 * 0. 24 * Math. Cos(d. Angle))), (int)(cy *(0. 50 + 0. 48 * Math. Sin(d. Angle)))); }
grfx. Fill. Polygon(brush, apt, Fill. Mode. Alternate); for (int i = 0; i < apt. Length; i++) apt[i]. X += cx / 2; grfx. Fill. Polygon (brush, apt, Fill. Mode. Winding); } • Первый цикл for определяет пять концов звезды, выводимой в левой половине клиентской области. Для такого многоугольника устанавливается режим заливки alternate. • Второй цикл for сдвигает вершины звезды в правую часть клиентской области, где многоугольник закрашивается в режиме winding.
• Обычно в режиме Winding закрашиваются все замкнутые области. Но не всегда все так просто — есть и ряд исключений. Чтобы определить, будет ли закрашена область в режиме Winding, мысленно проведите прямую из любой точки этой области в бесконечность. • Если воображаемая линия пересекает нечетное число границ, область закрашивается (так же, как в режиме Alternate). Если же воображаемая линия пересекает четное число границ, то область может быть закрашена, а может остаться и свободной. Такая область закрашивается, если число граничных линий, направленных по часовой стрелке, не равно числу линий, направленных против часовой стрелки.
• Следующий код изображает фигуру, внутренняя область которой останется незакрашенной в режиме winding. protected override void Do. Page(Graphics grfx, Color clr, int сx, int cy) Brush brush = new Solid. Brush(clr); Point. F[] aptf = { new Point. F(0. 1 f, 0. 7 f), new Point. F(0. 5 f, 0. 1 f), new Point. F(0. 9 f, 0, 1 f), new Point. F(0. 9 f, 0. 5 f), new Point. F(0. 3 f, 0. 9 f), new Point. F(0. 7 f, 0. Зf). new Polnt. F(0. 1 f, 0. 3 f)};
for (int i = 0; i < aptf. Length; i++){ aptf[i]. X *= cx / 2; aptf[i]. Y *= cy; } grfx, Fill. Polygon(brush, aptf, Fill. Mode. Alternate); for (int i = 0; i < aptf. Length; i++) aptf[i]. X *= cx / 2; grfx, Fill. Polygon(brush, aptf, Fill. Mode. Winding);
Lection11_C#.ppt