清华计算几何-ConvexHull(凸包)-求极点InTriangle/ToLeft Test

news2024/9/16 19:12:50

ConvexHull(凸包)

凸包是什么

凸包是计算几何一个非常基础核心的概念。我理解的凸包就是给定一个点集合, 最外围的点的包围体就是凸包。如下所示:

极点(ExtremityPoint)

给定的点集合中, 如果一个点存在一条直线, 让其他所有点都在于该直线的同一侧, 则该点为极点。

非极点

和极点性质相反, 经过该点任一直线都无法做到让其他所有点位于同一侧

凸集合

给定一个点集合S = [P1, P2...Pn], 给每个点分配一个权重rx,  满足条件:

[1]rx >= 0 &&rx <= 1, 

[2]r1 + r2 +  ...... rn = 1.0

由 P = r1 * P1 + ..... + Pn * rn 计算公式,  得到新的点集合,成为S的凸组合.

我理解的是点S构成的凸包内部的点集合就是凸组合。

凸相关

给定一个点集合S, 加入一个点A后,凸组合没变化的,就称点A和点集合S是凸相关的。

换一个说法就是, 点A包含在集合S里的凸组合里。

比如给定点S = {1, 4}, 点2或者点3和集合S是凸相关。

凸无关

给定一个点集合S, 加入一个点A后,凸组合发生变化, 就称点A和点集合S是凸无关。

对于点集合S(1, 2, 3), 点4是凸无关.

给定点集合求极点

分解问题

极点满足: 不在点集合构成的所有三角形内部的点, 则为极点。反之为非极点。

伪代码实现

算法复杂度为O(n4)

算法实现

In-Trianle-Test

In-Trianle-Test: 判断一个点是否在一个三角形内.

如果点S在三角形三条边的同一侧, 则在三角形内. 分解为三次ToLeft测试,即点和三角形的三条边的测试.

ToLeft测试

ToLeft测试就是用于判断点是否在一条边的左侧.

叉积面积正负来判断左侧 or 右侧

(CCW 的时候ToLeft 世界左侧返回True, CW的时候ToLeft 世界左侧返回False)

代码实现

给定点集合

代码实现

#include <iostream>
#include <vector>

using namespace std;

struct Point
{
	float x;
	float y;
};


float Area2(const Point& inPointA, const Point& inPointB, const Point& inPointC)
{
	float value =
		inPointA.x * inPointB.y - inPointA.y * inPointB.x
		+ inPointB.x * inPointC.y - inPointB.y * inPointC.x
		+ inPointC.x * inPointA.y - inPointC.y * inPointA.x;

	return value;
}


bool IsLeftTest(const Point& inPointA, const Point& inPointB, const Point& inPointC)
{
	return Area2(inPointA, inPointB, inPointC) > 0;
}


bool IsInTrianle(const Point& inPoint, const Point& triangleA, const Point& triangleB, const Point& triangleC)
{
	bool bLeftA = IsLeftTest(triangleA, triangleB, inPoint);
	bool bLeftB = IsLeftTest(triangleB, triangleC, inPoint);
	bool bLeftC = IsLeftTest(triangleC, triangleA, inPoint);

	return (bLeftA == bLeftB) && (bLeftB == bLeftC);
}


void GetConvexPointSet(const vector<Point>& inPoints, vector<Point>& outPoints)
{
	int pointNum = inPoints.size();
	vector<bool> extrmeFlags;
	extrmeFlags.resize(pointNum);
	for (int index = 0; index < pointNum; index++)
	{
		extrmeFlags[index] = true;
	}

	// O(n4)
	for (int idxA = 0; idxA < pointNum; idxA++)
	{
		for (int idxB = idxA + 1; idxB < pointNum; idxB++)
		{
			for (int idxC = idxB + 1; idxC < pointNum; idxC++)
			{
				for (int s = 0; s < pointNum; s++)
				{
					if (s == idxA || s == idxB || s == idxC || !extrmeFlags[s])
						continue;


					if (IsInTrianle(inPoints[s], inPoints[idxA], inPoints[idxB], inPoints[idxC]))
					{
						extrmeFlags[s] = false;
					}
				}
			}
		}
	}

	for (int index = 0; index < pointNum; index++)
	{
		if (extrmeFlags[index])
		{
			outPoints.push_back(inPoints[index]);
		}
	}

}


int main()
{
    std::cout << "Hello World!\n";

	// point set contruct
	vector<Point> inPoints =
	{
		{0, 0},
		{-1, -1},
		{5, 2},
		{4, 5},
		{3, 3},
		{-1, 3},
		{2, 2},
		{-3, 2},
	};

	vector<Point> outPoints;
	GetConvexPointSet(inPoints, outPoints);
	for (int index = 0; index < outPoints.size(); index++)
	{
		printf("(%f, %f)\n", outPoints[index].x, outPoints[index].y);
	}

}
运行结果

