如何通过相交线段拆分PathGeometry Polygon

时间:2010-07-08 02:04:41

标签: c# wpf geometry computational-geometry pathgeometry

我有一个PathGeometry,我是从一堆LineSegments构建的,我想把它分成两个PathGeometries,它们是一条在几何体中间相交的线。这是我在这张照片中的意思:

http://i30.tinypic.com/2noyvm.png

我可以浏览LineSegments并创建一个简单的线对象数组(简单对象带有Point1,Point2属性,因此它代表一行)。但我需要以某种方式弄清楚哪条线位于相交线的一端,哪条线位于相交线的另一端...

这有点像几何组合方法的反面,就像我想要组合的几何划分方法。

有什么想法吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

确定哪条线位于交叉线的哪一侧的方法是计算线端点相对于交叉线的行列式的符号。正面是一面,负面是另一面。

如果你想在一个线段内部有更复杂的交叉点,那么你需要建立一个双向边和顶点的图形,并计算相交线和每个多边形边的交点。然后,您可以在线与边相交的位置插入顶点并回溯图形,从而在从一个方向到另一个方向时从有向边构建多边形。

如果您正在寻找这方面的实现,请查看Net Topology Suite,虽然主要用于GIS,但对于像这样的一般计算几何问题也很有用。

答案 1 :(得分:0)

嗯,这很有趣,这就是我的所作所为(如果有更有效的方式,我真的不知道这是否是“正确的”方式)。

  1. 创建一个移动几何体的变换,使分割线位于Y轴上。
  2. 对于几何体中的每一条线 - 如果X <0它在左边,如果X> 0,它在右边,如果线穿过Y轴,则将它分成两行。
  3. 使用步骤1中的变换的逆转换两个行列表,并从中重建几何。
  4. 这是一个SplitGeometry方法,它接受由两个点定义的几何和一条线,并返回两个几何:

        private void SplitGeometry(Geometry geo, Point pt1, Point pt2, out PathGeometry leftGeo, out PathGeometry rightGeo)
        {
            double c = 360.0 + 90.0 - (180.0 / Math.PI * Math.Atan2(pt2.Y - pt1.Y, pt2.X - pt1.X));
            var t = new TransformGroup();
            t.Children.Add(new TranslateTransform(-pt1.X, -pt1.Y));
            t.Children.Add(new RotateTransform(c));
            var i = t.Inverse;
            leftGeo = new PathGeometry();
            rightGeo = new PathGeometry();
            foreach (var figure in geo.GetFlattenedPathGeometry().Figures)
            {
                var left = new List<Point>();
                var right = new List<Point>();
                var lastPt = t.Transform(figure.StartPoint);
                foreach (PolyLineSegment segment in figure.Segments)
                {
                    foreach (var currentPtOrig in segment.Points)
                    {
                        var currentPt = t.Transform(currentPtOrig);
                        ProcessLine(lastPt, currentPt, left, right);
                        lastPt = currentPt;
                    }
                }
                ProcessFigure(left, i, leftGeo);
                ProcessFigure(right, i, rightGeo);
            }
        }
    
        private void ProcessFigure(List<Point> points, GeneralTransform transform, PathGeometry geometry)
        {
            if (points.Count == 0) return;
            var result = new PolyLineSegment();
            var prev = points[0];
            for (int i = 1; i < points.Count; ++i)
            {
                var current = points[i];
                if (current == prev) continue;
                result.Points.Add(transform.Transform(current));
                prev = current;
            }
            if (result.Points.Count == 0) return;
            geometry.Figures.Add(new PathFigure(transform.Transform(points[0]), new PathSegment[] { result }, true));
        }
    
        private void ProcessLine(Point pt1, Point pt2, List<Point> left, List<Point> right)
        {
            if (pt1.X >= 0 && pt2.X >= 0)
            {
                right.Add(pt1);
                right.Add(pt2);
            }
            else if (pt1.X < 0 && pt2.X < 0)
            {
                left.Add(pt1);
                left.Add(pt2);
            }
            else if (pt1.X < 0)
            {
                double c = (Math.Abs(pt1.X) * Math.Abs(pt2.Y - pt1.Y)) / Math.Abs(pt2.X - pt1.X);
                double y = pt1.Y + c * Math.Sign(pt2.Y - pt1.Y);
                var p = new Point(0, y);
                left.Add(pt1);
                left.Add(p);
                right.Add(p);
                right.Add(pt2);
            }
            else
            {
                double c = (Math.Abs(pt1.X) * Math.Abs(pt2.Y - pt1.Y)) / Math.Abs(pt2.X - pt1.X);
                double y = pt1.Y + c * Math.Sign(pt2.Y - pt1.Y);
                var p = new Point(0, y);
                right.Add(pt1);
                right.Add(p);
                left.Add(p);
                left.Add(pt2);
            }
        }