Procedura: controllare il riempimento di una forma composta
Aggiornamento: novembre 2007
La proprietà FillRule di un oggetto GeometryGroup o PathGeometry specifica una "regola" utilizzata dalla forma composta per determinare se un punto dato fa parte della geometria. Sono previsti due possibili valori per FillRule: EvenOdd e Nonzero. Nelle sezioni seguenti sarà descritto l'utilizzo di queste due regole.
EvenOdd: questa regola determina se un punto si trova all'interno dell'area di riempimento tracciando un raggio da tale punto all'infinito in qualsiasi direzione ed eseguendo il conteggio del numero di segmenti di percorso incrociati dal raggio all'interno della forma data. Se questo numero è dispari, il punto si trova all'interno dell'area. Se invece è pari, il punto si trova all'esterno.
Ad esempio, di seguito viene creata una forma composta in XAML, costituita da una serie di anelli concentrici (bersaglio), impostando FillRule su 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>
Nella figura seguente viene mostrata la forma creata nell'esempio precedente.
Nell'immagine precedente, notare che la porzione centrale e il terzo anello non sono riempiti. Ciò è dovuto al fatto che un raggio tracciato a partire da qualsiasi punto all'interno di uno di questi due anelli passa attraverso un numero pari di segmenti. Vedere l'illustrazione di seguito:
NonZero: questa regola determina se un punto si trova nell'area di riempimento del percorso tracciando un raggio da tale punto all'infinito in qualsiasi direzione e verificando quindi i punti in cui un segmento della forma incrocia il raggio. Iniziando il conteggio da zero, aggiungere un'unità ogni volta che un segmento incrocia il raggio da sinistra a destra e sottrarre un'unità ogni volta che il raggio viene incrociato da destra a sinistra. Se al termine del conteggio degli incroci il risultato è pari a zero, il punto si trova al di fuori del percorso. In caso contrario, si troverà all'interno.
<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>
Se si utilizza l'esempio riportato in precedenza, un valore di Nonzero per FillRule restituisce come risultato la seguente immagine:
Tutti gli anelli sono riempiti. Ciò è dovuto al fatto che tutti i segmenti si sviluppano nella stessa direzione; pertanto un raggio tracciato da qualsiasi punto incrocerà uno o più segmenti e la somma degli incroci non sarà uguale a zero. Nell'illustrazione riportata di seguito, ad esempio, le frecce rosse rappresentano la direzione in cui vengono tracciati i segmenti, mentre la freccia bianca rappresenta un raggio qualsiasi che parte da un punto dell'anello più interno. Cominciando da zero, per ciascun segmento incrociato dal raggio viene aggiunto un valore pari a uno, dal momento che il segmento incrocia il raggio da sinistra a destra.
Per illustrare meglio il comportamento della regola Nonzero, è richiesta una forma più complessa con segmenti che si estendono in diverse direzioni. Il codice XAML indicato di seguito crea una forma simile a quella dell'esempio precedente, con la differenza che si utilizza PathGeometry piuttosto che EllipseGeometry e vengono pertanto creati quattro archi concentrici al posto dei cerchi concentrici completamente chiusi.
<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>
Nella figura seguente viene mostrata la forma creata nell'esempio precedente.
Notare che il terzo arco a partire dal centro non è riempito. Il motivo è illustrato nell'immagine riportata di seguito. Nella figura, le frecce rosse rappresentano la direzione in cui sono tracciati i segmenti. Le due frecce bianche rappresentano invece due raggi qualsiasi che partono da un punto dell'area "non riempita". Come è possibile osservare dall'immagine, la somma dei valori dati dagli incroci di un raggio specificato con i segmenti lungo il relativo percorso è zero. Come precisato in precedenza, una somma pari a zero indica che il punto non fa parte della geometria (non è incluso nel riempimento), mentre una somma che non è pari a zero, compresi i valori negativi, fa parte della geometria.
Nota: ai fini di FillRule, tutte le forme vengono considerate chiuse. Se in un segmento è presente uno spazio vuoto, disegnare una linea immaginaria per chiuderlo. Nell'esempio precedente, gli anelli presentano dei piccoli spazi vuoti. Pertanto, ci si aspetterebbe che un raggio che attraversa l'apertura determini un risultato diverso rispetto a un raggio tracciato in un'altra direzione. Di seguito è riportata un'immagine ingrandita di uno di questi spazi vuoti e del "segmento immaginario" (tracciato allo scopo di applicare FillRule) che lo chiude.
Vedere anche
Attività
Procedura: creare una forma composta