Главная » Разработка для Windows Phone 7 » Полилинии и произвольные кривые

0

Элемент Line выглядит просто, а вот разметка для него несколько раздута. Сократить разметку для отрисовки одиночной линии можно, применив не Line, а Polyline (Полилиния):

<Grid Background="LightCyan">

<Polyline Points="100 300 200 50" Stroke="Blue" StrokeThickness="5" /> <Polyline Points="50 100 300 200" Stroke="Red" StrokeThickness="30" />

</Grid>

Свойство Points (Точки) класса Polyline типа PointCollection (Коллекция точек). Это коллекция объектов Point (Точка). В XAML множество точек задается просто набором чередующихся координат X и Y. Пары значений можно задавать в одну строку, записывая их через пробел, как в предыдущем примере, или можно сделать разметку несколько более понятной и добавить запятые. Некоторые разработчики предпочитают ставить запятые между координатами X и Y:

<Polyline Points = "100, 300 200, 50" …

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

<Polyline Points="100 300, 200 50"

Преимущества Polyline в том, что в нем можно задавать любое количество точек:

<Grid Background="LightCyan">

<Polyline Points="100 300, 200 50, 350 100, 200 250" Stroke="Blue" StrokeThickness="5" />

<Polyline Points=" 50 100, 300 200, 300 4 00" Stroke="Red" StrokeThickness="30" />

</Grid>

Каждая дополнительная точка продлевает полилинию на один сегмент.

Но у Polyline есть один существенный недостаток, которого нет у Line. Поскольку в Polyline мы имеем дело с коллекцией объектов Point, отдельный объект не может быть целью стиля, привязки данных или анимации. Но это не означает, что мы не можем изменять PointCollection во время выполнения. Это, несомненно, возможно, и все изменения будут отражены в формируемом визуальном представлении Polyline. Я продемонстрирую это в приложении GrowingPolygons (Увеличивающиеся многоугольники) далее в данной главе.

С помощью Polyline можно отрисовывать простые соединенные линии, но его истинное предназначение – создание сложных кривых, которые обычно формируются алгоритмически в коде. Polyline – это всегда коллекция прямых линий, но если сделать эти линии достаточно короткими, и если их будет очень много, результирующая линия будет выглядеть как настоящая кривая.

Например, используем Polyline для отрисовки круга. Обычно круг определяется как фигура с центром в точке (0, 0) и радиусом R, координаты (x, y) всех точек которой удовлетворяют уравнению:

х2 + у2 = R2

Это всем известная теорема Пифагора.

Но в задачах вычисления координат точек для отрисовки круга эту формулу использовать неудобно. Только представьте, нам придется выбирать значения x в диапазоне от -R до R, затем вычислять y, не забывая при этом, что большинству значений x соответствуют два значения y. И даже если мы будем выбирать значения x с регулярным интервалом, мы получим большую плотность точек в области, где x стремиться к 0, чем в области, где y стремиться к 0.

Для компьютерной графики лучше использовать параметрические уравнения, в которых и x, и y являются функциями какой-то третьей переменной. Эту переменную иногда называют t, предполагая время. В нашем случае этой третьей переменной является угол с диапазоном значений от 0 до 360°.

Предположим, центр круга радиусом R располагается в точке (0, 0). Этот круг будет вписан в квадрат со стороной 2R, где соответственно принятому в Silverlight соглашению значения x будут меняться слева направо от -R до +R, и значения y будут меняться сверху вниз от -R до +R.

Начнем с угла 0°, что соответствует точке (R, 0), и будем двигаться по часовой стрелке вдоль по окружности. По мере увеличения угла от 0° to 90° x меняется от R до 0 и затем до -R, когда угол равен 180°, и возвращается опять к нулю при угле в 270°, и опять становится равным R, когда угол достигает значения 360°. Всем известное уравнение:

х = R* cos(?)

В то же время значения y меняются от 0 до R и опять к 0, и до -R, и назад к 0, что описывается уравнением

у = R * sin(?)

В зависимости от того, где начинается окружность, и в каком направлении мы идем, формулы могут немного отличаться: косинус и синус могут меняться местами, один из них или оба могут использоваться со знаком минус.

Если в этих формулах использовать разные значения R, получим эллипс. Круг можно центрировать в точке (Cx, Cy). Для этого просто добавляем эти координаты в соответствующие формулы:

х = Сх + R * cos(?)

