使用LINQ指向多边形上的OR点

时间:2010-05-06 19:32:03

标签: c# .net linq algorithm geometry

2 个答案:

答案 0 :(得分:2)

我不太了解算法本身,但我想我可以提供帮助:
假设我理解了这一点,你基本上需要的是检查任何元组是否适合某个谓词。然后你可以将每对Positions放在一个元组中,并将它作为IEnumerable<Tuple<Position, Position>>传递给你。你可以做任何事情(谓词在这里),看看它们是否符合描述(或者,如果你需要的话)谓词为true的实际值,使用.FirstOrDefault()并检查null)
所以你的代码看起来像这样:

   public static PointInPolygonLocation PointInPolygon(IEnumerable<Position> pts, Position pt)
    {
        bool isIn = pts.Pairwise((p1, p2) => Tuple.Create(p1, p2)).Any(tuple => tuple.Item1.Y != tuple.Item2.Y &&
        ((tuple.Item1.Y >= pt.Y && tuple.Item2.Y < pt.Y) || (tuple.Item1.Y < pt.Y && tuple.Item2.Y >= pt.Y))
        && ((tuple.Item1.X < tuple.Item1.X && tuple.Item2.X < pt.X) || ((pt.Y - tuple.Item1.Y) * ((tuple.Item1.X - tuple.Item2.X) / (tuple.Item1.Y - tuple.Item2.Y)) * tuple.Item1.X) < pt.X));
        return isIn ? PointInPolygonLocation.Inside : PointInPolygonLocation.Outside;
    }

我会认真考虑为那个怪异的谓词添加一些评论。我有点拉链了。嵌套的ifs变成&amp;&amp; s等等。你应该检查它,当然,但我90%肯定它和你做的一样。引用knuth:Beware of bugs in the above code; I have only proved it correct, not tried it.

编辑:现在更具可读性,你不是吗?

   public static PointInPolygonLocation PointInPolygon(IEnumerable<Position> pts, Position pt)
    {
        bool isIn = pts.Pairwise((p1, p2) => Tuple.Create(p1, p2)).Any(tuple => ReadablePredicate(tuple.Item1, tuple.Item2, pt));
        return isIn ? PointInPolygonLocation.Inside : PointInPolygonLocation.Outside;
    }
    public static bool ReadablePredicate(Position p1, Position p2, Position pt)
    {
        if (p1.Y == p2.Y)
            return false;
        if (!((p1.Y >= pt.Y && p2.Y < pt.Y) || (p1.Y < pt.Y && p2.Y >= pt.Y)))
            return false;
        if (p1.X < pt.X && p2.X < pt.X) // Originally was (p1.X < p1.X && ...). that makes no sense, so I assumed you meant p1.X < pt.X
            return true;
        if ((p1.X < pt.X || p2.X < pt.X) && ((pt.Y - p1.Y) * ((p1.X - p2.X) / (p1.Y - p2.Y)) * p1.X) < pt.X)
            return true;
        return false;
    }

答案 1 :(得分:0)

我已经经历了几次这个问题的迭代(哈哈)。最终有两个关键:

  1. 我的结果选择器函数计算多边形的每个段的两个值。从该点向左延伸的光线是否与该段相交? (1或0以便于稍后通过Aggregate求和)。这一点是否在该细分市场上? (1或0以便于稍后通过Aggregate求和)。如果段值上的点为1(点在段上),则迭代应该停止。

  2. 由于我想根据需要迭代尽可能少的项目,我需要限制点在一个段上的顺序。如果该点不在任何段上,我别无选择,只能迭代所有段。如果该点在一个段上,我想退出,但我还需要聚合那个最后一个值(导致“迭代直到该点在一个段上”的条件失败)。我虽然TakeWhile会工作,因为我会采用{intersect,on}对直到 == 1(即直到那个点在一个段上)。不幸的是,TakeWhile会检测到这种情况并停止迭代,但我的输出(提供给Aggregate)不包含“失败”条件,因此Aggregate的结果并不反映该点在某个段上。

  3. 我认为,如果我通过LINQ完成此操作,我将不得不迭代整个序列,然后检查结果(Aggregate)以确定该点是否已落在我刚才的任何段上迭代。使用从上面链接的帖子启发/复制的TakeWhileInclusive扩展方法以及代码中注释中提到的非SO网站,我能够获得我需要的TakeWhile + 1行为。

    也许有更好的方法可以做到这一点?我不知道。我真的刚刚开始。成对模式适用于涉及点序列的许多几何计算。给定一系列点和以点对(p1-> p2,p2-> p3,p3-> p4等)表示的算法,它可以“简单地”表达算法的问题在一个lambda(简单的像Distance)或一个独立的函数(如本文中的那个,Centroid等)。

    我将暂时离开,以防其他人想要评论或提出建议或提出替代答案。最终,我可能会接受这个答案(如原始问题中的附加代码和评论所表达的那样。