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.