【算法学习】射线法判断点在多边形内外(C#)以及确定内外两点连线与边界的交点

1.前言:

在GIS开发中,经常会遇到确定一个坐标点是否在一块区域的内部这一问题。
如果这个问题不是一个单纯的数学问题,例如:在判断DEM、二维图像像素点、3D点云点等含有自身特征信息的这些点是否在一个区域范围内部的时候,可以结合其高程信息、RGB信息、深度信息来辅助处理,相比与单纯从数学角度来看更简单、快速。

举几个我认为正确的例子:SLAM中前端角点的选取,利用的是OpenCV来提取;DEM提取边界,根据周围高程的有无;PS中扣出某物边界,利用的是RGB差异性;点云提取可以利用深度信息(本质也是RGB)来做。

但是,如果我现在只拥有点的坐标,该问题就变成一个数学问题了。

2.射线法:

在这里插入图片描述

该算法基本思路是从待定点朝任意方向射出一条射线(通常是水平向右),判断该射线与多边形边的交点个数。一般来说,交点个数为偶数(包括0),点在外部;交点个数为奇数,点在内部。
因为点和图形的位置是固定不动的,所以射线的朝向,对于最终的交点个数,也就是位置结果是没有影响的。

2.1 算法介绍

在分析前要先明白几个问题:

  • 如果没有特殊需求,待求点在图形的边界(线段、交点)上,默认是属于图形内部的。
  • 默认待求点的射线沿着x轴方向水平射出(水平向)。
  • 射线经过边界交点情况很常见,为了防止上一个线段的末顶点和下一个线段的首顶点(这两个是一个点)被算作两次,所以只看线段的y更小的一端,即参数方程的值域符号:[y1,y2)
    假如逆时针遍历各边,看下图示例:
    在这里插入图片描述

(1)从简单情况开始分析:
最简单的情况当属一个规整的四边形,射线与四边形的交点个数存在的情况有:0,1,2。
如果,不考虑穿过顶点,不考虑点的射线与边平行(重合),就单纯考虑穿的全部是边,遇到这种情况:
先建立遍历边的参数方程,找到射线与参数方程的交点,再判断交点X交和待定点的位置关系。
在这里插入图片描述
如果P在X交左侧,有一个交点则计数+1;
如果P和X交是一个点,则说明P在边界线段上,直接返回true;
如果在X交右侧,没有交点。

注意:这里的上下yi和yj的取值,要包含下界,不包含上界,否则会被计算两次,而且这样可以有效忽略水平边界而待求点在左侧的问题。
当然,反过来也可以,都选择更大的y值。

(2)我认为的几种特殊情况,这几种特殊情况有特点:就是无法找到线段去做参数方程或者实际交点无数个。这几种特殊情况需要单独处理。

  • 待求点就是边界交点:通过坐标判断,直接返回是否在边界内。
  • 待求点在水平边界线上:通过坐标判断,直接返回是否在边界内。
  • 待求点在水平边界线左侧:配合前后该水平边界先后线段参数方程的值域,这种情况可以直接忽略!(忽略不是没有考虑)

2.2 C#代码实现

using System;
using System.Collections.Generic;

public class Point
{
    public double X { get; set; }
    public double Y { get; set; }

    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }
}
public class Polygon
{
    private List<Point> vertices;

    public Polygon(List<Point> points)
    {
        vertices = points;
    }