很显然漏掉了 (-1, -1, -1, -1)

原因是(-1, -1), (0, 0) (2, 2), (3, 3)四点共线导致ToLeft测试失效,误认为也在三角形内部。所以得改进检测算法。

改进In-Trianle-Test

在进行In-Trianle-Test的时候先判断是否四点共线, 然后在进行ToLeftTest

bool IsInTrianle(const Point& inPoint, const Point& triangleA, const Point& triangleB, const Point& triangleC)
{
	float a_area2 = Area2(triangleA, triangleB, inPoint);
	float b_area2 = Area2(triangleB, triangleC, inPoint);
	float c_area2 = Area2(triangleC, triangleA, inPoint);

	if (a_area2 == 0 && b_area2 == 0 && c_area2 == 0)
		return false;

	bool bLeftA = a_area2 > 0;
	bool bLeftB = b_area2 > 0;
	bool bLeftC = c_area2 > 0;

	// 取决于CCW/CW
	return (bLeftA == bLeftB) && (bLeftB == bLeftC);
}
运行结果

参考资料

[1]清华计算几何 P1-P12

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java--数据结构】栈:不仅仅是数据存储,它是编程的艺术

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 栈 栈的方法介绍 入栈push 出栈pop和 瞄一眼peek 判空isEmpty和判满isFull 模拟实现栈 push入栈 pop出栈和peek 测试 使用泛型实现栈 测试 使用链表实现栈&#xff08…

本地部署免费AI大模型+知识库

利用Ollama部署本地大模型 一&#xff1a;终端实现与大模型对话 1.去Ollama官网下载 https://ollama.com/download2.下载之后按照提示一步步安装即可 3.WinR, 输入cmd,打开命令行提示符 输入&#xff1a;ollama run llama3运行大模型 4.使用其他开源大模型&#xff0c;比如…

基于门控循环单元(GRU)的数据回归预测

代码原理 基于GRU&#xff08;Gated Recurrent Unit&#xff09;的数据回归预测通常涉及多输入单输出的情况。以下是简单的原理及流程&#xff1a; 数据准备&#xff1a; 准备多个时间序列作为输入特征&#xff0c;每个时间序列可以表示不同的变量或特征。准备一个目标变量作…

算法学习day12(动态规划)

一、不同的二叉搜索树 二叉搜索树的性质&#xff1a;父节点比左边的孩子节点都大&#xff1b;比右边的孩子节点都小&#xff1b; 由图片可知&#xff0c;dp[3]是可以由dp[2]和dp[1]得出来的。(二叉搜索树的种类和根节点的val有关) 当val为1时&#xff0c;左边是一定没有节点的…

写真图片视频打赏系统源码全开源无加密

这是一款开源的写真图片及视频打赏系统源码&#xff0c;顾名思义他可以做写真图片打赏站也可以做视频打赏站&#xff0c;支付对接了易支付&#xff0c;拥有独立代理后台&#xff0c;全部源码无加密&#xff0c;另外也可以配合付费进群使用。支付扣量、域名防洪这些基本的就不介…

基于信号处理的PPG信号滤波降噪方法(MATLAB)

光电容积脉搏波PPG信号结合相关算法可以用于人体生理参数检测&#xff0c;如血压、血氧饱和度等&#xff0c;但采集过程中极易受到噪声干扰&#xff0c;对于血压、血氧饱和度测量的准确性造成影响。随着当今社会医疗保健技术的发展&#xff0c;可穿戴监测设备对于PPG信号的质量…

修正版头像上传组件

修正版头像上传组件 文章说明核心源码展示运行效果展示源码下载 文章说明 在头像剪切上传一文中&#xff0c;我采用div做裁剪效果&#xff0c;感觉会有一些小问题&#xff0c;在昨天基于canvas绘制的功能中改进了一版&#xff0c;让代码变得更简洁&#xff0c;而且通用性相对高…

Linux的load(负载)

负载(load)是Linux机器的一个重要指标&#xff0c;直观了反应了机器当前的状态。 在Linux系统中&#xff0c;系统负载是对当前CPU工作量的度量&#xff0c;被定义为特定时间间隔内运行队列中的平均线程数。 Linux的负载高&#xff0c;主要是由于CPU使用、内存使用、10消…

[UTCTF2020]babymips

