当前位置: 首页 > backend >正文

Cad c# 射线法判断点在多边形内外

1、向量叉乘法

2、射线法原理

 

射线法是判断点与多边形位置关系的经典算法,核心思想是:

从目标点发出一条水平向右的射线(数学上可视为 y = p_y, x \geq p_x 的射线),统计该射线与多边形边的交点数量:

 

- 偶数次(含0次)相交:点在多边形外部

 

- 奇数次相交:点在多边形内部

 

- 点在边上:直接判定为内部或边界(根据需求处理)

 

关键边界处理

 

1. 边为水平线段( y_1 = y_2 = p_y ):

射线与边重合,若点在边上则直接判定为边界,否则忽略(不计数)。

 

2. 点正好是多边形顶点:

若顶点的两个相邻边分别在射线上下两侧,则计为1次交点;否则不计(避免重复计数)。

 

3. 射线经过边的顶点(非目标点):

仅当该顶点是边的“下端点”(即另一顶点 y > p_y )时,计为1次交点(采用“左闭右开”规则避免重复)。

 

CAD环境下的实现(基于AutoCAD API)

 

假设使用 Autodesk.AutoCAD.DatabaseServices 中的  Polyline  和  Point3d  类型,且所有点的 Z=0 (XY平面)。

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Geometry;

using System;

 

public static class PointInPolylineChecker

{

    /// <summary>

    /// 射线法判断点是否在闭合Polyline内部(处理直线段,忽略凸度/圆弧,如需处理圆弧需额外交点计算)

    /// </summary>

    /// <param name="point">目标点(Z=0)</param>

    /// <param name="polyline">闭合Polyline(顶点按顺序排列,需闭合,最后一点可与第一点重复)</param>

    /// <param name="includeBoundary">是否包含边界(点在边上时返回true)</param>

    /// <returns>true=内部或边界,false=外部</returns>

    public static bool IsPointInside(Point3d point, Polyline polyline, bool includeBoundary = true)

    {

        double px = point.X;

        double py = point.Y;

        int vertexCount = polyline.NumberOfVertices;

        int intersectionCount = 0;

 

        for (int i = 0; i < vertexCount; i++)

        {

            // 获取当前边的两个端点(闭合,最后一个顶点连接到第一个顶点)

            Point3d p1 = polyline.GetPoint3dAt(i);

            Point3d p2 = polyline.GetPoint3dAt((i + 1) % vertexCount);

 

            // 处理点在边上的情况(优先判断边界)

            if (includeBoundary && IsPointOnSegment(point, p1, p2))

                return true;

 

            double y1 = p1.Y;

            double y2 = p2.Y;

 

            // 边为水平线段(与射线共线),跳过(不计入交点)

            if (Math.Abs(y1 - py) < 1e-8 && Math.Abs(y2 - py) < 1e-8)

                continue;

 

            // 边的两个端点都在射线下方或上方,不相交

            if ((y1 < py - 1e-8 && y2 < py - 1e-8) || (y1 > py + 1e-8 && y2 > py + 1e-8))

                continue;

 

            // 计算交点的x坐标(射线为y=py,x≥px)

            double xIntersect;

            double dx = p2.X - p1.X;

            double dy = p2.Y - p1.Y;

 

            // 处理垂直边(避免除零)

            if (Math.Abs(dy) < 1e-8)

                continue; // 已处理水平边,此处为垂直边但y不相等,不可能与射线相交

 

            double t = (py - y1) / dy; // 参数t∈[0,1]表示交点在边上的位置

            xIntersect = p1.X + t * dx;

 

            // 交点必须在射线右侧(xIntersect ≥ px - 1e-8,允许微小误差)且在边的范围内(t∈[0,1])

            if (xIntersect >= px - 1e-8 && t >= -1e-8 && t <= 1 + 1e-8)

            {

                // 处理射线经过顶点的情况(左闭右开规则:仅当顶点是边的“下端点”时计数)

                if ((y1 < py + 1e-8 && y2 >= py + 1e-8) || (y2 < py + 1e-8 && y1 >= py + 1e-8))

                {

                    // 排除顶点在射线上且相邻边同向的情况(避免重复计数)

                    if (!(Math.Abs(y1 - py) < 1e-8 && Math.Abs(y2 - py) < 1e-8))

                        intersectionCount++;

                }

            }

        }

 

        // 奇数次相交则在内部,边界情况已提前处理

        return intersectionCount % 2 == 1;

    }

 

    /// <summary>

    /// 判断点是否在线段上(包含端点,允许1e-8精度误差)

    /// </summary>

    private static bool IsPointOnSegment(Point3d point, Point3d p1, Point3d p2)

    {

        double cross = (point.X - p1.X) * (p2.Y - p1.Y) - (point.Y - p1.Y) * (p2.X - p1.X);

        if (Math.Abs(cross) > 1e-8) return false; // 点不在直线上

 

        double minX = Math.Min(p1.X, p2.X) - 1e-8;

        double maxX = Math.Max(p1.X, p2.X) + 1e-8;

        double minY = Math.Min(p1.Y, p2.Y) - 1e-8;

        double maxY = Math.Max(p1.Y, p2.Y) + 1e-8;

 

        return point.X >= minX && point.X <= maxX && point.Y >= minY && point.Y <= maxY;

    }

}

http://www.xdnf.cn/news/875.html

相关文章:

  • JVM内存模型与垃圾回收
  • 蚂蚁全媒体总编刘鑫炜再添新职,出任共工新闻社新媒体研究院院长
  • 《FDTD Solutions仿真全面教程:超构表面与光束操控的前沿探索》
  • vue项目通过GetCapabilities获取wms服务元数据信息并在openlayers进行叠加显示
  • prometheus-operator部署服务监控其他节点mysql服务
  • 重构・协同・共生:传统代理渠道数字化融合全链路解决方案
  • 如何远程访问家中服务器-FRP内网穿透详细
  • 获取电脑信息(登录电脑的进程、C盘文件信息、浏览器信息、IP)
  • Windows网络及服务:制作系统盘
  • idea30天使用无限使用
  • uni-app 状态管理深度解析:Vuex 与全局方案实战指南
  • Android audio_policy_configuration.xml加载流程
  • 云原生--基础篇-2--云计算概述(云计算是云原生的基础,IaaS、PaaS和SaaS服务模型)
  • 在 UE5 编辑器中,由于游戏设置 -> EV100 设置,点击播放前后的光照不同。如何保持点击播放前后的光照一致?
  • 如何在腾讯云Ubuntu服务器上部署Node.js项目
  • SVM(支持向量机)
  • Ray Tracing(光线追踪)与 Ray Casting(光线投射)
  • 【pytorch】torch.nn.Unfold操作
  • PyTorch实现糖尿病预测的CNN模型:从数据加载到模型部署全解析【N折交叉验证、文末免费下载】
  • Android端使用无障碍服务实现远程、自动刷短视频
  • 流量抓取工具(wireshark)
  • 大文件分片上传进阶版(新增md5校验、上传进度展示、并行控制,智能分片、加密上传、断点续传、自动重试),实现四位一体的网络感知型大文件传输系统‌
  • 二叉树操作与遍历实现
  • 详细介绍:MCP(大模型上下文协议)的架构与组件,以及MCP的开发实践
  • VTK官方C++ demo1
  • 论文分享:【2024 CVPR】Vision-and-Language Navigation via Causal Learning
  • Harbor对接非AWS对象存储
  • 零基础学Python——第六章:Python标准库
  • mongodb 存储数据的具体实现方式
  • 计算机视觉中的正则化:从理论到实践的全面解析