Xamarin.Forms Фигуры: правила заливки
Несколько Xamarin.Forms классов фигур имеют FillRule
свойства типа FillRule
. Это операторы Polygon
, Polyline
, а также GeometryGroup
.
Перечисление FillRule
определяет EvenOdd
и Nonzero
элементы. Каждый элемент представляет собой другое правило для определения того, находится ли точка в области заливки фигуры.
Внимание
Все фигуры считаются закрытыми для целей правил заполнения.
EvenOdd
EvenOdd
Правило заливки рисует луч от точки до бесконечности в любом направлении и подсчитывает количество сегментов в фигуре, которую луч пересекает. Если это число нечетно, точка находится внутри. Если это число даже, точка находится вне.
В следующем примере XAML создается составная фигура FillRule
и отрисовывается EvenOdd
по умолчанию:
<Path Stroke="Black"
Fill="#CCCCFF"
Aspect="Uniform"
HorizontalOptions="Start">
<Path.Data>
<!-- FillRule doesn't need to be set, because EvenOdd is the default. -->
<GeometryGroup>
<EllipseGeometry RadiusX="50"
RadiusY="50"
Center="75,75" />
<EllipseGeometry RadiusX="70"
RadiusY="70"
Center="75,75" />
<EllipseGeometry RadiusX="100"
RadiusY="100"
Center="75,75" />
<EllipseGeometry RadiusX="120"
RadiusY="120"
Center="75,75" />
</GeometryGroup>
</Path.Data>
</Path>
В этом примере отображается составная фигура, состоящей из ряда концентрических кругов:
В составной фигуре обратите внимание, что центр и третие кольца не заполнены. Это связано с тем, что луч, нарисованный из любой точки в пределах одного из этих двух кругов, проходит через четное число сегментов:
На изображении выше красные круги представляют точки, а линии представляют произвольные лучи. Для верхней точки два произвольных лучей каждый проходит через четное количество сегментов линии. Таким образом, кольцо, в который находится точка, не заполнена. Для нижней точки два произвольных лучей каждый проходит через нечетное количество сегментов линии. Таким образом, кольцо, в который находится точка, заполняется.
Ненулевой
Nonzero
Правило заливки рисует луч от точки до бесконечности в любом направлении, а затем проверяет места, где сегмент фигуры пересекает луч. Начиная с числа ноль, число увеличивается каждый раз, когда сегмент пересекает луч слева направо и уменьшается каждый раз, когда сегмент пересекает луч справа налево. После подсчета пересечений, если результат равен нулю, точка находится за пределами многоугольника. В противном случае это внутри.
Следующий пример XAML создает и отрисовывает составную фигуру с заданным значением FillRule
Nonzero
:
<Path Stroke="Black"
Fill="#CCCCFF"
Aspect="Uniform"
HorizontalOptions="Start">
<Path.Data>
<GeometryGroup FillRule="Nonzero">
<EllipseGeometry RadiusX="50"
RadiusY="50"
Center="75,75" />
<EllipseGeometry RadiusX="70"
RadiusY="70"
Center="75,75" />
<EllipseGeometry RadiusX="100"
RadiusY="100"
Center="75,75" />
<EllipseGeometry RadiusX="120"
RadiusY="120"
Center="75,75" />
</GeometryGroup>
</Path.Data>
</Path>
В этом примере отображается составная фигура, состоящей из ряда концентрических кругов:
В составной фигуре обратите внимание, что все кольца заполнены. Это связано с тем, что все сегменты выполняются в одном направлении, и поэтому луч, нарисованный из любой точки, будет пересекать один или несколько сегментов, и сумма пересечений не будет равно нулю:
На изображении над красными стрелками отображается направление, в котором нарисуются сегменты, а черная стрелка представляет произвольный луч, работающий с точки в самом внутреннем кольце. Начиная с нуля, для каждого сегмента, который пересекает луч, значение 1 добавляется, так как сегмент пересекает луч слева направо.
Более сложная фигура с сегментами, работающими в разных направлениях, необходима, чтобы лучше продемонстрировать поведение Nonzero
правила заливки. В следующем примере XAML создается аналогичная фигура предыдущего примера, за исключением того, что она создана с PathGeometry
помощью EllipseGeometry
:
<Path Stroke="Black"
Fill="#CCCCFF">
<Path.Data>
<GeometryGroup FillRule="Nonzero">
<PathGeometry>
<PathGeometry.Figures>
<!-- Inner ring -->
<PathFigure StartPoint="120,120">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="50,50"
IsLargeArc="True"
SweepDirection="CounterClockwise"
Point="140,120" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Second ring -->
<PathFigure StartPoint="120,100">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="70,70"
IsLargeArc="True"
SweepDirection="CounterClockwise"
Point="140,100" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Third ring -->
<PathFigure StartPoint="120,70">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="100,100"
IsLargeArc="True"
SweepDirection="CounterClockwise"
Point="140,70" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Outer ring -->
<PathFigure StartPoint="120,300">
<PathFigure.Segments>
<ArcSegment Size="130,130"
IsLargeArc="True"
SweepDirection="Clockwise"
Point="140,300" />
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</GeometryGroup>
</Path.Data>
</Path>
В этом примере нарисуется ряд сегментов дуги, которые не закрываются:
На приведенном выше изображении третья дуга из центра не заполнена. Это связано с тем, что сумма значений из заданного луча, пересекающих сегменты в пути, равна нулю:
На изображении выше красный круг представляет точку, черные линии представляют произвольные лучи, которые перемещаются с точки в незаполнения области, а красные стрелки представляют направление сегментов. Как видно, сумма значений от лучей, пересекающих сегменты, равна нулю:
- Произвольный луч, который перемещается по диагонали вправо, пересекает два сегмента, которые выполняются в разных направлениях. Таким образом, сегменты отменяют друг друга, давая значение нуля.
- Произвольный луч, который перемещается по диагонали слева пересекает в общей сложности шесть сегментов. Однако пересечения отменяют друг друга, чтобы ноль является окончательной суммой.
Сумма нулевых результатов в кольце не заполняется.