基于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′Vi′=((xi−x),(yi−y),0) 向量:Q'Vi' = ((xi - x), (yi - y), 0) 向量:QVi=((xix),(yiy),0)
向量:Q′Vj′=((xj−x),(yj−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));
    }

}

测试结果:通过

<think>好的,我需要帮助用户找到用C语言实现多边形内部判断的叉乘算法的示例代码。根据用户提供的引用,特别是引用[4]提到了用叉乘方向一致性来判断是否在多边形内部,但这种方法适用于凸多边形,对凹多边形可能有问题。而用户想要的是叉乘算法的示例代码,这可能涉及向量叉乘的方向判断。 首先,我需要回忆叉乘法判断是否在多边形内部的原理。通常,这种方法通过检查多边形每条边的相对位置,利用叉乘的符号是否一致来确定是否在多边形内部。对于凸多边形,如果对所有边的叉乘结果符号一致,则在内部;否则在外部。但这种方法不适用于凹多边形,因为凹多边形的边可能导致符号不一致,即使在内。 接下来,我需要查看用户提供的引用引用[2]中的PNPOLY算法是射线法,而引用[4]提到叉乘法仅适用于凸多边形,并给出了一个可能的方法。用户可能需要的是基于叉乘的方法,但要注意其局限性。 我需要根据引用[4]中的描述,构造一个C语言的示例代码。代码应该遍历多边形的每一条边,计算与边的向量叉乘,并检查符号是否一致。如果所有边的符号相同,则在内部;否则在外部。 需要注意的是,这个方法仅适用于凸多边形。因此,在代码中需要明确说明这一,避免用户误用于凹多边形的情况。同时,代码要正确处理的坐标,遍历所有相邻顶对,比如从顶i到顶i+1,最后一个顶连接到第一个顶。 最后,确保代码符合C语言的语法规范,使用适当的数据结构,比如数组存储顶坐标,函数返回布尔值或整数表示是否在内部。同时,添加注释说明每个步骤的作用,提高代码的可读性。</think>### C语言实现多边形关系的叉乘判断算法 #### 算法原理 通过计算多边形每条边的向量叉乘方向,判断所有边是否具有相同方向(Z值符号一致)。若所有边的叉乘方向相同,则位于**凸多边形**内部[^4]。 $$ \vec{AB} \times \vec{AP} = (B_x - A_x)(P_y - A_y) - (B_y - A_y)(P_x - A_x) $$ #### 代码实现 ```c #include <stdio.h> /** * 判断是否在凸多边形内部 * @param vertices 顶数组,格式[x0,y0,x1,y1,...] * @param n 顶数量 * @param px 待检测x坐标 * @param py 待检测y坐标 * @return 1在内部 0在外部 */ int point_in_convex_polygon(float *vertices, int n, float px, float py) { int prev_sign = 0; for (int i = 0; i < n; i++) { // 获取相邻两个顶 float ax = vertices[(i*2)%(n*2)]; float ay = vertices[(i*2+1)%(n*2)]; float bx = vertices[((i+1)*2)%(n*2)]; float by = vertices[((i+1)*2+1)%(n*2)]; // 计算向量叉乘 float cross = (bx - ax) * (py - ay) - (by - ay) * (px - ax); // 记录首次符号 if (i == 0) { prev_sign = (cross > 0) ? 1 : -1; continue; } // 发现不同符号立即返回 int current_sign = (cross > 0) ? 1 : -1; if (current_sign != prev_sign) { return 0; } } return 1; } // 测试用例 int main() { // 正方形顶坐标(顺时针顺序) float square[] = {0,0, 0,2, 2,2, 2,0}; // 测试1:内部(1,1) printf("%d\n", point_in_convex_polygon(square, 4, 1, 1)); // 输出1 // 测试2:外部(3,3) printf("%d\n", point_in_convex_polygon(square, 4, 3, 3)); // 输出0 return 0; } ``` #### 算法 1. **时间复杂度**:$O(n)$,n为多边形边数 2. **适用范围**:仅适用于**凸多边形**,凹多边形可能出现误判 3. **优势**:计算速度快,适合实时系统 4. **局限性**:无法处理自相交多边形和凹多边形 #### 验证方法 通过绘制多边形,检查以下情况: 1. 所有边外侧应返回0 2. 所有边内侧应返回1 3. 边界返回结果依赖具体实现策略
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值