C# 实现 任意多边形切割折线算法

1.    内容简介

本文旨在解决任意多边形切割折线,获取切割之后的折线集合。

本文实现的算法内容包括:判断两条线段是否相交,如若相交,获取交点集合、对线上的点集,按斜率方向排序、判断点是否在多边形内、获取线段和任意多边形的交点集合、中点算法、获取任意多边形裁剪折线的线段集合。

2.    效果实现

3.    算法实现

a)    本文使用到的命名空间

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Shapes;
命名空间

b)    源代码实现

 public partial class Help
    {

        #region 线段与多边形的交点集合,按从p1到p2的方向进行排序
        /// <summary>
        /// 对一线段与多边形的交点集合,按从p1到p2的方向进行排序
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <param name="interPoints"></param>
        /// <returns></returns>
        public List<Point> SortPointsBySlopeOfLine(Point p1, Point p2, List<Point> interPoints)
        {
            List<Point> points = new List<Point>();
            List<Point> newInterPoints = new List<Point>();
            points.Add(p1);
            if (Equals(p1.X, p2.X))//垂直线段
            {
                if (p1.Y > p2.Y)
                {
                    newInterPoints = interPoints.OrderByDescending(t => t.Y).ToList();
                }
                else
                {
                    newInterPoints = interPoints.OrderBy(t => t.Y).ToList();
                }
            }
            else
            {
                if (Equals(p1.Y, p2.Y))//水平线段
                {
                    if (p1.X > p2.X)
                    {
                        newInterPoints = interPoints.OrderByDescending(t => t.X).ToList();
                    }
                    else
                    {
                        newInterPoints = interPoints.OrderBy(t => t.X).ToList();
                    }

                }
                else//普通斜率线段,按x或y都行
                {
                    if (p1.X > p2.X)
                    {
                        newInterPoints = interPoints.OrderByDescending(t => t.X).ToList();
                    }
                    else
                    {
                        newInterPoints = interPoints.OrderBy(t => t.X).ToList();
                    }

                }

            }

            foreach (Point interPoint in newInterPoints)
            {
                points.Add(interPoint);
            }
            points.Add(p2);
            return points;
        }
        #endregion


        #region 获取线段和线段的交点

        private double eps = 1e-8;
        /// <summary>
        /// 判断一个数值是否在误差范围内
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private bool zero(double x)
        {

            return (((x) > 0 ? (x) : -(x)) < eps);
        }


        /// <summary>
        /// 计算交叉乘积(P1-P0)x(P2-P0)
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <param name="p0"></param>
        /// <returns></returns>
        private double xmult(Point p1, Point p2, Point p0)
        {
            return (p1.X - p0.X) * (p2.Y - p0.Y) - (p2.X - p0.X) * (p1.Y - p0.Y);
        }

        /// <summary>
        /// 判点是否在线段上,包括端点
        /// </summary>
        /// <param name="p"></param>
        /// <param name="l1"></param>
        /// <param name="l2"></param>
        /// <returns></returns>
        private bool dot_online_in(Point p, Point l1, Point l2)
        {
            return zero(xmult(p, l1, l2)) && (l1.X - p.X) * (l2.X - p.X) < eps && (l1.Y - p.Y) * (l2.Y - p.Y) < eps;
        }

        /// <summary>
        /// 判两点在线段同侧,点在线段上返回0
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <param name="l1"></param>
        /// <param name="l2"></param>
        /// <returns></returns>
        private bool same_side(Point p1, Point p2, Point l1, Point l2)
        {
            return xmult(l1, p1, l2) * xmult(l1, p2, l2) > eps;
        }

        /// <summary>
        /// 判断两直线平行
        /// </summary>
        /// <param name="u1"></param>
        /// <param name="u2"></param>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        private bool parallel(Point u1, Point u2, Point v1, Point v2)
        {
            return zero((u1.X - u2.X) * (v1.Y - v2.Y) - (v1.X - v2.X) * (u1.Y - u2.Y));
        }

        /// <summary>
        /// 判三点共线
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <param name="p3"></param>
        /// <returns></returns>
        private bool dots_inline(Point p1, Point p2, Point p3)
        {
            return zero(xmult(p1, p2, p3));
        }

        /// <summary>
        /// 判两线段相交,包括端点和部分重合
        /// </summary>
        /// <param name="u1"></param>
        /// <param name="u2"></param>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        private bool intersect_in(Point u1, Point u2, Point v1, Point v2)
        {
            if (!dots_inline(u1, u2, v1) || !dots_inline(u1, u2, v2))
                return !same_side(u1, u2, v1, v2) && !same_side(v1, v2, u1, u2);
            return dot_online_in(u1, v1, v2) || dot_online_in(u2, v1, v2) || dot_online_in(v1, u1, u2) || dot_online_in(v2, u1, u2);
        }

        /// <summary>
        /// 计算两线段交点,请判线段是否相交(同时还是要判断是否平行!)
        /// </summary>
        /// <param name="u1"></param>
        /// <param name="u2"></param>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <param name="ret"></param>
        /// <returns></returns>
        private int GetIntersectionPoint(Point u1, Point u2, Point v1, Point v2, out Point ret)
        {
            ret = u1;
            if (parallel(u1, u2, v1, v2) || !intersect_in(u1, u2, v1, v2))
            {
                return 0;
            }
            double t = ((u1.X - v1.X) * (v1.Y - v2.Y) - (u1.Y - v1.Y) * (v1.X - v2.X))
              / ((u1.X - u2.X) * (v1.Y - v2.Y) - (u1.Y - u2.Y) * (v1.X - v2.X));
            ret.X += (u2.X - u1.X) * t;
            ret.Y += (u2.Y - u1.Y) * t;
            return 1;
        }
        #endregion


        #region 多边形包含点
        /// <summary>
        /// 判断多边形是否包含某个点
        /// </summary>
        /// <param name="poly">多边形边框上每个角的点坐标数组</param>
        /// <param name="p">要进行判断的点</param>
        /// <returns>true:包含; false:不包含</returns>
        public bool InPoly(Polygon polygon, Point p)
        {

            Point[] poly = polygon.Points.ToArray();
            int i = 0, f = 0;
            double xi = 0, a = 0, b = 0, c = 0;
            Point ps, pe;
            ///遍历每一个点
            /// Microsoft.VisualBasic.Information.UBound 获取最大下标,等同于 poly.Count-2
            for (i = 0; i <= Microsoft.VisualBasic.Information.UBound(poly, 1); i++)
            {
                ps = poly[i];

                if (i < Microsoft.VisualBasic.Information.UBound(poly, 1))
                {
                    pe = poly[i + 1];
                }
                else
                {
                    pe = poly[0];
                }
                GetStdLine(ps, pe, ref a, ref b, ref c);
                if (a != 0)
                {
                    xi = 0 - ((b * p.Y + c) / a);
                    if (xi == p.X)
                    {
                        return true;
                    }
                    else if (xi < p.X)
                    {
                        f = f + Sgn(pe.Y - p.Y) - Sgn(ps.Y - p.Y);
                    }
                }
            }
            return f != 0;
        }

        /// <summary>
        /// 根据两个点的坐标求经过两点的直线的标准方程参数A、B、C
        /// </summary>
        /// <param name="ps"></param>
        /// <param name="pe"></param>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="c"></param>
        private void GetStdLine(Point ps, Point pe, ref double a, ref double b, ref double c)
        {
            double xs, ys, xe, ye;
            double p1, p2;
            xs = ps.X;
            ys = ps.Y;
            xe = pe.X;
            ye = pe.Y;
            p1 = (xs * ye);
            p2 = (xe * ys);
            if (p1 == p2)
            {
                if (xs == 0)
                {
                    if (xe == 0)
                    {
                        a = 1;
                        b = 0;
                        c = 0;
                    }
                    else if (ys == 0)
                    {
                        a = ye;
                        b = 0 - xe;
                        c = 0;
                    }
                }
                else if (ye == 0)
                {
                    if (ys == 0)
                    {
                        a = 0;
                        b = 1;
                        c = 0;
                    }
                    else if (xe == 0)
                    {
                        a = 0 - ys;
                        b = xs;
                        c = 0;
                    }
                }
            }
            else
            {
                a = (ys - ye) / (p1 - p2);
                c = 1;
                if (ys == 0)
                {
                    if (ye == 0)
                    {
                        b = 1;
                        c = 0;
                    }
                    else
                    {
                        b = 0 - ((a * xe + 1) / ye);
                    }
                }
                else
                {
                    b = 0 - ((a * xs + 1) / ys);
                }
            }
        }
        private int Sgn(double a)
        {
            if (a == 0)
            {
                return 0;
            }
            else if (a < 0)
            {
                return -1;
            }
            else
            {
                return 1;
            }
        }

        #endregion

        /// <summary>
        /// 求出线段和多边形的交点,不包括p1p2
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <param name="polygon"></param>
        /// <returns></returns>
        public List<Point> GetInterPoints(Point p1, Point p2, Polygon polygon)
        {
            List<Point> interPoints = new List<Point>();
            List<Point> polygonPoints = polygon.Points.ToList();
            for (int i = 0; i < polygonPoints.Count; i++)
            {
                Point polygon1 = polygonPoints[i];
                Point polygon2 = new Point();
                if (i == polygonPoints.Count - 1)
                {
                    polygon2 = polygonPoints[0];
                }
                else
                {
                    polygon2 = polygonPoints[i + 1];

                }
                Point inter = new Point();
                int interType = GetIntersectionPoint(p1, p2, polygon1, polygon2, out inter);
                switch (interType)
                {

                    case 1:

                        if (!Equals(inter, p1) && !Equals(inter, p2))
                        {
                            interPoints.Add(inter);
                        }
                        break;
                    case 0:
                    default:
                        break;
                }
            }
            return interPoints;
        }

        /// <summary>
        /// 取两个点的中点
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        public Point GetCenter(Point p1, Point p2)
        {
            return new Point((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2);
        }

        /// <summary>
        /// 获取多边形裁剪折线形成的线段集合
        /// </summary>
        /// <param name="polyline"></param>
        /// <param name="polygon"></param>
        /// <returns></returns>
        public List<Polyline> GetInterPolylines(Polyline polyline, Polygon polygon)
        {
            List<Polyline> list = new List<Polyline>();
            //TODO: 1.遍历折线上的每相邻的两个个点,组成线段,与多边形的每一条边计算,求出此线段与多边形的边的交点
            //TODO:  2.对此线段的上的交点进行排序,组成连续点的折线,判断这些线段在多边形内部的部分,加入集合

            List<Point> polinePoints = polyline.Points.ToList();
            List<Point> polygonPoints = polygon.Points.ToList();

            for (int i = 0; i < polinePoints.Count - 1; i++)
            {
                Point one = polinePoints[i];
                Point two = new Point();
                if (i == polinePoints.Count - 1)
                {

                }
                else
                {
                    two = polinePoints[i + 1];
                }
                List<Point> inters = GetInterPoints(one, two, polygon); 
                List<Point> sortInters = SortPointsBySlopeOfLine(one, two, inters);

                for (int j = 0; j < sortInters.Count; j++)
                {
                    if (j < sortInters.Count - 1)
                    {
                        if (InPoly(polygon, GetCenter(sortInters[j], sortInters[j + 1])))
                        {
                            Polyline interPolyline = new Polyline();
                            interPolyline.Points.Add(sortInters[j]);
                            interPolyline.Points.Add(sortInters[j + 1]);
                            list.Add(interPolyline);
                        }
                    }

                }
            }
            return list;
        }
    }
SourceCode
原文地址:https://www.cnblogs.com/shanranlei/p/5082928.html