Comment : contrôler le remplissage d'une forme composite

La propriété FillRule d'un GeometryGroup ou d'un PathGeometry spécifie une « règle » que la forme composite utilise pour déterminer si un point donné fait partie de la géométrie. Il existe deux valeurs possibles pour FillRule: EvenOdd et Nonzero. Les sections suivantes décrivent comment utiliser ces deux règles.

EvenOdd : Cette règle détermine si un point se trouve dans la région de remplissage en dessinant un rayon qui part de ce point jusqu'à l'infini dans n'importe quelle direction et en comptant le nombre de segments du tracé se trouvant dans la forme donnée que le rayon traverse. Si ce nombre est impair, le point se trouve à l'intérieur ; s'il est pair, le point se trouve à l'extérieur.

Par exemple, le code XAML suivant crée une forme composite comprenant une série d'anneaux concentriques (c'est-à-dire une cible) avec une FillRule ayant pour valeur 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>

L'illustration suivante montre la forme créée dans l'exemple précédent.

Capture d'écran : propriété FillRule de EvenOdd

Dans l'illustration précitée, remarquez que le centre et le 3e anneau ne sont pas remplis. En effet, un rayon tracé à partir de tout point dans l'un ou l'autre de ces deux anneaux traverse un nombre pair de segments. Voyez l'illustration ci-dessous :

Diagramme : valeur de propriété FillRule de EvenOdd

NonZero : Cette règle détermine si un point se trouve dans la région de remplissage du tracé en dessinant un rayon qui part de ce point jusqu'à l'infini dans n'importe quelle direction et en examinant ensuite les emplacements où un segment de la forme croise le rayon. En commençant à zéro, ajoutez un chaque fois qu'un segment traverse le rayon de gauche à droite et soustrayez un chaque fois qu'un segment de trajectoire traverse le rayon de droite à gauche. Après avoir compté les intersections, si le résultat est nul, le point se trouve à l'extérieur du tracé. Sinon, il se trouve à l'intérieur.

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

À partir de l'exemple précité, la valeur Nonzero affectée à FillRule produit l'illustration suivante :

Capture d'écran : valeur FillRule de NonZero

Comme vous pouvez le voir, tous les anneaux sont remplis. En effet, tous les segments sont orientés dans la même direction, de telle sorte qu'un rayon dessiné à partir de tout point traversera un ou plusieurs segments, et la somme des intersections ne sera pas égale à zéro. Par exemple, dans l'illustration ci-dessous, les flèches rouges représentent le sens dans lequel les segments sont dessinés et la flèche blanche représente un rayon arbitraire partant d'un point dans l'anneau central. Lorsque vous démarrez avec une valeur égale à zéro, pour chaque segment que le rayon traverse, une valeur égale à un est ajoutée parce que le segment traverse le rayon de gauche à droite.

Diagramme : valeur de propriété FillRule égale à NonZero

Pour mieux illustrer le comportement de la règle Nonzero, il convient de prendre une forme plus complexe avec des segments qui partent dans des directions différentes. Le code XAML ci-dessous crée une forme semblable à l'exemple précédent, si ce n'est qu'elle est créée avec une PathGeometry au lieu d'une EllipseGeometry, qui crée quatre arcs concentriques au lieu de cercles concentriques complètement fermés.

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

L'illustration suivante montre la forme créée dans l'exemple précédent.

Capture d'écran : valeur de propriété FillRule de NonZero

Remarquez que le troisième arc à partir du centre n'est pas rempli. L'illustration suivante montre la raison de ce phénomène. Dans l'illustration, les flèches rouges représentent la direction dans laquelle les segments sont dessinés. Les deux flèches blanches représentent deux rayons arbitraires qui partent d'un point dans la région « non remplie ». Comme vous pouvez le voir dans l'illustration, la somme des valeurs d'un rayon donné qui traverse les segments dans son tracé est nulle. Comme indiqué ci-dessus, une somme égale à zéro signifie que le point ne fait pas partie de la géométrie (et donc du remplissage) tandis qu'une somme qui n'est pas égale à zéro, y compris une valeur négative, fait partie de la géométrie.

Diagramme : valeur de propriété FillRule de NonZero

Remarque : Pour les besoins de FillRule, toutes les formes sont considérées comme fermées. Si un segment est ouvert, dessinez une ligne imaginaire pour le fermer. Dans l'exemple précité, les anneaux ne sont pas fermés. Il est dès lors possible qu'un rayon qui traverse l'intervalle donne un autre résultat qu'un rayon qui part dans une autre direction. Vous trouverez ci-dessous une illustration agrandie d'un segment ouvert et de son « prolongement imaginaire » (segment dessiné afin d'appliquer la FillRule) qui le ferme.

Diagramme : Pour FillRule, les segments sont toujours fermés

Voir aussi

Tâches

Comment : créer une forme composite

Concepts

Vue d'ensemble de Geometry