Cómo: Controlar el relleno de una forma compuesta

La propiedad FillRule de un elemento GeometryGroup o un elemento PathGeometry especifica una "regla" que la forma compuesta utiliza para determinar si un punto concreto forma parte de la geometría. Hay dos posibles valores para FillRule: EvenOdd y Nonzero. En las secciones siguientes se describe cómo usar estas dos reglas.

EvenOdd: esta regla determina si un punto se encuentra en la región de relleno dibujando un radio desde ese punto hasta el infinito en cualquier dirección y contando el número de segmentos de trazado dentro la forma especificada que cruza el radio. Si este número es impar, el punto está dentro; si es par, el punto está fuera.

Por ejemplo, el siguiente XAML crea una forma compuesta formada por una serie de anillos concéntricos (destino) con un elemento FillRule establecido 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>

La siguiente ilustración muestra la forma creada en el ejemplo anterior.

Un círculo formado por anillos concéntricos de serie con colores alternados

En la ilustración anterior, observe que el centro y el tercer anillo no se han rellenado. Esto se debe a que un radio dibujado desde cualquier punto dentro de esos dos anillos pasa a través de un número par de segmentos. Vea la ilustración siguiente:

Diagrama que muestra los rayos EvenOdd dibujados en el círculo

NonZero: esta regla determina si un punto se encuentra dentro de la región de relleno del trazado dibujando un radio desde ese punto hasta el infinito en cualquier dirección y examinando después los lugares donde un segmento de la forma cruza el radio. 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 trazado 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 del trazado. 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>

Utilizando el ejemplo anterior, un valor de Nonzero para FillRule da como resultado la siguiente ilustración:

Un círculo formado por anillos concéntricos de serie todos rellenos del mismo color

Como puede ver, se rellenan todos los anillos. Esto es porque todos los segmentos se ejecutan en la misma dirección de modo que un radio 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 radio arbitrario trazado desde un punto en el anillo más profundo. A partir de un valor de cero, para cada segmento que cruza el radio, se agrega un valor de uno porque el segmento cruza el radio de izquierda a derecha.

Diagrama en el que se muestra el valor de la propiedad FillRule igual a Nonzero.

Para mostrar mejor el comportamiento de la regla Nonzero es necesaria una forma más compleja con segmentos que se ejecutan en diferentes direcciones. El código XAML siguiente crea una forma similar a la del ejemplo anterior, salvo que se crea con un PathGeometry en lugar de tener un EllipseGeometry, que crea 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>

La siguiente ilustración muestra la forma creada en el ejemplo anterior.

Un círculo formado por anillos concéntricos de serie con colores alternados con el tercer arco no relleno

Observe que no se rellena el tercer arco contando desde el centro. En la siguiente ilustración 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 radios arbitrarios que parten desde un punto en la región "no rellena". Como puede observarse en la ilustración, la suma de los valores de un radio determinado que cruza los segmentos en su trazado 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 la suma que no es igual a cero, incluido un valor negativo, sí forma parte de la geometría.

Diagrama en el que se muestran los segmentos arbitrarios que cruzan los rayos.

Nota:

Para fines de FillRule, todas las formas se consideran cerradas. Si hay un hueco en un segmento, dibuje una línea imaginaria para cerrarlo. En el ejemplo anterior, hay huecos pequeños en los anillos. Por tanto, se podría esperar que un radio que atraviesa el hueco ofrezca un resultado distinto al de un radio que se ejecuta en otra dirección. A continuación se muestra una ilustración ampliada de uno de estos huecos y el "segmento imaginario" (segmento dibujado para aplicar FillRule) que lo cierra.

Diagrama que muestra los segmentos FillRule que siempre están cerrados.

Ejemplo

Vea también