5

我有两个巨大的(> 100000 项)PathGeometry 集合,我需要使用 PathGeometry 进行比较。组合如下:

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()
    }
  }

令我惊讶的是,PathGeometry 是 GUI 的一部分,并且确实使用了调度程序,这有时会导致问题,因为我的计算确实在没有使用 Parallel.ForEach() 的 GUI 的情况下在某些后台线程中运行

因此,我正在寻找未连接到 GUI 的 PathGeometry 的替代方案。
我的人物非常复杂,在 PathGeometry.Figures 中添加了很多 PathFigures。

我自己从一些臃肿的政府 xml 文件中创建了那些 PathGeometries,所以创建其他东西是没有问题的。但是我需要一个函数来创建这两个几何图形的交集(而不是将它们相互添加)以获得两个几何图形覆盖的区域,例如该图中的红色区域:

在此处输入图像描述

4

3 回答 3

3

使用几何“迷你语言”而不是使用对象将您的字符串解析Path.Data成字符串怎么样?PathGeometry

例如,不要创建这样的东西:

<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>

你可以创建

<Path Stroke="Black" Data="M 10 100 L 100 100 L 100 50 Z" />

或者在您的情况下,XAML 可能看起来像这样:

<Path Stroke="Black" Data="{Binding MyPathProperty}" />

Path.Data是一个字符串,应该是特定格式的,是绘制路径几何图形的一种更短的方式。

以下是从上面的链接中获取的字母含义以及预期参数的列表:

  • F 值 - 设置 Geometry.FillRule 属性。对偶奇数使用 0,对非零使用 1。此命令必须出现在字符串的开头(如果您决定使用它)。

  • M x,y - 为几何体创建一个新的 PathFigure 并设置它的起点。此命令必须在除 F 之外的任何其他命令之前使用。但是,您也可以在绘图序列期间使用它来移动坐标系的原点。(M 代表移动)。

  • L x,y – 创建到指定点的 LineSegment。

  • H x – 使用指定的 X 值创建水平 LineSegment 并保持 Y 值不变。

  • V y - 使用指定的 Y 值创建垂直 LineSegment 并保持 X 值不变。

  • A radiusx, radiusY, degree isLargeArch, isClockwise x,y – 创建指向指定点的 ArcSegment。您可以指定描述圆弧的椭圆半径、圆弧旋转的度数以及设置 IsLargeArc 和 SweepDirection 属性的布尔标志。

  • C x1,y1 x2,y2 x,y - 使用 (x1, y1) 和 (x2, y2) 处的控制点创建指向指定点的 BezierSegment。

  • Q x1,y1 x,y - 创建指向指定点的 QuadraticBezierSegment,在 (x1, y1) 处有一个控制点。

  • S x2,y2 x,y - 通过使用前一个 BezierSegment 中的第二个控制点作为新 BezierSegment 中的第一个控制点来创建平滑的 BezierSegment。

  • Z - 结束当前 PathFigure 并将 IsClosed 设置为 true。如果您不想将 IsClosed 设置为 true,则不需要使用此命令——相反,如果您想开始一个新的 PathFigure 或结束字符串,只需使用 M。

例如,上面示例中使用的字符串 ( M 10 100 L 100 100 L 100 50 Z) 可以分解为:

  • M 10 100 - 在 10,100 处开始路径
  • L 100 100 - 画一条线到 100,100
  • L 100 50 - 画一条线到 100,50
  • Z - 结束字符串

在您从数据库中读取您的情况下Path.Data,您将首先创建一个字符串,M x y其中包含x y路径的 x,y 起始位置在哪里,然后一次读取一个段并使用上面的速记将它们附加到字符串,然后以Z

请注意,大写和小写字母具有不同的含义。大写字母表示您正在为路径段提供绝对值,而小写字母表示它应该相对于最后一段。

例如,如果您的段"L 10 10"显示 ,则表示在网格上的位置 10,10 处画一条线,而"l 10 10"表示从当前位置向上和 10 处画一条线。

在您从数据库中读取您的情况下PathGeometry,您需要将每个转换PathGeometry为一个字符串,然后组合这些字符串。

这是一些粗略的代码作为示例。我知道它不起作用,但希望它可以为您指明正确的方向

编辑

根据您编辑的问题,听起来您的数据项存储为 a PathGeometry,因此您可能需要将PathGeometry对象转换为字符串,然后将字符串组合起来

这是一个非常粗略的例子。我很确定我有一些语法错误,可能还有一些逻辑,因为我对这个PathGeometry对象不太熟悉,但希望它可以为你指明正确的方向

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();
}
于 2012-09-25T16:08:25.437 回答
3

System.Windows.Media还包含一个类StreamGeometry,它是 . 的“轻量级替代品” PathGeometry。它不支持数据绑定、动画或修改,但由于它派生自Geometry它应该有一个Combine方法。

请参阅StreamGeometry @ MSDN

于 2012-09-27T15:01:15.303 回答
0

我刚刚玩过它,但NetTopologySuite看起来它会做你想做的事。

下面的代码创建了两个多边形,检查它们是否相交,找到它们的面积并计算它们的相交。

var gf = new GeometryFactory();

var c1 = new[] { new Coordinate(0, 0), new Coordinate(2, 0),
                 new Coordinate(2, 2), new Coordinate(0, 2),
                 new Coordinate(0, 0) };
var lr1 = gf.CreateLinearRing(c1);
var p1 = gf.CreatePolygon(lr1, new ILinearRing[0]);

var c2 = c1.Select(c => new Coordinate(c.X + 1, c.Y + 1)).ToArray();
var lr2 = gf.CreateLinearRing(c2);
var p2 = gf.CreatePolygon(lr2, new ILinearRing[0]);

var intersects = p1.Intersects(p2);        // true
var intersection = p1.Intersection(p2);    // another polygon
var area = intersection.Area;              // 1.0
于 2012-09-27T12:51:48.123 回答