Как контролировать заливку составной фигуры
Свойство FillRuleGeometryGroup или PathGeometryуказывает "правило", используемое составной фигурой для определения того, является ли данная точка частью геометрии. Существует два возможных значения для FillRule: EvenOdd и Nonzero. В следующих разделах описано, как использовать эти два правила.
EvenOdd: Это правило определяет, находится ли точка в области заполнения путем рисования луча от этой точки до бесконечности в любом направлении и подсчета количества сегментов пути в заданной форме, пересекающих луч. Если это число нечетно, точка находится внутри; Если даже, точка находится вне.
Например, в приведенном ниже коде XAML создается составная фигура, состоящая из ряда концентрических кругов (целевого объекта), у которой значение FillRule установлено на EvenOdd.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<GeometryGroup FillRule="EvenOdd">
<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: Это правило определяет, находится ли точка в области заполнения пути путем рисования луча с этой точки до бесконечности в любом направлении, а затем проверяет места, где сегмент фигуры пересекает луч. Начиная с нуля, добавьте один каждый раз, когда сегмент пересекает луч слева направо, и вычтите один каждый раз, когда сегмент пути пересекает луч справа налево. После подсчета пересечений, если результат равен нулю, точка находится за пределами пути. В противном случае он находится внутри.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<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>
В предыдущем примере значение Nonzero для FillRule приводит к следующему рисунку:
Как видите, все кольца заполнены. Это связано с тем, что все сегменты направлены в одном направлении, поэтому луч, нарисованный из любой точки, пересечёт один или несколько сегментов, и сумма пересечений не будет равна нулю. Например, на следующем рисунке красные стрелки представляют направление, в котором нарисуются сегменты, и белая стрелка представляет произвольный луч, работающий с точки в самом внутреннем кольце. Начиная с нуля, для каждого сегмента, пересекаемого лучом, добавляется значение одного, так как сегмент пересекает луч слева направо.
Для лучшего показа поведения правила Nonzero требуется более сложная фигура с сегментами, направленными в разных направлениях. Приведенный ниже код XAML создает аналогичную фигуру, как и в предыдущем примере, за исключением того, что она создается с помощью PathGeometry вместо EllipseGeometry, что приводит к созданию четырех концентрических дуг, а не полностью закрытых концентрических кругов.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<GeometryGroup FillRule="NonZero">
<PathGeometry>
<PathGeometry.Figures>
<!-- Inner Ring -->
<PathFigure StartPoint="10,120">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="50,50" IsLargeArc="True" SweepDirection="CounterClockwise" Point="25,120" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Second Ring -->
<PathFigure StartPoint="10,100">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="70,70" IsLargeArc="True" SweepDirection="CounterClockwise" Point="25,100" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Third Ring (Not part of path) -->
<PathFigure StartPoint="10,70">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="100,100" IsLargeArc="True" SweepDirection="CounterClockwise" Point="25,70" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Outer Ring -->
<PathFigure StartPoint="10,300">
<PathFigure.Segments>
<ArcSegment Size="130,130" IsLargeArc="True" SweepDirection="Clockwise" Point="25,300" />
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</GeometryGroup>
</Path.Data>
</Path>
На следующем рисунке показана фигура, созданная в предыдущем примере.
Обратите внимание, что третья дуга из центра не заполнена. На следующем рисунке показано, почему это так. На рисунке красные стрелки представляют направление, куда рисуются сегменты. Две белые стрелки представляют два произвольных луча, которые исходят из точки в "незаполненной" области. Как видно на рисунке, сумма значений луча, пересекающего сегменты в своем пути, равна нулю. Как определено выше, сумма нуля означает, что точка не является частью геометрии (не частью заливки), а сумма, которая не ноль, включая отрицательное значение, является частью геометрии.
Заметка
В целях FillRuleвсе фигуры считаются закрытыми. Если в сегменте есть разрыв, нарисуйте мнимую линию, чтобы закрыть ее. В приведенном выше примере есть небольшие пробелы в кольцах. Учитывая это, можно ожидать, что луч, который проходит через разрыв, мог бы дать другой результат, чем луч, движущийся в другом направлении. Ниже приведена увеличенная иллюстрация одного из этих пробелов и "мнимый сегмент" (сегмент, нарисованный для целей применения FillRule) который закрывает его.
Пример
См. также
.NET Desktop feedback