Практическое руководство. Управление заливкой составных фигур

Свойство FillRule класса GeometryGroup или 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>

На следующем рисунке показана фигура, созданная в предыдущем примере.

A circle made up of a series concentric rings with alternating colors.

Обратите внимание, что на приведенном выше рисунке центр и третье кольцо не заполнены. Это потому, что луч, нарисованный из любой точки в одном из этих двух колец проходит через четное число сегментов. См. указанный ниже рисунок:

Diagram showing the EvenOdd rays drawn in the circle.

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 дает следующую иллюстрацию в результате:

A circle made up of a series concentric rings all filled with the same color.

Как можно видеть, все кольца заполнены. Это связано с тем, что все сегменты идут в одном направлении и поэтому луч, рисуемый из любой точки, будет пересекать один или несколько сегментов и сумма пересечений не будет равна нулю. Например, на следующем рисунке красные стрелки обозначают направление рисования сегментов, а белая стрелка показывает произвольный луч, идущий от точки во внутреннем круге. Начиная с нуля, для каждого сегмента, который пересекает луч, значение 1 добавляется, так как сегмент пересекает луч слева направо.

Diagram showing the FillRule property value equal to Nonzero.

Чтобы лучше продемонстрировать поведение правила 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>

На следующем рисунке показана фигура, созданная в предыдущем примере.

A circle made up of a series concentric rings with alternating colors with the third arc not filled.

Обратите внимание, что третья дуга от центра не заполнена. На следующем рисунке показано, почему. На рисунке красные стрелки обозначают направление, в котором рисуются сегменты. Две белые стрелки показывают два произвольных луча, которые выходят из точки в «незаполненной» области. Как видно из рисунка, сумма значений для луча, пересекающего сегменты в пути, равна нулю. Как указано выше, сумма ноль означает, что точка не является частью геометрии (не является частью заливки), а сумма, которая не равна нулю, в том числе отрицательное значение, является частью геометрии.

Diagram showing arbitrary rays crossing segments.

Примечание.

Для целей FillRule все фигуры считаются закрытыми. Если в сегменте есть разрыв, нарисуйте воображаемую линию, чтобы его закрыть. В приведенном выше примере имеются небольшие разрывы в кольцах. Учитывая это, можно предположить, что луч, проходящий через разрыв, даст другой результат, чем луч, проходящий в другом направлении. Ниже приведен увеличенный рисунок одного из этих разрывов и "воображаемый сегмент" (сегмент, нарисованный для применения FillRule), который закрывает его.

Diagram showing FillRule segments that are always closed.

Пример

См. также