Главная » Разработка для Windows Phone 7 » Класс ArcSegment Windows Phone 7

0

Сложности возникают с классом ArcSegment (Сегмент дуга). Дуга – это лишь часть эллипса, но поскольку ArcSegment должен соответствовать парадигме начальных и конечных точек, он должен определяться двумя точками, лежащими на контуре некоторого эллипса. Но если эллипс задается посредством центра и радиуса, как найти точные координаты точки его контура без тригонометрических вычислений?

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

Полагаю, необходимо привести конкретный пример. Проведем линию из точки (120, 240) в точку (360, 240).

<Grid Background="LightCyan">

<Polyline Points="120 240, 360 240" Stroke="Black" StrokeThickness="6" />

</Grid>

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

Продемонстрируем это.

Предположим, мы хотим соединить эти две точки дугой, являющейся часть окружности с радиусом 144 пиксела. В данном фрагменте показано, как задать ArcSegment такого размера между точками (120, 240) и (360, 240):

<Grid Background="LightCyan">

<Polyline Points="120 240, 360 240" Stroke="Black" StrokeThickness="6" />

<Path Stroke="Blue"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120

240">

<ArcSegment Point="360 240"

Size="144 144" />

</PathFigure> </PathGeometry> </Path.Data> </Path> </Grid>

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

<Grid Background="LightCyan">

<Polyline Points="120 240, 360 240" Stroke="Black" StrokeThickness="6" />

<Path Stroke="Red"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Si:e="144 144" SweepDirection="Clockwise"

/>

</PathFigure> </PathGeometry> </Path.Data> </Path> </Grid>

В обоих случаях между двумя точками отрисовывается меньшая из двух возможных дуг. На рисунке проиллюстрирован результат, получаемый, когда свойство SweepDirection имеет значение по умолчанию CounterClockwise, а свойству IsLargeArc (Большая дуга) задано значение true:

<Grid Background="LightCyan">

<Polyline Points="120 240, 360 240" Stroke="Black" StrokeThickness="6" />

<Path Stroke="Green"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Si=e="144 144" IsLargeArc="True" /> </PathFigure> </PathGeometry> </Path.Data> </Path> </Grid>

Наконец, зададим свойству IsLargeArc значение true и SweepDirection значение Clockwise:

<Grid Background="LightCyan">

<Polyline Points="120 240, 360 240" Stroke="Black" StrokeThickness="6" />

<Path Stroke="Brown"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="144 144" IsLargeArc="True"

SweepDirection="Clockwise"/> </PathFigure> </PathGeometry> </Path.Data>

</Path> </Grid>

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

<Grid Background="LightCyan">

<Polyline Points="120 240, 360 240" Stroke="Black" StrokeThickness="6" />

<Path Stroke="Blue"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="144 144" /> </PathFigure> </PathGeometry> </Path.Data> </Path>

<Path Stroke="Red"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="144 144" SweepDirection="Clockwise"

/>

</PathFigure> </PathGeometry> </Path.Data> </Path>

<Path Stroke="Green"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="144 144" IsLargeArc="True" /> </PathFigure> </PathGeometry> </Path.Data> </Path>

<Path Stroke="Brown"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="144 144" IsLargeArc="True" SweepDirection="Clockwise"

/>

</PathFigure> </PathGeometry> </Path.Data> </Path> </Grid>

Это справедливо и для эллипса. Следующая разметка аналогична предыдущему примеру, только свойству Size объекта ArcSegment задано значение не (144, 144), а (200, 100):

<Grid Background="LightCyan">

<Polyline Points="120 240, 360 240" Stroke="Black" StrokeThickness="6" />

<Path Stroke="Blue"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="200 100" /> </PathFigure> </PathGeometry> </Path.Data> </Path>

<Path Stroke="Red"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="200 100" SweepDirection="Clockwise"

/>

</PathFigure> </PathGeometry> </Path.Data> </Path>

