HOW TO:控制複合圖案的填色
更新:2007 年 11 月
GeometryGroup 或 PathGeometry 的 FillRule 屬性會指定供複合圖案使用的「規則」,以判斷指定點是否屬於幾何的一部分。FillRule 有兩個可能值:EvenOdd 和 Nonzero。下列小節將描述如何使用這兩個規則。
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>
下圖顯示在上述範例中建立的圖案。
在上面的圖例中,請注意中央和第三個環並沒有填滿。這是因為在這兩個環內任何點繪製出的射線,都會穿越過偶數的區段數。請參閱下圖:
NonZero:這個規則會判斷某個點是否在路徑的填滿區域內,方法是從該點往任何方向畫一條無限長的射線,然後檢視圖案的區段與這條射線交叉的位置。從零開始算起,每當有區段由左至右穿過這條射線時就加一,而每當有路徑區段由右至左穿過這條射線時就減一。如果在計算交叉後得到的結果是零,則這個點是在路徑外。否則會在內部。
<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 值會產生下圖這樣的結果:
如您所見,所有的環都已填滿。這是因為所有區段都是朝同一個方向,所以從任何點繪製出的射線,都會穿越過一個或多個區段,而這些交叉總合不會等於零。舉例來說,在下圖中,紅色箭號代表區段繪製的方向,而白色箭號代表從最裡面環的某點出發的任意射線。從零開始算起,每當射線穿過區段時值就加一,因為區段是由左至右穿越射線。
為了要為 Nonzero 規則行為提供更佳的示範,需要有一個較為複雜的圖案,而區段要從不同方向出發。下列 XAML 程式碼會建立類似上述範例的圖案,不同的是這是以 PathGeometry 建立的四個同心弧狀,而非 EllipseGeometry 建立的完全封閉同心圓。
<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>
下圖顯示在上述範例中建立的圖案。
請注意,第三個從中央出發的弧並沒有填滿。下圖顯示為什麼會這樣。圖中的紅色箭號代表區段繪製的方向。而兩個白色箭號代表從「未填滿」區域的某點出發的兩條任意射線。如圖所示,指定射線在路徑上穿越的區段值總合為零。依據以上定義,總合為零代表該點不是幾何的一部分 (不屬於填滿部分),而總合「不」為零 (包含負值) 則為幾何的一部分。
**注意:**基於 FillRule 的目的,所有圖案都視為封閉的。如果區段中有缺口,請繪製虛構的線來封閉區段。在上述範例中,環當中有小型的缺口。基於這個樣子,有人可能會認為穿過缺口的射線,可能會跟另一個方向的射線產生不同的結果。以下是其中一個缺口的放大圖,以及封閉該缺口的「虛構區段」(繪製區段的目的在於套用 FillRule)。