1

I have complex StreamGeometry and I want to clip it. Unfortunately, it looks like StreamGeometry doesn't supports combine.

Here is a test.

Xaml:

<Path x:Name="path" Stroke="Red"/>

Code:

var clip = new RectangleGeometry(new Rect(50, 50, 10, 10));
var rect = new RectangleGeometry(new Rect(0, 0, 100, 100));
var stream = new StreamGeometry();
using (var geometry = stream.Open())
{
    geometry.BeginFigure(new Point(0, 0), false, true);
    geometry.LineTo(new Point(0, 100), true, false);
    geometry.LineTo(new Point(100, 100), true, false);
    geometry.LineTo(new Point(100, 0), true, false);
}

//path.Data = rect;
//path.Data = stream;
//path.Data = clip;
//path.Data = Geometry.Combine(rect, clip, GeometryCombineMode.Intersect, null);
//path.Data = Geometry.Combine(stream, clip, GeometryCombineMode.Intersect, null);

Uncommenting first line (to show rect) or second line (to show stream) produces same visual result:

Uncommenting third line (to show clip) or fourth line (to show intersection of clip and rect) will produce:

While uncommenting last line (to show intersection of clip and geometry) produce blank screen.

My question is: how to combine (clip) StreamGeometry?

I know there is UIElement.Clip, but:

// blank screen
path.Data = stream;
path.Clip = clip;

// blank screen as well
path.Data = stream;
path.Clip = clip;
Sinatr
  • 20,892
  • 15
  • 90
  • 319

2 Answers2

3

You can definitely combine StreamGeometry objects. For this, you want to use a CombinedGeometry object. Here's a simple extension method for drawing polygons with holes:

public static void DrawPolygon(this DrawingContext context, Brush brush, Pen pen, IEnumerable<Point> exteriorRing, IEnumerable<IEnumerable<Point>> interiorRings = null)
{
    StreamGeometry geo = new StreamGeometry();
    Geometry finalGeometry = geo;

    using (StreamGeometryContext ctxExterior = geo.Open())
    {
        ctxExterior.BeginFigure(exteriorRing.First(), (brush != null), true);
        ctxExterior.PolyLineTo(exteriorRing.Skip(1).ToArray(), (pen != null), false);

        if (interiorRings != null)
        {
            foreach (var ring in interiorRings)
            {
                var interiorGeometry = new StreamGeometry();

                using (var ctxInterior = interiorGeometry.Open())
                {
                    ctxInterior.BeginFigure(ring.First(), true, true);
                    ctxInterior.PolyLineTo(ring.Skip(1).ToArray(), (pen != null), false);
                }

                finalGeometry = new CombinedGeometry(GeometryCombineMode.Exclude, finalGeometry, interiorGeometry);
            }
        }
    }

    context.DrawGeometry(brush, pen, finalGeometry);
}
seairth
  • 1,966
  • 15
  • 22
2

Solution is simple: do not use StreamGeometry.

To example, this will work (using PathGeometry instead):

var clip = new RectangleGeometry(new Rect(50, 50, 10, 10));
var geometry = new PathGeometry(new[] { new PathFigure(new Point(0, 0), new[] {
    new LineSegment(new Point(0, 100), true),
    new LineSegment(new Point(100, 100), true),
    new LineSegment(new Point(100, 0), true),
}, true) });
path.Data = Geometry.Combine(clip, geometry, GeometryCombineMode.Intersect, null);

Result:

Very important!

It looks like UIElement.Clip still render invisible parts (mayhap only with StreamGeometry) ! Never use it! Clip geometry before assigning it.

Sinatr
  • 20,892
  • 15
  • 90
  • 319