2

I have a single System.Windows.Media.PathGeometry like that: 1 And I would like to split the geometry, so that each gray shape is in a new Geometry object. I have tried to iterate over the FigureColletion of the PathGeometry and put each figure in a new Geometry but the result is not as I expected it to be, because one figure only describes one edge of a shape and not the shape itself. This means, some figures are applied additive and some subtractive: 2 To split the geometries correctly i have to figure out which PathFigures are applied additive and which subtractive. There is no Property on the PathFigure type which gives me information about how its applied. Does anybody have an idea how to solve this problem?

Thanks in advance.

Timo
  • 9,269
  • 2
  • 28
  • 58
  • are the PathFigures necessarily non-intersecting? cos if they do intersect then things get more complicated –  Jun 07 '16 at 18:41
  • No they will never intersect. I'm analyzing all geometries before I apply this operation. If there are intersections, the intersecting geometries will be merged (via Geometry.Combine(...) and afterwards PathGeometry.GetOutlinedPath(... )). The GetOutlinedPath function should prevent intersecting figures. – Timo Jun 08 '16 at 05:52

2 Answers2

3

I'm currently trying to do the same thing, so here's my plan, along with some of the thought process.

First, convert everything to PathGeometries. The FillContains(Geometry geometry) method is what I want to use to see what's inside of what. The issue with .Bounds.Contains is that, if you have a C shape with a dot in the middle, the dot will be contained in the bounding rectangle but not the shape.

Next, create a tree data structure. If PathGeometry A contains PathGeometry B, A will be an ancestor of B in the tree. The other answer here suggests using a list, but that won't work as well. The rest of this paragraph exaplains why. Suppose there are two PathFigures and neither is inside the other: after sorting the list, we would assume that one is inside the other. We can account for that without too much extra work, but now suppose there are two PathFigures surrounded by a third PathFigure (like the number 8): after sorting, we only get one hole to be part of the 8. We can maybe account for that as well. Last issue: suppose A contains B and C contains D, but they're put into the list in the order {A,C,B,D}: some sorting algorithms (like BubbleSort) will leave them in this order, because no shape contains its neighbor. Lists are just too confusing here.

So for our tree, what's the root node? The root node will be something that contains everything. If you want to create such a thing, you can take the Union of all your PathGeometries and use .Bounds. There might be weird cases where that doesn't work, but it's not important.

What will our tree look like? I'll use the figure numbers from your example. Click here to see the tree.

How do we make the tree? I think this pseudocode is easier to understand than me trying to describe it:

TreeNode.AddNode(PathGeometry geomToAdd)
{
    bool containedByChild = false
    foreach (TreeNode current in this.Children)
    {
        if (current.FillContains(geomToAdd)
        {
            containedByChild = true
            current.AddNode(geomToAdd)
        }
    }
    if (!containedByChild)
        this.Children.Add(geomToAdd)
}

Unlike a binary tree, we have a list of children instead of a fixed number of children. A leaf in the tree is any node with an empty list of children. Because the root node is supposed to contain everything, you can just call this method on the root without ever needing to define the PathGeometry for root.

How do we turn the tree into our PathGeometries? Start with the children of root. These are additive PathFigures, and their children are subtractive PathFigures. Using .Combine() with GeometryCombineMode.Exclude, you can subtract the grandchildren of root from the children of root. Then, remove all children from root, and turn root's great-grandchildren into its new list of children, and repeat.

Hopefully, this is clear. If it's not, please let me know how to improve the answer.

Pi Fisher
  • 260
  • 1
  • 11
0

If, as you mentioned, the PathFigure's don't intersect, i.e. containment is complete, then the corollary is that if PathFigure A contains B, then the AABB bounds of A also contains that of B.

Unfortunately I don't think PathFigure has such a property, so one way to overcome this would probably be to create individual new PathGeometry objects from each PathFigure inside the .Figures property, and store these in an array. Then sort this array using the .Bounds.Contains property, i.e. this: https://msdn.microsoft.com/en-us/library/ms557978(v=vs.110).aspx, in place of the comparison operation in a conventional sorting routine.

Now you have a "Matryoshka doll" list of PathGeometry's, starting from the outermost one, select every successive pair and take their union (.Combine as you said). If the array has an odd number of elements, i.e. one left at the end, then that must be the case in Figure 4.