填充规则
多个 .NET Multi-platform App UI (.NET MAUI) 形状类包含类型为 FillRule 的 FillRule
属性。 其中包括 Polygon、Polyline 和 GeometryGroup。
FillRule 枚举定义 EvenOdd 和 Nonzero 成员。 每个成员表示一个不同的规则,用于确定某个点是否位于形状的填充区域中。
重要
出于填充规则考虑,所有形状都视为闭合。
EvenOdd
EvenOdd 填充规则从一点向任意方向绘制射向无穷远的射线,并计算射线穿过的形状内的线段数。 如果此数字为奇数,则点位于内部。 如果此数字为偶数,则点位于外部。
以下 XAML 示例创建并呈现复合形状,其中 FillRule
默认为 EvenOdd:
<Path Stroke="Black"
Fill="#CCCCFF"
Aspect="Uniform"
HorizontalOptions="Start">
<Path.Data>
<!-- FillRule doesn't need to be set, because EvenOdd is the default. -->
<GeometryGroup>
<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 填充规则从一点向任意方向绘制射向无穷远的射线,然后检查一段形状与射线相交的位置。 从零计数开始,每当线段从左到右与射线相交时,计数就会递增;每当线段从右向左与射线相交时,计数就会递减。 在对交叉点进行计数后,如果结果为零,则该点位于多边形外。 否则,该点位于多边形内。
以下 XAML 示例创建并呈现复合形状,并将 FillRule
设置为 Nonzero:
<Path Stroke="Black"
Fill="#CCCCFF"
Aspect="Uniform"
HorizontalOptions="Start">
<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>
在此示例中,将显示由一系列同心环构成的复合形状:
在复合形状中,请注意所有圆环都已填充。 这是因为所有线段按同一方向运行,因此从任一点绘制的射线将与一个或多个线段相交,并且交点总数不会等于零。
在上图中,红色箭头表示线段的绘制方向,黑色箭头表示从最内环中的某一个点运行的任意一条射线。 从零值开始,对于该射线相交的每个段,会加值“1”,因为从左到右该段与该射线相交。
为更好地演示 Nonzero 填充规则的行为,需要以不同方向运行的包含多条线段的更复杂形状。 以下 XAML 示例创建与上一个示例类似的形状,只不过它使用 PathGeometry 而不是 EllipseGeometry 创建:
<Path Stroke="Black"
Fill="#CCCCFF">
<Path.Data>
<GeometryGroup FillRule="Nonzero">
<PathGeometry>
<PathGeometry.Figures>
<!-- Inner ring -->
<PathFigure StartPoint="120,120">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="50,50"
IsLargeArc="True"
SweepDirection="CounterClockwise"
Point="140,120" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Second ring -->
<PathFigure StartPoint="120,100">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="70,70"
IsLargeArc="True"
SweepDirection="CounterClockwise"
Point="140,100" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Third ring -->
<PathFigure StartPoint="120,70">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="100,100"
IsLargeArc="True"
SweepDirection="CounterClockwise"
Point="140,70" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Outer ring -->
<PathFigure StartPoint="120,300">
<PathFigure.Segments>
<ArcSegment Size="130,130"
IsLargeArc="True"
SweepDirection="Clockwise"
Point="140,300" />
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</GeometryGroup>
</Path.Data>
</Path>
在此示例中,绘制了一系列未闭合的弧线段:
在上图中,从中心开始的第三条弧线未填充。 这是因为与给定射线路径中的线段相交的该射线值的总和为零。
在上图中,红色圆表示一个点,黑色线条表示从非填充区域中的点射向外的任意射线,红色箭头表示线段绘制的方向。 可以看出,与线段相交的射线值的总和为零:
- 射向对角线的任意射线在右侧与两条以不同方向运行的线段相交。 因此,这些线段相互抵消,从而使得值为零。
- 射向对角线的任意射线在左侧与总共六条线段相交。 但是,交点相互抵消,因此最终总和为零。
总和为零导致圆环未填充。