數學篇 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