矩形
矩形只是多段線的一種解,
正交矩形可以利用坐標直接相減(速度最快),見 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 + "外外外外外外外外外外外");
}
});
}
}
子函數
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; // 射線穿過多邊形邊界的次數為奇數時點在多邊形內
}
}
(完)