Como controlar o preenchimento de uma forma composta

A FillRule propriedade de a ou a , especifica uma "regra" que a GeometryGroupPathGeometryforma composta usa para determinar se um determinado ponto faz parte da geometria. Há dois valores possíveis para FillRule: EvenOdd e Nonzero. As seções a seguir descreverão como usar essas duas regras.

EvenOdd: essa regra que determina se um ponto está na região de preenchimento desenhando um raio desse ponto até o infinito em qualquer direção e contando o número de segmentos de caminho dentro da forma especificada que o raio cruza. Se esse número for ímpar, o ponto estará dentro, se for par, o ponto estará fora.

Por exemplo, o XAML abaixo cria uma forma composta composta por uma série de anéis concêntricos (destino) com um FillRule conjunto como 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 ilustração a seguir mostra a forma criada no exemplo anterior.

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

Na ilustração anterior, observe que o centro e o terceiro anel não estão preenchidos. Isso ocorre porque um raio desenhado de qualquer ponto dentro de qualquer um desses dois anéis passa por um número par de segmentos. Veja a ilustração a seguir:

Diagram showing the EvenOdd rays drawn in the circle.

NonZero: essa regra determina se um ponto está na região de preenchimento do caminho desenhando um raio desse ponto até o infinito em qualquer direção e então examinando os locais em que um segmento da forma cruza o raio. Começando com uma contagem de zero, adicione um sempre que um segmento cruzar o raio da esquerda para a direita e subtraia um sempre que um segmento de caminho cruzar o raio da direita para a esquerda. Após a contagem de cruzamentos, se o resultado for zero, o ponto estará fora do caminho. Caso contrário, ele estará dentro.

<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>

Usando o exemplo anterior, um valor de Nonzero for FillRule fornece a seguinte ilustração como resultado:

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

Como podemos ver, todos os anéis estão preenchidos. Isso acontece porque todos os segmentos estão indo na mesma direção, assim, um raio desenhado de qualquer ponto cruzará um ou mais segmentos e a soma dos cruzamentos não será igual a zero. Por exemplo, na ilustração a seguir, as setas vermelhas representam a direção em que os segmentos são desenhados e a seta branca representa um raio arbitrário que corre de um ponto no anel mais interno. Iniciando com um valor de zero, para cada segmento que o raio cruza, um valor de um é adicionado, já que o segmento cruza o raio da esquerda para a direita.

Diagram showing the FillRule property value equal to Nonzero.

Para demonstrar melhor o comportamento da Nonzero regra é necessária uma forma mais complexa com segmentos rodando em direções diferentes. O código XAML abaixo cria uma forma semelhante ao exemplo anterior, exceto que ele é criado com um em vez de um PathGeometryEllipseGeometry que cria quatro arcos concêntricos em vez de círculos concêntricos totalmente fechados.

<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 ilustração a seguir mostra a forma criada no exemplo anterior.

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

Observe que o terceiro arco partindo do centro não está preenchido. A ilustração a seguir mostra por que isso acontece. Na ilustração, as setas vermelhas representam a direção em que os segmentos são desenhados. As duas setas brancas representam dois raios arbitrários que se movem para fora de um ponto na região "não preenchida". Como podemos ver na ilustração, a soma dos valores de um determinado raio que cruza os segmentos no seu caminho é zero. Conforme definido acima, uma soma zero significa que o ponto não faz parte da geometria (não faz parte do preenchimento), enquanto uma soma que não é zero, incluindo um valor negativo, faz parte da geometria.

Diagram showing arbitrary rays crossing segments.

Observação

Para os fins do FillRule, todas as formas são consideradas fechadas. Se houver uma lacuna em um segmento, desenhe uma linha imaginária para fechá-la. No exemplo acima, existem pequenas lacunas nos anéis. Devido a isso, seria possível erar que um raio que passasse pela lacuna gerasse um resultado diferente que um raio indo para outra direção. Abaixo está uma ilustração ampliada de uma dessas lacunas e do "segmento imaginário" (segmento que é desenhado para fins de aplicação do FillRule) que a fecha.

Diagram showing FillRule segments that are always closed.

Exemplo

Confira também