Holes

How do you make a hole in a visual? That is the question. It appears that there are four ways to do so, as follow:

• Use a CombinedGeometry
• Use a GeometryGroup
• Use a Clip

One hole, it seems, isn't too onerous. Two holes, on the other hand is today's headache.

 CombinedGeometry

A CombinedGeometry allows only two geometries to be combined. Further holes are created by nesting further

```    <Canvas Background="OldLace">
<Path Fill="LightSkyBlue" Stroke="Black">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>``` If you want to make a wheel with five holes in, you need 5 nested combined geometries.

The CombinedGeometry allows two geometries to be combined according to the usual combination suspects using the GeometryCombineMode property:

• Exclude - exlude the area of the second geometry when drawing the first
• Union -
• Complement
• Xor GeometryGroup

The GeometryGroup allows more than two geometries to be combined. There are only two CombineModes

• EvenOdd - switch the fill between the fillcolor and transparent every time en edge is reached.
• NonZero - fill any enclosed space with the fill colour ```<Path Stroke="Black" StrokeThickness="1" Fill="Orange">
<Path.Data>
<!-- Creates a composite shape from three geometries. -->
<GeometryGroup FillRule="EvenOdd">
<RectangleGeometry Rect="70,140 40 30" />
</GeometryGroup>
</Path.Data>
</Path>
<Path Stroke="Black" StrokeThickness="1" Fill="Orange">
<Path.Data>
<!-- Creates a composite shape from three geometries. -->
<GeometryGroup FillRule="Nonzero">
<RectangleGeometry Rect="210,140 40 30" />
</GeometryGroup>
</Path.Data>
</Path>```

The geometrygroup does not allow us to exclude, so does not prove as useful as the CombinedGeometry when animating the hole and shape seperately, as a hole moving beyond the edge of the shape will begin to be filled.

 Clip

A clip, in GDI+ was only semi useful as the clip was not anti-aliased. However there was an exclude mode, so the clip could draw either only inside, or only outside the clipping region.

The new clip only allows drawing within the clipping region, so is of little use making holes.

```        <Ellipse Width="100" Height="70" Canvas.Left="300" Canvas.Top="120" Fill="#FFFF0000" Stroke="#FF000000">
<Ellipse.Clip>
</Ellipse.Clip>
</Ellipse>``` I can't say I like how inordinately complex it is to use the OpacityMask. First, the dimensions of the shapes used in the mask are relative to the element, in units of 0 to 100%, so we cannot use this either to move holes beyond the edge of the shape as the coordinate systems are different. If a shape's height is changed, the hole's height will change too.

```        <Ellipse Width="100" Height="100" Canvas.Left="450" Canvas.Top="100" Fill="Blue">
<DrawingBrush>
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Brush>
<SolidColorBrush Color="Black"></SolidColorBrush>

</GeometryDrawing.Brush>
<GeometryDrawing.Geometry>
<GeometryGroup FillRule="EvenOdd" >
<RectangleGeometry Rect="0,0,100,100" />
</GeometryGroup>

</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="0" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>

</Ellipse>``` Update - the many hole solution

Well, after ruminating over other things for a while, I finally came up with the answer. My IQ of 4, ably supported by my doggedness quotient of 0.27, and further supported by a good night's sleep, provided the answer on awakening this morning. It's so obvious, I can't see how I didn't think of it before. The way to punch many holes in a single object is to use a combination of the combinations. In short, use a GeometryGroup as the second geometry in a CombinedGeometry, as follows:

```        <Path Fill="LightSkyBlue" Stroke="Black">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<GeometryGroup FillRule="NonZero" >
<RectangleGeometry Rect="0,0,100,100" /> 