I've got two huge (>100000 items) collections of PathGeometry I need to compare using PathGeometry.Combine something like this:
List<PathGeometry> firstList;
List<PathGeometry> secondList;
[...]
foreach (PathGeometry pg1 in firstList)
foreach (PathGeometry pg2 in secondList)
{
PathGeometry intergeo = PathGeometry.Combine(pg1, pg2, GeometryCombineMode.Intersect, null);
if (intergeo.GetArea() > 0)
{
// do whatever with intergeo.GetArea()
}
}
To my surprise PathGeometry is part of the GUI and does use the dispatcher, which sometimes causes problems since my calculations do run in some background thread without GUI using Parallel.ForEach()
So I'm looking for an alternative to PathGeometry which is not connected to the GUI.
My figures are quite complex and add a lot of PathFigures to the PathGeometry.Figures.
I'm creating those PathGeometries myself from some bloated government xml files, so it would be no problem to create something else instead. But I need a function to create an intersection of two of these geometries (not adding them to each other) to get the area which both geometries cover, such as the red area in this diagram:
System.Windows.Media
also contains a class StreamGeometry
, which is a "light-weight alternative" to PathGeometry
. It does not support data binding, animation, or modification but since it derives from Geometry
it should have a Combine
method.
See StreamGeometry @ MSDN
What about parsing your Path.Data
into a string using the geometry "mini-langauge" instead of using the PathGeometry
object?
For example, instead of creating something like this:
<Path Stroke="Black">
<Path.Data>
<PathGeometry>
<PathFigure IsClosed="true" StartPoint="10,100">
<LineSegment Point="100,100" />
<LineSegment Point="100,50" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Data>
You could create
<Path Stroke="Black" Data="M 10 100 L 100 100 L 100 50 Z" />
Or in your case, the XAML would probably look something like this:
<Path Stroke="Black" Data="{Binding MyPathProperty}" />
The Path.Data is a string that is expected to be in a specific format, and is a shorter way of drawing your path's geometry.
Here's a list of what letters mean, and what parameters are expected, taken from the link above:
F value - Sets the Geometry.FillRule property. Use 0 for EvenOdd, or 1 for NonZero. This command must appear at the beginning of the string (if you decide to use it).
M x,y - Creates a new PathFigure for the geometry and sets its start point. This command must be used before any other commands except F. However, you can also use it during your drawing sequence to move the origin of your coordinate system. (The M stands for move).
L x,y – Creates a LineSegment to the specified point.
H x – Creates a horizontal LineSegment using the specified X value and keeping the Y value constant.
V y - Creates a vertical LineSegment using the specified Y value and keeping the X value constant.
A radiusx, radiusY, degrees isLargeArch, isClockwise x,y – Creates an ArcSegment to the indicated point. You specify the radii of the ellipse that describes the arc, the number of degrees the arc is rotated, and Boolean flags that set the IsLargeArc and SweepDirection properties.
C x1,y1 x2,y2 x,y - Creates a BezierSegment to the indicated point, using control points at (x1, y1) and (x2, y2).
Q x1,y1 x,y - Creates a QuadraticBezierSegment to the indicated point, with one control point at (x1, y1).
S x2,y2 x,y - Creates a smooth BezierSegment by using the second control point from the previous BezierSegment as the first control point in the new BezierSegment.
Z - Ends the current PathFigure and sets IsClosed to true. You don’t need to use this command if you don’t want to set IsClosed to true – instead, simply use M if you want to start a new PathFigure or end the string.
For example, the string used in the sample above (M 10 100 L 100 100 L 100 50 Z
) can be broken down into:
In your case where your Path.Data
is read from the database, you would start by creating a string containing M x y
where x y
is the x,y starting position for the path, and then read your segments one at a time and append them to the string using the shorthand above, then end your string with Z
Note that upper and lower case letters have different meanings. An uppercase letter means you are providing an absolute value for your path segment, while a lowercase letter means it should be relative to the last segment.
For example, if your segment says "L 10 10"
, it means draw a line to position 10,10 on the grid while "l 10 10"
means draw a line 10 up and 10 right from the current position.
In your case where your PathGeometry
is read from the database, you would need to convert each PathGeometry
into a string, then combine the strings.
Here's some rough code as an example. I know it doesn't work, but hopefully it can point you in the right direction
Edit
Based on your edited question, it sounds like your data item is stored as a PathGeometry
, so you may need to convert your PathGeometry
objects to a string, then just combine the strings
Here's a very rough example. I'm fairly sure I have some syntax wrong and possibly a bit of the logic as well since I'm not that familiar with the PathGeometry
object, but hopefully it can point you in the right direction
foreach (PathGeometry pg1 in firstList)
foreach (PathGeometry pg2 in secondList)
{
var s1 = ConvertGeometryToString(pg);
var s2 = ConvertGeometryToString(pg2);
// Ideally you probably wouldn't want this until you have your
// full PathGeometry string built, but I'm not sure what you're doing
// with the object so left it in anyways
PathGeometry intergeo = Geometry.Parse(s1 + s2);
}
}
string ConvertGeometryToString(PathGeometry pg)
{
StringBuilder sb = new StringBuilder();
foreach(var figure in pg.PathFigures)
{
sb.Append("M " + figure.StartPoint);
foreach(var seg in figure.Segments)
{
if (seg is LineSegment)
sb.Append(" L " + ((LineSegment)seg).Point);
else if (seg is ArcSegment)
... etc
}
if (figure.IsClosed)
sb.Append(" Z");
}
return sb.ToString();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With