    public bool IsPointInside(Point testPoint)
    {
        int intersectionCount = 0;
        int vertexCount = vertices.Count;

        for (int i = 0, j = vertexCount - 1; i < vertexCount; j = i++)
        {
            Point vi = vertices[i];
            Point vj = vertices[j];

            // 检查测试点是否在顶点上
            if ((vi.X == testPoint.X && vi.Y == testPoint.Y) ||
                (vj.X == testPoint.X && vj.Y == testPoint.Y))
            {
                return true;
            }

            // 检查测试点是否在水平边上
            if (vi.Y == vj.Y && vi.Y == testPoint.Y)
            {
                if (testPoint.X > Math.Min(vi.X, vj.X) && testPoint.X < Math.Max(vi.X, vj.X))
                {
                    return true;
                }
            }
            // 检查testpoint.y是否在两个端点的中间
            //if ((vi.Y > testPoint.Y) != (vj.Y > testPoint.Y)) // 这行代码更简单,但是有点小小的不直观
            if ((vi.Y <= testPoint.Y && vj.Y > testPoint.Y) || (vj.Y <= testPoint.Y && vi.Y > testPoint.Y))
            {
                double intersectionX = (vj.X - vi.X) * (testPoint.Y - vi.Y) / (vj.Y - vi.Y) + vi.X;
                // 处理边界情况
                if (testPoint.X == intersectionX)
                {
                    return true;
                }

                if (testPoint.X < intersectionX)
                {
                    intersectionCount++;
                }
            }
        }
        // 如果交点数为奇数,则点在多边形内部
        return intersectionCount % 2 != 0;
    }
}
internal class Program
{
    static void Main(string[] args)
    {
        List<Point> vertices = new List<Point>
        {
            new Point(0,0),
            new Point(1,0),
            new Point(2,-1),
            new Point(3,0),
            new Point(5,0),
            new Point(5,1),
            new Point(4,1),
            new Point(4,2),
            new Point(3,3),
            new Point(3,4),
            new Point(2,4),
            new Point(2,3),
            new Point(1,3),
            new Point(1,4),
            new Point(0,4),
            new Point(0,2),
            new Point(-1,1),
        };

        Polygon polygon = new Polygon(vertices);

        List<Point> testPoint = new List<Point>();
        for (int i = -1; i < 6; i++)
        {
            for (int j = -1; j < 7; j++)
            {
                testPoint.Add(new Point(i, j));
            }
        }

        foreach (var p in testPoint)
        {
            Console.WriteLine($"p点坐标:({p.X}, {p.Y}),是否在图形内部:{polygon.IsPointInside(p)}");
        }

        Console.ReadKey();
    }
}

在这里插入图片描述
用for循环写了一个从(-1,-1)到(5,6)覆盖的测试点,最后结果:

