矩形
矩形只是多段线的一种解,
正交矩形可以利用坐标直接相减(速度最快),见 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; // 射线穿过多边形边界的次数为奇数时点在多边形内
}
}
(完)