Cómo: Controlar el relleno de una forma compuesta
Actualización: noviembre 2007
La propiedad FillRule de un GeometryGroup o una PathGeometry, especifica una "regla" que la forma compuesta utiliza para determinar si un punto determinado forma parte de la geometría. Existen dos valores posibles para FillRule: EvenOdd y Nonzero. En las secciones siguientes se describe cómo utilizar estas dos reglas.
EvenOdd: esta regla determina si un punto está en la región de relleno; para ello, dibuja un rayo desde ese punto al infinito en cualquier dirección y cuenta el número de segmentos de la trayectoria dentro de la forma dada que el rayo cruza. Si este número es impar, el punto se encuentra dentro de la región; si es par, el punto está fuera.
Por ejemplo, el XAML siguiente crea una forma compuesta de una serie de anillos concéntricos (destino) con una propiedad FillRule establecida en 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>
En la ilustración siguiente se muestra la forma creada en el ejemplo anterior.
En la ilustración anterior, observe que no se rellenan el centro ni el tercer anillo. Esto se debe a que el rayo dibujado desde cualquier punto situado en el interior de cualquiera de esos dos anillos pasa a través de un número par de segmentos. Vea la ilustración siguiente:
NonZero: esta regla determina si un punto está en la región de relleno de la trayectoria; para ello, dibuja un rayo desde ese punto al infinito en cualquier dirección y, a continuación, examina los lugares donde un segmento de la forma cruza el rayo. Partiendo de cero, sume una ubicación cada vez que un segmento cruce el radio de izquierda a derecha y reste una ubicación cada vez que un segmento de la trayectoria cruce el radio de derecha a izquierda. Después de contar el número de veces que cruza, si el resultado es cero, el punto está fuera de la trayectoria. De lo contrario, 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>
Con el ejemplo anterior, un valor de Nonzero para FillRule proporciona como resultado la ilustración siguiente:
Como puede ver, se rellenan todos los anillos. Esto se debe a que todos los segmentos están dibujados en la misma dirección y, por tanto, un rayo dibujado desde cualquier punto cruzará uno o más segmentos, y la suma de las veces que cruza no será igual a cero. Por ejemplo, en la ilustración siguiente, las flechas rojas representan la dirección en que se dibujan los segmentos y la flecha blanca representa un rayo arbitrario trazado desde un punto situado en el anillo más profundo. Partiendo de cero, se suma una unidad cada vez que el rayo cruza un segmento, porque el segmento cruza el radio de izquierda a derecha.
Para ilustrar mejor el comportamiento de la regla Nonzero, se necesita una forma más compleja con segmentos trazados en distintas direcciones. En el código XAML que se muestra a continuación se crea una forma similar a la del ejemplo anterior, pero con PathGeometry en lugar de con EllipseGeometry, de manera que se crean cuatro arcos concéntricos en lugar de círculos concéntricos totalmente cerrados.
<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>
En la ilustración siguiente se muestra la forma creada en el ejemplo anterior.
Observe que no se rellena el tercer arco contando desde el centro. En la ilustración siguiente se muestra por qué. En la ilustración, las flechas rojas representan la dirección en que se dibujan los segmentos. Las dos flechas blancas representan dos rayos arbitrarios que parten desde un punto situado en la región "no rellena". Como se puede apreciar en la ilustración, la suma de los valores de un rayo determinado que cruza los segmentos que encuentra en su trayectoria, es cero. Como se ha definido anteriormente, una suma de cero significa que el punto no forma parte de la geometría (no es parte del relleno) mientras que si la suma es distinta de cero, aunque sea un valor negativo, sí forma parte de la geometría.
Nota: para los fines de FillRule, todas las formas se consideran cerradas. Si existe una abertura en un segmento, dibuje una línea imaginaria para cerrarlo. En el ejemplo anterior, hay pequeñas aberturas en los anillos. En semejante caso, cabría esperar que un rayo trazado a través de las aberturas diese un resultado distinto que uno que se trazase en otra dirección. A continuación se muestra una ilustración ampliada de una de estas aberturas y el "segmento imaginario" (segmento que se dibuja para aplicar FillRule) que lo cierra.
Vea también
Tareas
Cómo: Crear una forma compuesta