如何:控制复合形状的填充

GeometryGroupPathGeometryFillRule 属性指定复合形状用于确定给定点是否属于几何图形的“规则”。 FillRule 有两个可能的值:EvenOddNonzero。 以下各节将介绍如何使用这两个规则。

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>

下图显示在上一个示例中创建的形状。

A circle made up of a series concentric rings with alternating colors.

在前面的图中,请注意,中心和第三个环并未填充。 这是因为射线是从穿过偶数段的这两个环中的点绘制的。 请参阅下图:

Diagram showing the EvenOdd rays drawn in the circle.

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>

使用前面的示例,FillRuleNonzero 值的结果如下图所示:

A circle made up of a series concentric rings all filled with the same color.

如图所示,所有环都已填充。 这是因为所有段按同一方向运行,因此从任一点绘制的射线将与一个或多个段相交,并且交点总数不会等于零。 例如,在下图中,红色箭头表示段的绘制方向,白色箭头表示从最内部环中的某一个点运行的任意一条射线。 从零值开始,对于该射线相交的每个段,会加值“1”,因为从左到右该段与该射线相交。

Diagram showing the FillRule property value equal to 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>

下图显示在上一个示例中创建的形状。

A circle made up of a series concentric rings with alternating colors with the third arc not filled.

请注意,自中心数起的第三条弧未填充。 下图说明了原因。 在图中,红色箭头表示绘制段的方向。 两个白色箭头表示以“未填充”区域中的某个点为起点绘制的任意两条射线。 如图所示,与给定射线路径中的段相交的该射线值的总和为零。 如上文所定义,总和为零意味着该点不是几何图形的一部分(也不是填充的一部分),而总和为零(包括负值)意味着该点是几何图形的一部分。

Diagram showing arbitrary rays crossing segments.

注意

为了实现 FillRule,所有形状都视为闭合。 如果段中存在间隙,请绘制用于闭合该段的假想线。 在以上示例中,环中存在多个较小间隙。 考虑到这一点,人们可能希望穿过间隙的射线产生不同的结果,然后射线在另一个方向上运行。 放大图中含有以下内容:这些间隙之一,以及用于闭合该间隙的“假想段”(为应用 FillRule 而绘制的段)。

Diagram showing FillRule segments that are always closed.

示例

另请参阅