水一篇 32位 c写的&#xff0c;长得比较丑陋 进入sub-401164函数 V7的数据可以得到 unsigned char ida_chars[] {0x62, 0x6C, 0x7F, 0x76, 0x7A, 0x7B, 0x66, 0x73, 0x76, 0x50, 0x52, 0x7D, 0x40, 0x54, 0x55, 0x79, 0x40, 0x49, 0x47, 0x4D, 0x74, 0x19, 0x7B, 0x6A, 0x…

大数据之路 读书笔记 Day5 数据同步遇到的问题与解决方案

回顾 Day 4 数据同步Day 3 无线客户端的日志采集 1. 分库分表的处理 分库分表&#xff08;Sharding&#xff09;是数据库水平扩展的一种策略&#xff0c;当单个数据库的性能和存储能力无法满足应用需求时&#xff0c;可以采用分库分表来分散数据和查询负载。它通常包括两个方面…

钡铼4G无线RTU助力智慧能源发展实现电网远程调控

随着全球对清洁能源和高效能源管理的需求日益增长&#xff0c;智慧能源技术正逐渐成为推动可持续发展的重要驱动力。在这一背景下&#xff0c;钡铼4G无线远程终端单元正在为智慧能源的发展和电网的远程调控提供强有力的支持。 钡铼4G无线RTU&#xff1a;智慧能源的神经网络 钡…

数据恢复篇:适用于 Android 的恢复工具

正在摆弄 Android 设备。突然&#xff0c;您意外删除了一张或多张图片。不用担心&#xff0c;您总能找到一款价格实惠的照片恢复应用。这款先进的软件可帮助 Android 用户从硬盘、安全数字 (SD) 或存储卡以及数码相机中恢复已删除的图片。 Android 上文件被删除的主要原因 在获…

Blender使用(二)点线面基本操作

Blender使用之点线面 1.编辑模式 tab键进行切换&#xff0c;为了方便菜单调出&#xff0c;可以设置键位映射为拖动时的饼菜单。 设置好后&#xff0c;按住tab键移动鼠标(注意不要点击鼠标)&#xff0c;即可弹出编辑菜单。 默认是点模式&#xff0c;在左上角可进行点线面的切换…

Linux系统OpenSSH出现漏洞(CVE-2024-6387)修复

CVE-2024-6387 是一个影响 OpenSSH 服务器&#xff08;sshd&#xff09;的严重远程代码执行&#xff08;RCE&#xff09;漏洞&#xff0c;它允许未经身份验证的攻击者在受影响的 Linux 系统上以 root 权限执行任意代码。此漏洞尤其危险&#xff0c;因为它可以在不需要任何用户交…

husky 和 lint-staged 构建代码项目规范

目录 前言 最简单的方法 过 scripts 来解决如果检测工具多&#xff0c;需要多次处理 通过 husky(哈士奇)来解决容易遗忘的问题 1. 安装 2. husky init 3. 试一试​ lint-stadge 只 lint 改动的 1. 安装 2. 修改 package.json 配置 3. 添加 npm 脚本: 4.使用 Husky…

ETL数据集成丨主流ETL工具(ETLCloud、DataX、Kettle)数据传输性能大PK

目前市面上的ETL工具众多&#xff0c;为了方便广大企业用户在选择ETL工具时有一个更直观性能方面的参考值&#xff0c;我们选取了目前市面上最流行的三款ETL工具&#xff08;ETLCloud、DataX、Kettle&#xff09;来作为本次性能传输的代表&#xff0c;虽然性能测试数据有很多相…

类和对象 中篇

类和对象 中篇 ​ 在上篇中&#xff0c;我们介绍了类的基础部分&#xff0c;本篇我们讲解C类的六大默认成员函数 ​ 所谓默认成员函数&#xff0c;就是我们不写编译器也会自动生成&#xff0c;自动调用的函数。而自动生成的函数对内置类型的成员不会处理(有些高版本编译器会…

FreeRTOS 入门 知识

什么是FreeRTOS FreeRTOS 是一个轻量级的实时操作系统&#xff08;RTOS&#xff09;&#xff0c;由 Richard Barry 在 2003 年开发&#xff0c;并且由亚马逊的 FreeRTOS 项目&#xff08;一个由 Amazon Web Services (AWS) 支持的开源项目&#xff09;进一步推动和发展。FreeR…

python开发遇到的坑汇总

文章目录 1.点击导入操作&#xff0c;所有配置全没了 1.点击导入操作&#xff0c;所有配置全没了 在 PyCharm 中&#xff0c;如果你遇到了点击导入&#xff08;import&#xff09;操作后&#xff0c;项目似乎进行了重新安装或重新部署的情况&#xff0c;这通常不是由简单的导入…

在Linux上设置MySQL允许远程连接的完整指南

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…