у = Су + R * sin(?)

В приложении эти две формулы помещаются в цикл for, который обеспечивает равномерное приращение переменной angle (угол) в диапазоне значений от 0 до 360 для формирования коллекции точек.

Какая дискретизация значений позволит получить сглаженную окружность? В данном конкретном примере это зависит от радиуса. Длина окружности вычисляется по формуле 2?R, так что если радиус равен 240 пикселам, к примеру, длина окружности будет приблизительно 1500 пикселов. Разделим на 360° и получим примерно 4, т.е. если в цикле for угол будет каждый раз получать приращение в 0,25°, результирующие точки будут отстоять друг от друга примерно на один пиксел. (Позже в данной главе я покажу, что можно обойтись и намного меньшим количеством точек.)

Создадим новый проект. Откроем файл MainPage.cs и зададим обработчик события Loaded, что позволит выполнять доступ к размерам сетки ContentPanel. Вычисляем центр и радиус окружности так, чтобы круг был центрирован относительно панели для содержимого и занимал всю ее площадь:

Point center = new Point(ContentPanel.ActualWidth / 2,

ContentPanel.ActualHeight / 2 – 1); double radius = Math.Min(center.X – 1, center.Y – 1);

Обратите внимание на то, что при вычислении радиуса мы вычитаем по пикселу от каждой координаты. Таким образом, размер круга будет несколько меньше, чем размер области содержимого. Любой контур имеет некоторую толщину, если размер круга будет таким же, как и размер занимаемой им области, его контур будет отсечен по краям.

Теперь создадим Polyline и зададим свойства Stroke и StrokeThickness:

Polyline polyline = new Polyline();

polyline.Stroke = this.Resources["PhoneForegroundBrush"] as Brush; polyline.StrokeThickness = (double)this.Resources["PhoneStrokeThickness"];

Вычисляем объекты Point в цикле for, используя рассмотренные выше формулы, и добавляем их в коллекцию Points объекта polyline:

for (double angle = 0; angle < 360; angle += 0.25) {

double radians = Math.PI * angle / 180;

И получаем такой результат:

Мы отрисовали окружность, идя сложным путем. Также мы получили незамкнутую окружность, потому что переменная angle в цикле for не достигла значения 360. Так что на самом деле там есть небольшой разрыв справа.

Но давайте не будем заниматься устранением проблемы, а сделаем кое-что другое. Пусть верхняя граница диапазона значений угла будет 3600:

for (double angle = 0; angle < 3600; angle += 0.25)

Теперь цикл обеспечит 10 полных оборотов по окружности. Возьмем этот angle и исходное значение radius и вычислим scaledRadius (Переменный радиус):

double scaledRadius = radius * angle / 3600;

double x = center.X + radius * Math.Cos(radians); double y = center.Y + radius * Math.Sin(radians); polyline.Points.Add(new Point(x, y));

Теперь добавим Polyline в Grid:

ContentPanel.Children.Add(polyline);

Подставим это значение scaledRadius в наши формулы с синусом и косинусом. В результате получаем архимедову спираль:

Привожу класс полностью:

Проект Silverlight: Spiral Файл: MainPage.xaml.cs (фрагмент)

public partial class MainPage : PhoneApplicationPage {

public MainPage() {

InitializeComponent(); Loaded += OnLoaded;

}

void OnLoaded(object sender, RoutedEventArgs args) {

Point center = new Point(ContentPanel.ActualWidth / 2,

ContentPanel.ActualHeight / 2 – 1); double radius = Math.Min(center.X – 1, center.Y – 1);

Polyline polyline = new Polyline();

polyline.Stroke = this.Resources["PhoneForegroundBrush"] as Brush; polyline.StrokeThickness = (double)this.Resources["PhoneStrokeThickness"];

for (double angle = 0; angle < 3600; angle += 0.25) {

double scaledRadius = radius * angle / 3600; double radians = Math.PI * angle / 180;

double x = center.X + scaledRadius * Math.Cos(radians); double y = center.Y + scaledRadius * Math.Sin(radians); polyline.Points.Add(new Point(x, y));

}

ContentPanel.Children.Add(polyline);

}

}

Не обязательно создавать объект Polyline в коде, его можно описать в XAML и затем просто использовать для размещения точек в коллекции Points. В главе 15 я покажу, как применить анимацию вращения к спирали и создать гипнотизирующую спираль.

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

  • Комментарии