Xamarin.Forms 形状:填充规则
多个 Xamarin.Forms Shapes 类具有 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>
在此示例中,将绘制一系列不闭合的圆弧线段:
在上图中,从中心算起的第三个圆弧未填充。 这是因为与给定射线路径中的线段相交的该射线值的总和为零。
在上图中,红色圆表示一个点,黑色线条表示从非填充区域中的点移出的任意射线,红色箭头表示绘制线段的方向。 可以看出,与线段相交的射线值的总和为零:
- 沿对角线向右射出的任意射线穿过两个沿不同方向行进的线段。 因此,这些线段相互抵消,给出零值。
- 沿对角线向左行进的任意射线总共穿过六个线段。 然而,交叉相互抵消,因此零是最终的和。
如果和为零,则环不会被填充。