<Path Stroke="Green"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="200 100" IsLargeArc="True" /> </PathFigure> </PathGeometry> </Path.Data> </Path>

<Path Stroke="Brown"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="200 100" IsLargeArc="True" SweepDirection="Clockwise"

/>

</PathFigure>

</PathGeometry> </Path.Data> </Path> </Grid>

Если требуется построит дугу на основании эллипса, оси которого не лежат на вертикали и горизонтали, можно задать еще одно, последнее, свойство ArcSegment – RotationAngle (Угол поворота).

<Grid Background="LightCyan">

<Polyline Points="120 240, 360 240" Stroke="Black" StrokeThickness="6" />

<Path Stroke="Blue"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="200 100" RotationAngle="3 6" /> </PathFigure> </PathGeometry> </Path.Data> </Path>

<Path Stroke="Red"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="200 100" SweepDirection="Clockwise" RotationAngle="3 6" /> </PathFigure> </PathGeometry> </Path.Data> </Path>

<Path Stroke="Green"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="200 100" IsLargeArc="True" RotationAngle="3 6" /> </PathFigure> </PathGeometry> </Path.Data> </Path>

<Path Stroke="Brown"

StrokeThickness="2"> <Path.Data>

<PathGeometry>

<PathFigure StartPoint="120 240"> <ArcSegment

Point="3 60 240" Size="200 100" IsLargeArc="True" SweepDirection="Clockwise"

RotationAngle="3 6" /> </PathFigure> </PathGeometry> </Path.Data> </Path> </Grid>

Точки окружности, ограничивающие дугу, можно определить в коде алгоритмически. Эта идея положена в основу приложения SunnyDay (Солнечный день), в котором путем сочетания объектов LineSegment и ArcSegment построено сложное изображение. Экземпляр элемента Path создается в файле MainPage.xaml, и задаются значения всех его свойств, кроме самих сегментов:

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

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Path Fill="Yellow"> <Path.Data>

<PathGeometry>

<PathFigure x:Name="pathFigure" IsClosed="True" />

</PathGeometry> </Path.Data> </Path> </Grid>

После этого обработчик события Loaded должен получить размер области содержимого и вычислить все координаты всех сегментов контура:

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

public partial class MainPage : PhoneApplicationPage {

const int BEAMCOUNT = 24;

const double INCREMENT = Math.PI / BEAMCOUNT;

public MainPage() {

InitializeComponent(); Loaded += OnLoaded;

}

void OnLoaded(object sender, RoutedEventArgs args) {

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

ContentPanel.ActualHeight / 2);

double radius = 0.45 * Math.Min(ContentPanel.ActualWidth,

ContentPanel.ActualHeight); double innerRadius = 0.8 * radius;

for (int i = 0; i < BEAMCOUNT; i++) {

double radians = 2 * Math.PI * i / BEAMCOUNT;

if (i == 0) {

pathFigure.StartPoint = new Point(center.X, center.Y – radius);

}

LineSegment lineSeg = new LineSegment(); lineSeg.Point = new Point(

center.X + innerRadius * Math.Sin(radians + INCREMENT / 2),

center.Y – innerRadius * Math.Cos(radians + INCREMENT / 2)); pathFigure.Segments.Add(lineSeg);

ArcSegment arcSeg = new ArcSegment(); arcSeg.Point = new Point(

center.X + innerRadius * Math.Sin(radians + 3 * INCREMENT / 2), center.Y – innerRadius * Math.Cos(radians + 3 * INCREMENT / 2)); pathFigure.Segments.Add(arcSeg);

lineSeg = new LineSegment(); lineSeg.Point = new Point(

center.X + radius * Math.Sin(radians + 2 * INCREMENT), center.Y – radius * Math.Cos(radians + 2 * INCREMENT)); pathFigure.Segments.Add(lineSeg);

}

}

}

В результате получаем вот такое очень мультипликационное солнышко:

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

По теме:

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