Como controlar o preenchimento de uma forma composta
A propriedade FillRule de um GeometryGroup ou um PathGeometry, especifica uma "regra" que a forma 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: Esta regra determina se um ponto está na região de preenchimento ao traçar um raio desse ponto até ao infinito em qualquer direção e contar o número de segmentos de percurso da forma dada que o raio atravessa. Se este número for ímpar, o ponto está dentro; se for par, o ponto está fora.
Por exemplo, o XAML abaixo cria uma forma composta composta por uma série de anéis concêntricos (destino) com um FillRule definido 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.
Na ilustração anterior, observe que o centro e o terceiro anel não estão preenchidos. Isso ocorre porque um raio extraído 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:
Não Nulo: Esta regra determina se um ponto está na região de preenchimento do caminho ao desenhar um raio desse ponto até o infinito em qualquer direção e depois examina os pontos onde um segmento da forma cruza o raio. Começando com uma contagem de zero, adicione um de cada vez que um Segmento cruzar o raio da esquerda para a direita e subtraia um de cada vez que um segmento de caminho cruzar o raio da direita para a esquerda. Depois de contar as travessias, se o resultado for zero, então o ponto está fora do caminho. Caso contrário, está 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 para FillRule dá a seguinte ilustração como resultado:
Como você pode ver, todos os anéis estão cheios. Isso ocorre porque todos os segmentos estão correndo na mesma direção e, portanto, um raio tirado de qualquer ponto cruzará um ou mais segmentos e a soma das travessias 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. Começando com um valor de zero, para cada segmento que o raio cruza, um valor de um é adicionado porque o segmento cruza o raio da esquerda para a direita.
Para demonstrar melhor o comportamento da regra Nonzero, é necessária uma forma mais complexa, com segmentos a correr em direções diferentes. O código XAML abaixo cria uma forma semelhante ao exemplo anterior, exceto que ele é criado com um PathGeometry em vez de um EllipseGeometry 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.
Observe que o terceiro arco 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 pode ser visto na ilustração, a soma dos valores de um dado raio cruzando os segmentos em seu caminho é zero. Como definido acima, uma soma de 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.
Observação
Para efeitos de 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, há pequenas lacunas nos anéis. Dado isso, pode-se esperar que um raio que percorre a lacuna dê um resultado diferente de um raio correndo em 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 o fecha.
Exemplo
Ver também
- Criar uma forma composta
- Visão geral da geometria
.NET Desktop feedback