方法 : 複合図形の塗りつぶしを制御する
GeometryGroup または PathGeometry の FillRule プロパティは、複合図形において、指定された点がジオメトリの一部かどうかを判別するために使用される "規則" を指定します。 FillRule に指定できる値は、EvenOdd と Nonzero の 2 つです。 以下のセクションでは、これら 2 つの規則の使用方法を説明します。
EvenOdd : この規則では、ある点から任意の方向に無限に伸びる射線を描画し、その射線が横断する指定した図形のパス セグメントの数をカウントすることにより、その点が塗りつぶし領域の内側にあるかどうかを判断します。 この数が奇数の場合、点は内側にあり、偶数の場合は外側にあります。
たとえば、次に示す XAML では、FillRule を 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>
前の例で作成した図を次に示します。
上の図で、中央と 3 番目のリングは塗りつぶされていないことに注意してください。 これは、これら 2 つのリングの任意の点から描画された射線が、偶数個のセグメントを通過するためです。 次の図を参照してください。
NonZero : この規則では、ある点から任意の方向に無限に伸びる射線を描画し、図形のセグメントがこの射線と交わる場所を調べることにより、その点がパスの塗りつぶし領域の内側にあるかどうかを判断します。 0 からカウントを開始し、パス セグメントが左から右に射線と交わるたびに 1 を加算し、パス セグメントが右から左に射線と交わるたびに 1 を減算します。 このカウントの結果がゼロの場合、点はパスの外側となります。 それ以外の場合は内側です。
<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>
前の例で言えば、FillRule の値に Nonzero を指定すると、結果は次の図のようになります。
上の図からわかるように、すべてのリングが塗りつぶされます。 これは、すべてのセグメントが同じ方向で、任意の点から描画された射線は 1 つ以上のセグメントと交わり、交差の合計が 0 にならないためです。 たとえば、次の図で、赤い矢印はセグメントが描画されている方向を表し、白い矢印は最も内側のリング内にある点から伸びる任意の射線を表しています。 値 0 から開始し、セグメントは左から右に射線と交わるため、射線が交わるセグメントごとに値 1 が加算されます。
Nonzero 規則の動作をよりわかりやすく説明するには、方向の異なる複数のセグメントから構成される、より複雑な図形が必要になります。 次に示す XAML コードでは、前の例と似た図形を作成しますが、EllipseGeometry の代わりに PathGeometry を使用して、完全に閉じた同心円ではなく、4 つの同心の円弧を作成している点で異なっています。
<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>
前の例で作成した図を次に示します。
中央から 3 番目の円弧が塗りつぶされていないことに注意してください。 その理由を次の図に示します。 この図で、赤い矢印はセグメントが描画されている方向を表しています。 2 つの白い矢印は、"塗りつぶされていない" 領域内の点から外に向かって伸びる、2 本の任意の射線を表しています。 この図からわかるように、指定された射線がパス内でセグメントと交わることによる値の合計は 0 です。 前に定義したように、合計が 0 となるのは、その点がジオメトリの一部でない (塗りつぶしに含まれない) ことを意味し、合計が 0 でない場合 (負の値を含む) は、その点がジオメトリの一部であることを意味しています。
メモ : FillRule では、すべての図形は閉じていると見なされます。 セグメントにすき間がある場合は、架空の線を描画して、そのすき間を閉じます。 上の例では、リングに小さなすき間があります。 この場合、このすき間を通る射線は、別の方向に伸びる射線とは異なる結果を生じると思えるかもしれません。 これらのすき間の 1 つと、そのすき間を閉じる "架空のセグメント" (FillRule を適用するために描画されるセグメント) を拡大した図を次に示します。