数学篇 cad.net 判断点在多段线内-射线法


矩形

矩形只是多段线的一种解,

正交矩形可以利用坐标直接相减(速度最快),见 using System.Windows.Rect; WPF的类.

非正交矩形可以利用叉乘求解.
举个例子,不完全的代码: R1,R2,R3,R4是矩形的角点

/// <summary>
/// 点在矩形内为<see langword="true"/>
/// </summary>
/// <returns></returns>
public bool Contains(PointV p)
{
    return R1.Cross(R2, p) * R3.Cross(R4, p) >= 0 &&
           R2.Cross(R3, p) * R4.Cross(R1, p) >= 0;
}

射线法

但是非矩形的情况下,就需要射线法.

测试命令

public partial class CmdTest
{
    [CommandMethod("CmdTest_InBoundary")]
    public void CmdTest_InBoundary()
    {
        var dm = Acap.DocumentManager;
        var doc = dm.MdiActiveDocument;
        var db = doc.Database;
        var ed = doc.Editor;
        ed.WriteMessage(Environment.NewLine + "射线法测试:");

        var ppo = new PromptPointOptions(Environment.NewLine + "测试点:")
        {
            AllowArbitraryInput = true,//任意输入
            AllowNone           = true //允许回车
        };
        var ppr = ed.GetPoint(ppo);//用户点选
        if (ppr.Status != PromptStatus.OK)
            return;

        db.Action(tr => {
            var peo = new PromptEntityOptions(Environment.NewLine + "点选多段线")
            {
                AllowObjectOnLockedLayer = false,
                AllowNone                = false
            };

            var ent2 = ed.WhileEntsel(tr, peo, null, new EntityType[] { EntityType.Polyline });
            if (ent2 is ObjectId id && id.IsOk())
            {
                var ent = id.ToEntity(tr);
                if (ent is Polyline pl && ppr.Value.InBoundary(pl.GetEntityPoint3ds().ToArray()))
                    ed.WriteMessage(Environment.NewLine + "内内内内内内内内");
                else
                    ed.WriteMessage(Environment.NewLine + "外外外外外外外外外外外");
            }
        });
    }
}

子函数

GetEntityPoint3ds

public static partial class MathHelper
{ 
    /// <summary>
    /// 点在闭合多段线内,水平射线法
    /// </summary>
    /// <param name="p">判断的点</param>
    /// <param name="ptsArr">边界点集</param>
    /// <param name="onBoundary">在边界上算不算</param>
    /// <param name="tolerance">容差</param>
    /// <returns></returns>
    public bool InBoundary(this Point3d p, Point3d[] ptsArr, bool onBoundary = true, double tolerance = 1e-6)
    {
        // 首尾相连
        static void End2End<T>(List<T> lst)
        {
            if (!lst[0].Equals(lst[lst.Count - 1]))
                lst.Add(lst[0]);
        }
        var pts = ptsArr.ToList();
        End2End(pts);
        return InBoundary(p, pts, onBoundary, tolerance);
    }

    /// <summary>
    /// 点在闭合多段线内,水平射线法
    /// </summary>
    /// <param name="p">判断的点</param>    
    /// <param name="pts">边界点集</param>
    /// <param name="onBoundary">在边界上算不算</param>
    /// <param name="tolerance">容差</param>
    /// <returns></returns>
    public static bool InBoundary(this Point3d p, List<Point3d> pts, bool onBoundary = true, double tolerance = 1e-6)
    { 
        bool Eq(this double a, double b)
        {
            return Abs(a - b) <= tolerance;
        }
 
        var flag = false;
        var x = p.X;
        var y = p.Y;

        for (var i = 0; i < pts.Length - 1; i++)
        {
            var x1 = pts[i].X;//头
            var y1 = pts[i].Y;
            var x2 = pts[i + 1].X;//尾
            var y2 = pts[i + 1].Y;

            // 点与多边形顶点重合
            if ((Eq(x1, x) && Eq(y1, y)) || Eq(x2, x) && Eq(y2, y))
            {
                flag = true;
                break;
            }
            // 子段端点是否在水平射线两侧(都在下面 || 都在上面)
            if ((y1 < y && y2 >= y) || (y1 >= y && y2 < y))
            {
                //射线穿过子段时,得出交点为(ox,y)
                var derivative = (x2 - x1) / (y2 - y1);  //导数.斜率
                var high = y - y1;                       //高
                var ox = x1 + high * derivative;

                // 点在多边形的边上
                if (Eq(ox, x) && onBoundary)
                {
                    flag = true;
                    break;
                }
                // 射线穿过多边形的边界
                if (ox > x)
                    flag = !flag;
            }
        }
        return flag; // 射线穿过多边形边界的次数为奇数时点在多边形内
    }
}

(完)


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM