基于sign(x)函数的点在多边形内外判别算法(引用论文,侵删)

基于sign(x)函数的点在多边形内外判别算法

引用论文:

孙爱玲,赵光华,赵敏华,常璐.基于sign(x)函数的点在多边形内外判别算法及应用[J].计算机工程与科学,2017,39(04):785-790

一、名词定义

多边形的一般定义为:

给定平面上一系列共面的点V,V2,…,Vn,将这些点依次用线段连接,这些点就是多边形的顶点,线段就是多边形的边。
若多边形的不相邻的边对不相交,则称该多边形为简单多边形。
简单多边形把平面分为两个不同的区域:内部区域(有界的)和外部区域(无界的)。
这样,平面中点与多边形的位置关系有:点在多边形内、点在多边形外、点在多边形边上或顶点上

在几何中:

二维空间仅指的是一个平面,上面的每一个点可以由两个数构成的坐标(x, y)表示;
三维空间则是立体的,点的位置由(x ,y,z)三个坐标决定。
将二维空间看作xoy平面,三维空间看作o'x'y'z’立体空间,那么二维平面上的任意点可以看作是三维空间o'x'y'z’在c'o'y′平面上的点Q',这样二维平面上点的坐标(x,y)扩展	为空间坐标而成为(x, y,0)。

sign(x)定义:

二、算法思路

将xoy 平面上的多边形的顶点从某一起始点开始,沿同一方向(如:逆时针方向)进行编号,如图

所示,点Vi、V+1分别是多边形某条边的两个顶点,点Q是对应平面上的一点。现在要判别点Q与多边形的位置关系。
我们可以这样做:将平面xoy 上的点Q(x,y)、多边形的顶点Vi(xi,yi)和Vj(xj,yj)看作是三维空间oxyz在xoy平面上的点Q’、Vi’及 Vj’,如图

所示,其平面坐标扩展为空间坐标而成为(x,y,0)、(xi,yi ,0)及(xj,yj,0)。
于是得出:
向量: Q ′ V i ′ = ( ( x i − x ) , ( y i − y ) , 0 ) 向量:Q'Vi' = ((xi - x), (yi - y), 0) 向量:QVi=((xix),(yiy),0)
向量: Q ′ V j ′ = ( ( x j − x ) , ( y j − y ) , 0 ) 向量:Q'Vj' = ((xj - x), (yj - y), 0) 向量:QVj=((xjx),(yjy),0)
则两向量的积为:

得到了向量的乘积后代入sign(x)函数:

分类讨论:
(1)如果sign(x) = 0,则点Q、Vi、Vj三点共线,点Q可能位于线段 V-Vi 的延长线上,也可能位于线段** V-Vi** 上(包括端点)。但是,当该点同时满足以下条件时:

待判断点位于多边形上(边界或顶点)。所以,在算法中按式(6)进行判断。
(2)如果sign(z)≠0,过Q点作一条射线,该射线可能与多边形的某几条边有交点(但不需要求出交点坐标,也就不存在坐标的计算误差),只需进行Q点与其相交边对应顶点坐标值的计算。
为计算方便,作一条水平射线,如图

所示,存在交点的边的顶点坐标与待判断点的坐标关系存在两种情况,分别为 yi < y < yj 或 yi > y > yj
在这两种情况下,作如下的计算:

K为一个累加和
若 K≠0 时,点Q位于多边形S内部;
若 K=0 时,点Q位于多边形S外部。

三、自然语言表述

(https://raw.githubusercontent.com/DN20011219/pic_home/main/258914213248856.png)]

四、算法实现代码:

点的数据模型

//点 数据模型
public class Point {
    public double x;
    public double y;

    public Point() {

    }

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return "Point{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }

}

具体算法

import Point;

import java.util.List;

/**
 * 检查点是否在图形内部
 * @孙爱玲,赵光华,赵敏华,常璐.基于sign(x)函数的点在多边形内外判别算法及应用[J].计算机工程与科学,2017,39(04):785-790
 * @author dingN
 * @date 2022/07/11
 */
public class CheckIsInside {

    static int K = 0; // 判断所用累加值

    /**
     * 检查 某点是否在图形内部
     * @param contourPoints 轮廓点, 依次连线
     * @param specific      待测点
     * @return boolean
     */
    public static boolean CheckInside(
            List<Point> contourPoints,
            Point specific
    ) {
        K = 0;
        int listLength = contourPoints.size();
        for (int i = 0; i < listLength; i++) {
            
            Point firstPoint = contourPoints.get(i);
            Point secondPoint = contourPoints.get((i + 1) % listLength);

            double difference = calculateVectorDifference(firstPoint, secondPoint, specific);
            
            int signX = sign(difference);  
            
            if (signX == 0 && whetherOnEdge(firstPoint, secondPoint, specific)) {
                return false; // 这里认为如果待测点在某边上,也同样是不在图形内
            } else {
                AddK(firstPoint, secondPoint, specific, signX);
            }
            
        }
        
        if (K == 0) return false; // K=0, 点在多边形外部
        return true; // k != 0, 点在多边形内部
    }

    /**
     * 计算向量差
     *
     * @param pointI       point i
     * @param pointIAddOne point i + 1
     * @param specific     待测点
     * @return double
     */
    public static double calculateVectorDifference(
            Point pointI,
            Point pointIAddOne,
            Point specific
    ) {
        return (pointI.x - specific.x) * (pointIAddOne.y - specific.y) - (pointIAddOne.x - specific.x) * (pointI.y - specific.y);
    }

    /**
     * 标志函数 sign(x)
     * @param x x
     * @return int
     */
    public static int sign(
            double x
    ) {
        if (x == 0)
            return 0;
        else if (x > 0)
            return 1;
        else
            return -1;
    }

    /**
     * 待测点是否在边上
     *
     * @param pointI       point i
     * @param pointIAddOne point i + 1
     * @param specific     待测点
     * @return boolean
     */
    public static boolean whetherOnEdge(
            Point pointI,
            Point pointIAddOne,
            Point specific
    ) {
        if ( // 在边上
                specific.x <= Math.max(pointI.x, pointIAddOne.x) && specific.x >= Math.min(pointI.x, pointIAddOne.x)
                        &&
                        specific.y <= Math.max(pointI.y, pointIAddOne.y) && specific.y >= Math.min(pointI.y, pointIAddOne.y)
        ) {
            System.out.println("待测点位于边" + pointI + " " + pointIAddOne + "上");
            return true;
        }
        System.out.println("待测点不位于边" + pointI + " " + pointIAddOne + "上");
        return false;
    }

    /**
     * addK
     * @param pointI       point i
     * @param pointIAddOne point i + 1
     * @param specific     待测点
     * @param sign         标志函数的结果
     */
    public static void AddK(
            Point pointI,
            Point pointIAddOne,
            Point specific,
            int sign
    ) {
        if ((pointIAddOne.y < specific.y && specific.y < pointI.y) || (pointI.y < specific.y && specific.y < pointIAddOne.y)) {
            K += sign;
        }
    }
    
}

测试代码

这里仅测试了 点在凸多边形内部、点不在凹多边形内部 两种情况

import Point;
import CheckIsInside;
import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class checkTest {

    @Test
    public void testCheckInside() {

        List<Point> contourPoints = new ArrayList<>();
        contourPoints.add( new Point(0, 0));
        contourPoints.add( new Point(2, 0));
        contourPoints.add( new Point(2, 2));
        contourPoints.add( new Point(1, 3));
        contourPoints.add( new Point(0, 2));
        Point specific = new Point(1, 1);

        Assert.assertEquals(true, CheckIsInside.CheckInside(contourPoints, specific));
    }

    @Test
    public void testCheckInsideTwo() {

        List<Point> contourPoints = new ArrayList<>();
        contourPoints.add( new Point(0, 0));
        contourPoints.add( new Point(2, 0));
        contourPoints.add( new Point(2, 2));
        contourPoints.add( new Point(1, 1));
        contourPoints.add( new Point(0, 2));
        Point specific = new Point(1, 2);

        Assert.assertEquals(false, CheckIsInside.CheckInside(contourPoints, specific));
    }

}

测试结果:通过

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值