p点坐标:(-1, -1,是否在图形内部:False|p点坐标:(-1, 0,是否在图形内部:False
p点坐标:(-1, 1,是否在图形内部:True|p点坐标:(-1, 2,是否在图形内部:False
p点坐标:(-1, 3,是否在图形内部:False|p点坐标:(-1, 4,是否在图形内部:False
p点坐标:(-1, 5,是否在图形内部:False|p点坐标:(-1, 6,是否在图形内部:False
p点坐标:(0, -1,是否在图形内部:False|p点坐标:(0, 0,是否在图形内部:True
p点坐标:(0, 1,是否在图形内部:True|p点坐标:(0, 2,是否在图形内部:True
p点坐标:(0, 3,是否在图形内部:True|p点坐标:(0, 4,是否在图形内部:True
p点坐标:(0, 5,是否在图形内部:False|p点坐标:(0, 6,是否在图形内部:False
p点坐标:(1, -1,是否在图形内部:False|p点坐标:(1, 0,是否在图形内部:True
p点坐标:(1, 1,是否在图形内部:True|p点坐标:(1, 2,是否在图形内部:True
p点坐标:(1, 3,是否在图形内部:True|p点坐标:(1, 4,是否在图形内部:True
p点坐标:(1, 5,是否在图形内部:False|p点坐标:(1, 6,是否在图形内部:False
p点坐标:(2, -1,是否在图形内部:True|p点坐标:(2, 0,是否在图形内部:True
p点坐标:(2, 1,是否在图形内部:True|p点坐标:(2, 2,是否在图形内部:True
p点坐标:(2, 3,是否在图形内部:True|p点坐标:(2, 4,是否在图形内部:True
p点坐标:(2, 5,是否在图形内部:False|p点坐标:(2, 6,是否在图形内部:False
p点坐标:(3, -1,是否在图形内部:False|p点坐标:(3, 0,是否在图形内部:True
p点坐标:(3, 1,是否在图形内部:True|p点坐标:(3, 2,是否在图形内部:True
p点坐标:(3, 3,是否在图形内部:True|p点坐标:(3, 4,是否在图形内部:True
p点坐标:(3, 5,是否在图形内部:False|p点坐标:(3, 6,是否在图形内部:False
p点坐标:(4, -1,是否在图形内部:False|p点坐标:(4, 0,是否在图形内部:True
p点坐标:(4, 1,是否在图形内部:True|p点坐标:(4, 2,是否在图形内部:True
p点坐标:(4, 3,是否在图形内部:False|p点坐标:(4, 4,是否在图形内部:False
p点坐标:(4, 5,是否在图形内部:False|p点坐标:(4, 6,是否在图形内部:False
p点坐标:(5, -1,是否在图形内部:False|p点坐标:(5, 0,是否在图形内部:True
p点坐标:(5, 1,是否在图形内部:True|p点坐标:(5, 2,是否在图形内部:False
p点坐标:(5, 3,是否在图形内部:False|p点坐标:(5, 4,是否在图形内部:False
p点坐标:(5, 5,是否在图形内部:False|p点坐标:(5, 6,是否在图形内部:False

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/755520.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

eclipse基础工程配置( tomcat配置JRE环境)

文章目录 I eclipse1.1 工程配置1.2 编译工程1.3 添加 JRE for the project build pathII tomcat配置JRE环境2.1 Eclipse编辑tomcat运行环境(Mac版本)2.2 Eclipse编辑tomcat运行环境(windows版本)2.3 通过tomcat7W.exe配置运行环境(windows系统)I eclipse 1.1 工程配置 …

C++笔记:实现一个字符串类(构造函数、拷贝构造函数、拷贝赋值函数)

实现一个字符串类String&#xff0c;为其提供可接受C风格字符串的构造函数、析构函数、拷贝构造函数和拷贝赋值函数。 声明依赖文件 其中ostream库用于打印标准输入输出&#xff0c;cstring库为C风格的字符串库 #include <iostream> #include <cstring> 声明命…

Matplotlib绘制并列的条形图:每个类别有多个条形并排显示

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

大语言模型LLM基础:推理/不同模型/量化对显存、推理速度和性能的影响

通过本文&#xff0c;你将了解以下几个方面的内容&#xff1a; 要运行一个LLM需要多少显存&#xff1f;&#xff08;我的GPU可以运行多大LLM&#xff1f;&#xff09;不同LLM推理速度如何&#xff1f;量化对显存、推理速度和性能的影响&#xff1f;vLLM、DeepSeed等工具的加速…

您还在为企业无公网独立IP而烦恼吗?

#IBCS虚拟专线# 企业对于高效、稳定且经济实惠的网络解决方案的需求愈发迫切。作为一家企业的技术负责人&#xff0c;我有幸接触并采用了 IBCS 虚拟专线&#xff0c;它的出现&#xff0c;犹如一道曙光&#xff0c;解决了我们长期以来面临的诸多网络难题。 我们企业是一家处于…

visual studio 2022配置和使用jsoncpp

下载 jsoncpp下载位置&#xff1a; GitHub - open-source-parsers/jsoncpp: A C library for interacting with JSON. 编译库 1、下载完成之后解压 2、在解压文件的makefiles文件下有个vs71&#xff0c;在vs71中有visual studio项目&#xff0c;不过这里的项目是visual stud…

用通俗易懂方式讲解:大模型 ChatGLM3 进行 LORA 高效微调全流程

本章我们以 ChatGLM3 为例&#xff0c;对 ChatGLM3-6B 模型进行 LORA 高效微调。 本章尽量用最简洁的语言及方法对大模型进行微调实际操练。 什么是 LORA 高效微调&#xff1a; lora微调原理论文&#xff1a; https://arxiv.org/abs/2106.09685 用最简单的语言理解LORA高效…

TCP/IP模型原理(理论)

TCP/IP模型 1. 网络模型简介2. 应用层2.1 URL2.1.1 urlencode和urldecode 2.2 HTTP协议2.2.1 HTTP协议格式2.2.2 HTTP问题2.2.3 HTTPS 3 传输层3.1 端口号3.2 udp3.2.1 udp协议帧格式3.2.2 udp特点3.2.3 udp缓冲区3.2.4 注意 3.3 tcp协议3.3.1 tcp协议段格式3.3.2 确认应答机制…

UE4_材质_水体的反射与折射制作_Ben教程

在这个教程中&#xff0c;将制作水的反射和折射&#xff0c;上个教程&#xff0c;我们主要讲了制作水涟漪&#xff08;水面波纹&#xff09;和水滴法线混合&#xff0c;水深计算&#xff0c;我们首先要谈的是反射和产生折射的问题。我们将所有从干扰从场景中分离出去&#xff0…

vue3 前端 去循环一个接口获取结果

有的时候 在我们开发过程中我i们会出现一个问题 就是一个后端的接口 哦我们需要调用多次才会出现结果 我们就需要连续掉用 有时候为了避免后端的压力的太大 我总结了一下前端的写法 1.有次数限制的 const getPayData async (orderId) > {const orderResult await ind…

MacBook 中 Java使用testcontainers报错

环境 MacBook 问题 Java代码中使用testcontainers启动报错 ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - Could not find a valid Docker environment. Please check configuration. Attempted configurations were:UnixSocketClientProviderStr…

鸿蒙NEXT

[中国&#xff0c;东莞&#xff0c;2024年6月24日] 华为开发者大会&#xff08;HDC&#xff09;正式开幕&#xff0c;带来全新的 HarmonyOS NEXT、盘古大模型5.0等最创新成果&#xff0c;持续为消费者和开发者带来创新体验。 HarmonyOS NEXT 鸿蒙生态 星河璀璨 鸿蒙生态设备数…

亨廷顿(Huntington)方法-名额分配

前言 20世纪初&#xff0c;美国人口普查局局长约瑟夫A亨廷顿&#xff08;Joseph A. Hill&#xff09;和数学家爱德华V亨廷顿&#xff08;Edward V. Huntington&#xff09;在研究议会议席分配问题时&#xff0c;提出了一种基于数学原理的算法。该算法通过计算每个州的人口比例&…

【数组】- 螺旋矩阵 II

1. 对应力扣题目连接 螺旋矩阵 II 题目简述&#xff1a; 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。如图&#xff1a; 2. 实现案例代码 public class SpiralMatrix {public static…

重磅更新-UniApp自定义字体可视化设计

重磅更新-UniApp自定义字体可视化设计。 DIY可视化为了适配不同APP需要&#xff0c;支持用户自定义字体&#xff0c;自定义字体后&#xff0c;设计出来的界面更多样化&#xff0c;不再是单一字体效果。用户可以使用第三方字体加入设计&#xff0c;在设计的时候选择上自己的字体…

MyBatis第一节

目录 1. 简介2. 配置3. doing3.1 创建一个表3.2 打开IDEA&#xff0c;创建一个maven项目3.3 导入依赖的jar包3.4 创建entity3.5 编写mapper映射文件(编写SQL)3.6 编写主配置文件3.7 编写接口3.8 测试 参考链接 1. 简介 它是一款半自动的ORM持久层框架&#xff0c;具有较高的SQ…

【技术指南】稳压器(电压调节器):原理、类型及其实际用用案例

电压调节器&#xff08;稳压器&#xff09;是一种电子器件或电路&#xff0c;用于控制电路中的电压水平&#xff0c;以确保在电源电压波动或负载变化时&#xff0c;输出电压能够保持在设定的稳定水平。它们通常用于各种电子设备和电源系统中&#xff0c;以提供稳定的电压供应。…

AI绘画 Stable Diffusion【特效文字】:火焰特效艺术字,轻松搞定特效生成!

大家好&#xff0c;我是画画的小强 今天我们继续艺术字系列的分享&#xff0c;艺术字的玩法很多&#xff0c;今天给大家带来的是火焰特效艺术字的制作。我们先来看火焰特效艺术字的效果图。 一. 火焰特效文字的制作方法 【第一步】&#xff1a;制作底图 这里制作底图使用白底…

HarmonyOS Next开发学习手册——通过startAbilityByType拉起垂类应用

使用场景 开发者可通过特定的业务类型如导航、金融等&#xff0c;调用startAbilityByType接口拉起对应的垂域面板&#xff0c;该面板将展示目标方接入的垂域应用&#xff0c;由用户选择打开指定应用以实现相应的垂类意图。垂域面板为调用方提供统一的安全、可信的目标方应用&a…

数据结构与算法笔记:高级篇 - 搜索:如何用 A* 搜索算法实现游戏中的寻路功能?

概述 魔兽世界、仙剑奇侠传这类 MMRPG 游戏&#xff0c;不知道你玩过没有&#xff1f;在这些游戏中&#xff0c;有一个非常重要的功能&#xff0c;那就是任务角色自动寻路。当任务处于游戏地图中的某个位置时&#xff0c;我们用鼠标点击另外一个相对较远的位置&#xff0c;任务…