Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

non-GUI alternative to PathGeometry?

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:

enter image description here

like image 290
Sam Avatar asked Sep 25 '12 08:09

Sam


2 Answers

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

like image 119
Lars Avatar answered Sep 30 '22 02:09

Lars


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:

  • M 10 100 - Start Path at 10,100
  • L 100 100 - Draw a line to 100,100
  • L 100 50 - Draw a line to 100,50
  • Z - End String

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();
}
like image 33
Rachel Avatar answered Sep 30 '22 02:09

Rachel