CGAL-几何对象基础判断-点线段使用-hello world

news2025/2/23 23:52:13

文章目录

    • 1.概述
    • 2.点和线段
    • 3.点序列的凸包
      • 3.1.内置数组中点的凸包
      • 3.2.向量中点的凸包
    • 4.关于kernel和Traits类
    • 5.概念和模型

1.概述

本教程是为CGAL新手,大概知道c++和几何算法的基本知识。第一部分展示了如何定义点和段类,以及如何在它们上应用几何谓词。本节进一步提高了对使用浮点数作为坐标时存在严重问题的认识。在第二部分中,您将遇到一个典型的CGAL函数,它计算一个2D凸包。第三部分展示了trait类的含义,第四部分解释了概念和模型的概念。

2.点和线段

在第一个例子中,我们演示了如何构造一些点和段,并对它们执行一些基本操作。所有的CGAL头文件都在子目录include/CGAL中。所有的CGAL类和函数都在名称空间CGAL中。类以大写字母开头,全局函数以小写字母开头,常量都是大写字母。对象的维度用后缀表示。几何原语,如点类型,是在核中定义的。我们为第一个示例选择的内核使用双精度浮点数表示点的笛卡尔坐标。除了这些类型,我们还看到了像三个点的方向测试这样的谓词,以及像距离和中点计算这样的结构。谓词有一组离散的可能结果,而构造要么产生一个数字,要么产生另一个几何实体。

#include <iostream>
#include <CGAL/Simple_cartesian.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_2 Point_2;
typedef Kernel::Segment_2 Segment_2;

int main()
{
	Point_2 p(1, 1), q(10, 10);
	std::cout << "p = " << p << std::endl;//点对象可直接输出
	std::cout << "q = " << q.x() << " " << q.y() << std::endl;//也可以分开输出
	std::cout << "sqdist(p,q) = "
		<< CGAL::squared_distance(p, q) << std::endl;//两个几何对象相距最近点的平方和距离
		
	Segment_2 s(p, q);//构造线段
	Point_2 m(5, 9);
	std::cout << "m = " << m << std::endl;
	std::cout << "sqdist(Segment_2(p,q), m) = "
		<< CGAL::squared_distance(s, m) << std::endl;//两个几何对象相距最近点的平方和距离
	std::cout << "p, q, and m ";
	switch (CGAL::orientation(p, q, m)) {//
	case CGAL::COLLINEAR:
		std::cout << "are collinear\n";
		break;
	case CGAL::LEFT_TURN:
		std::cout << "make a left turn\n";
		break;
	case CGAL::RIGHT_TURN:
		std::cout << "make a right turn\n";
		break;
	}
	std::cout << " midpoint(p,q) = " << CGAL::midpoint(p, q) << std::endl;//中点
	return 0;
}

/*
squared_distance:计算两个几何物体之间欧几里得距离的平方。对于任意几何对象obj1和obj2,距离的平方定义为最小的squared_distance(p1, p2),其中p1是obj1的一个点,p2是obj2的一个点。请注意,对于具有内部(有界区域)的对象,该内部是对象的一部分。因此,到内部点的距离的平方是零,而不是到边界上最近点的距离的平方。
*/

用浮点数进行几何运算可能令人惊讶,如下面的示例所示。

#include <iostream>
#include <CGAL/Simple_cartesian.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_2 Point_2;
int main()
{
	{
		Point_2 p(0, 0.3), q(1, 0.6), r(2, 0.9);
		std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
	}
	{
		Point_2 p(0, 1.0 / 3.0), q(1, 2.0 / 3.0), r(2, 1);
		std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
	}
	{
		Point_2 p(0, 0), q(1, 1), r(2, 2);
		std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
	}
	return 0;
}

在这里插入图片描述
阅读代码,我们可以假设它会打印三次“共线”。然而,实际输出如下:

not collinear
not collinear
collinear

这是因为这些分数不能表示为双精度数,而共线性检验将在内部计算一个3x3矩阵的行列式,该行列式接近但不等于零,因此前两个检验的非共线性。类似的情况也可能发生在执行左边的点上,但由于行列式计算期间的舍入误差,这些点似乎是共线的,或者执行右边。如果您必须确保您的数字得到完全精确的解释,您可以使用执行精确谓词和提取结构的CGAL内核。

#include <iostream>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <sstream>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;//使用精确内核
typedef Kernel::Point_2 Point_2;
int main()
{
	Point_2 p(0, 0.3), q, r(2, 0.9);
	{
		q = Point_2(1, 0.6);//使用双精度也不一定共线
		std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
	}
	{
		std::istringstream input("0 0.3   1 0.6   2 0.9");//使用字符串完整的描述数字
		input >> p >> q >> r;
		std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
	}
	{
		q = CGAL::midpoint(p, r);
		std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
	}
	return 0;
}

下面是输出,您可能仍然会感到惊讶。

not collinear
collinear
collinear

在第一个块中,这些点仍然不是共线的,原因很简单,您看到的文本坐标被转换为浮点数。当它们被转换成任意精度理数时,它们精确地表示浮点数,而不是文本!这在第二个块中是不同的,它对应于从文件中读取数字。然后直接从字符串构造任意精度的理数,以便它们精确地表示文本。在第三块代码中,可以看到作为中点结构的结构是精确的,正如内核类型的名称所暗示的那样。在许多情况下,您将使用“精确”的浮点数,因为它们是由某些应用程序计算或从传感器获得的。它们不是字符串“0.1”或动态计算为“1.0/10.0”,而是一个全精度浮点数。如果它们被输入到没有构造的算法中,则可以使用提供精确谓词但不精确构造的内核。一个这样的例子是凸包算法,我们将在下一节看到。输出是输入的子集,算法只比较坐标并执行方向测试。乍一看,内核执行精确的谓词和构造似乎是完美的选择,但性能要求或有限的内存资源使得它不是。此外,对于许多算法来说,精确构造是无关紧要的。例如,一种表面网格简化算法,通过将其折叠到边缘的中点来迭代地收缩边缘。大多数CGAL包都会解释它们应该使用或支持哪种内核。

3.点序列的凸包

本节中的所有示例都计算一组点的二维凸包。我们展示了算法将其输入作为表示一系列点的begin/end迭代器对,并将结果(在本例中是凸包上的点)写入输出迭代器。

3.1.内置数组中点的凸包

在第一个例子中,我们有一个包含五个点的数组作为输入。由于这些点的凸包是输入的子集,因此提供一个具有相同大小的数组来存储结果是安全的。

#include <iostream>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/convex_hull_2.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
int main()
{
	Point_2 points[5] = { Point_2(0,0), Point_2(10,0), Point_2(10,10), Point_2(6,5), Point_2(4,1) };
	Point_2 result[5];
	Point_2 *ptr = CGAL::convex_hull_2(points, points + 5, result);
	std::cout << ptr - result << " points on the convex hull:" << std::endl;
	for (int i = 0; i < ptr - result; i++) {
		std::cout << result[i] << std::endl;
	}
	return 0;
}

在前一节中,我们已经看到CGAL附带了几个内核。由于凸包算法只比较输入点的坐标和方向测试,我们可以选择提供精确谓词但不提供精确几何结构的核。凸包函数接受三个参数,作为输入的开始和结束指针,以及作为结果的数组的开始指针。该函数返回指向最后写入的凸包点后面的结果数组的指针,因此指针差告诉我们凸包上有多少个点。

3.2.向量中点的凸包

在第二个示例中,将内置数组替换为标准模板库的std::vector。back_inserter用于在末尾插入元素。实现方法是构造一个迭代器,这个迭代器可以在容器末尾添加元素。

#include <iostream>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/convex_hull_2.h>
#include <vector>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
typedef std::vector<Point_2> Points;
int main()
{
	Points points, result;
	points.push_back(Point_2(0, 0));
	points.push_back(Point_2(10, 0));
	points.push_back(Point_2(10, 10));
	points.push_back(Point_2(6, 5));
	points.push_back(Point_2(4, 1));
	CGAL::convex_hull_2(points.begin(), points.end(), std::back_inserter(result));//尾部迭代器
	std::cout << result.size() << " points on the convex hull" << std::endl;
	return 0;
}

通过调用std::vector类的push_back()方法,将一些点放入vector中。
然后调用凸包函数。前两个参数,point .begin()和points.end()是迭代器,它们是指针的泛化:它们可以解引用和自增。从某种意义上说,凸包函数是通用的,它接受任何可以解引用和递增的输入。
第三个参数是将结果写入的位置。在前面的例子中,我们提供了一个指向已分配内存的指针。这种指针的泛化是输出迭代器,它允许对解引用迭代器进行自增和赋值。在这个例子中,我们从一个空向量开始,它会根据需要增长。因此,不能简单地传递result.begin(),而是传递一个由辅助函数std::back_inserter(result)生成的输出迭代器。此输出迭代器在递增时不做任何操作,并对赋值操作调用result.push_back(…)。
如果你知道STL,标准模板库,上面的内容就很有意义了,因为这就是STL从容器中解耦算法的方式。如果您不了解STL,那么最好先熟悉一下它的基本思想。

4.关于kernel和Traits类

在本节中,我们将展示如何表达必须满足的需求,以便像convex_hull_2()这样的函数可以与任意点类型一起使用。
如果您查看函数convex_hull_2()和其他2D凸包算法的手册页,您会发现它们有两个版本。在我们目前看到的示例中,该函数使用两个迭代器来获取输入点的范围,并使用一个输出迭代器来将结果写入。第二个版本有一个额外的模板参数Traits,以及这个类型的额外参数。
C++ 的 traits 技术,是一种约定俗称的技术方案,用来为同一类数据(包括自定义数据类型和内置数据类型)提供统一的操作函数,例如 advance(), swap(), encode()/decode() 等。

template<class InputIterator , class OutputIterator , class Traits >
OutputIterator
convex_hull_2(InputIterator first,
              InputIterator beyond,
              OutputIterator result,
              const Traits & ch_traits)

典型凸包算法使用的几何基元是什么?当然,这取决于算法,所以让我们考虑一下可能是最简单有效的算法,即所谓的“Graham/Andrew Scan”。该算法首先从左到右对点进行排序,然后通过从排序列表中一个接一个地添加点来增量地构建凸包。要做到这一点,它至少必须知道一些点的类型,它应该知道如何对这些点进行排序,它必须能够计算出三个点的方向。
这就是模板参数Traits的用武之地。对于ch_graham_andrew(),它必须提供以下嵌套类型:

  • Traits::Point_2
  • Traits::Less_xy_2
  • Traits::Left_turn_2
  • Traits::Equal_2

可以猜到,Left_turn_2负责方向测试,而Less_xy_2用于对点进行排序。这些类型必须满足的需求在ConvexHullTraits_2概念中被完整地记录下来。这些类型重新分组的原因很简单。另一种选择是使用相当长的函数模板,以及更长的函数调用。

template <class InputIterator, class OutputIterator, class Point_2, class Less_xy_2, class Left_turn_2, class Equal_2>
OutputIterator
ch_graham_andrew( InputIterator  first,
                  InputIterator  beyond,
                  OutputIterator result);

有两个明显的问题:什么可以用作这个模板形参的实参?我们为什么要有模板参数呢?
要回答第一个问题,CGAL概念内核的任何模型都提供了ConvexHullTraits_2概念所需的内容。
至于第二个问题,考虑一个应用程序,我们想要计算投影到yz平面上的3D点的凸壳。使用Projection_traits_yz_3类,这是对前面示例的一个小修改。

#include <iostream>
#include <iterator>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Projection_traits_yz_3.h>
#include <CGAL/convex_hull_2.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K3;
typedef CGAL::Projection_traits_yz_3<K3> K;
typedef K::Point_2 Point_2;
int main()
{
	std::istream_iterator< Point_2 >  input_begin(std::cin);
	std::istream_iterator< Point_2 >  input_end;
	std::ostream_iterator< Point_2 >  output(std::cout, "\n");
	CGAL::convex_hull_2(input_begin, input_end, output, K());
	return 0;
}

另一个例子是关于用户定义的点类型,或者来自CGAL以外的第三方库的点类型。在类的作用域中,将点类型与此点类型所需的谓词放在一起,就可以使用这些点运行convex_hull_2()。
最后,让我们解释一下为什么性状对象被传递给凸包函数?它将允许使用更一般的投影特征对象来存储状态,例如,如果投影平面是由一个方向给出的,这是硬连接在类Projection_traits_yz_3中。

5.概念和模型

在上一节中,我们写道:CGAL概念内核的任何模型都提供了ConvexHullTraits_2概念所需的内容。概念是对类型的一组要求,也就是说,它具有某些嵌套类型、某些成员函数,或者带有以该类型为对象的某些自由函数。概念的模型是满足概念需求的类。让我们看一下下面的函数。

template <typename T>
T duplicate(T t)
{
  return t;
}

如果你想用类C实例化这个函数,这个类必须至少提供一个复制构造函数,我们说类C必须是CopyConstructible的一个模型。单例类不满足这个要求。
另一个例子是函数:

template <typename T>
T& std::min(const T& a, const T& b)
{
  return (a<b)?a:b;
}

只有为用作T的类型定义了操作符<(…)时,该函数才会编译,并且我们说该类型必须是LessThanComparable的模型。
具有所需自由函数的概念的一个例子是CGAL包CGAL和Boost Graph Library中的HalfedgeListGraph。为了成为类G的HalfedgeListGraph的模型,必须有一个全局函数halfedges(const G&),等等。
一个带有必需特征类的概念的例子是InputIterator。对于InputIterator的模型,必须存在类std::iterator_traits的特化(或者必须适用泛型模板)。

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

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

相关文章

环球数科、BUFFALO面试(部分)

环球数科 系统复杂且需求迭代频繁&#xff0c;如何维护微服务之间的接口调用关系&#xff1f; API接口在设计的时候需要大量的需求文档&#xff0c;而且文档也需要不断维护。如何高效维护API文档就很重要了。以下是一些常见的API管理工具&#xff1a;Swagger&#xff1a;Swag…

云主机OOM宕机原因分级及处理

一、故障现象 某次服务器告警宕机故障&#xff0c;无法ssh连入&#xff0c;控制台登录后查看&#xff0c;发生OOM事件&#xff0c;OOM就是我们常说的Out of Memory内存溢出&#xff0c;它是指需要的内存空间大于系统分配的内存空间&#xff0c;导致项目程序crash&#xff0c;甚…

【MySQL】使用C/C++连接MySQL数据库

【MySQL】使用C/C连接MySQL数据库 验证使用select特殊点 本文目的&#xff1a;使用MySQL提供的CAPI完成对数据库的操作 验证 #include <iostream> #include <mysql/mysql.h>int main() {std::cout<<"mysql cilent version: "<<mysql_get_cl…

面试热题100(二叉树的右视图)

给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 树这类问题用的最多的就是递归&#xff0c;因为树具有天然的递归结构&#xff1a; 我们来分析一下题目&#xff0c;给定一棵树根结…

回归预测 | MATLAB实现SO-CNN-GRU蛇群算法优化卷积门控循环单元多输入单输出回归预测

回归预测 | MATLAB实现SO-CNN-GRU蛇群算法优化卷积门控循环单元多输入单输出回归预测 目录 回归预测 | MATLAB实现SO-CNN-GRU蛇群算法优化卷积门控循环单元多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现SO-CNN-GRU蛇群算法优化卷积门控循…

nvm安装和使用

公司不同系统用的node版本不一样&#xff0c;所以就需要安装多版本了&#xff0c;那么使用nvm来管理就大大方便了开发。 使用nvm有哪些好处呢 安装node很方便&#xff0c;只需要一条命令可以轻松切换node版本可以多版本node并存 需要注意的是安装之前先把原有的node给卸载掉…

24考研数据结构-线索二叉树的线索化

目录 数据结构&#xff1a;线索二叉树与线索化线索二叉树的定义线索化过程线索化的应用总结 5.3.2线索二叉树1. 线索二叉树的概念与作用2.线索二叉树的存储结构3. 二叉树的线索化1. 中序线索化2. 先序线索化3. 后序线索化 4. 线索树的寻找前驱后继的各种情况&#xff08;多理解…

向“数”而“深”,联想凌拓的“破局求变”底气何来?

前言&#xff1a;要赢得更多机遇&#xff0c;“破局求变”尤为重要。 【全球存储观察 &#xff5c; 热点关注】2019年2月25日&#xff0c;承袭联想集团与NetApp的“双基因”&#xff0c;联想凌拓正式成立。历经四年多的发展&#xff0c;联想凌拓已成为中国企业级数据管理领域的…

opencv-29 Otsu 处理(图像分割)

Otsu 处理 Otsu 处理是一种用于图像分割的方法&#xff0c;旨在自动找到一个阈值&#xff0c;将图像分成两个类别&#xff1a;前景和背景。这种方法最初由日本学者大津展之&#xff08;Nobuyuki Otsu&#xff09;在 1979 年提出 在 Otsu 处理中&#xff0c;我们通过最小化类别内…

C语言-------函数栈帧的创建和销毁------剖析描骨

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; &#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382;…

Talk | 新加坡国立大学博士生施宇钧:DragDiffusion-基于扩散模型的关键点拖拽图片编辑

本期为TechBeat人工智能社区第518期线上Talk&#xff01; 北京时间8月2日(周三)20:00&#xff0c; 新加坡国立大学博士生—施宇钧的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “DragDiffusion-基于扩散模型的关键点拖拽图片编辑”&#xff0c;他…

浅谈机器视觉

目录 1.什么是机器视觉 2.学习机器视觉需要掌握的知识 3.机器视觉的由来 4.机器视觉带来的福利 1.什么是机器视觉 机器视觉&#xff08;Computer Vision&#xff09;是人工智能领域中的一个分支&#xff0c;旨在通过模仿人类的视觉系统&#xff0c;使计算机能够理解和解释图…

使用uni-app的uniCloud 云数据库入门:实现一个简单的增删改查

官方云数据库文档 前置步骤使用uni-app新建一个uniCloud项目 [外链图片转存失败,源站可能有防盗官方云数据库文档]!链机制,建议将()https://uniapp.dcloud.net.cn/uniCloud/hellodb.html)] 新建表 这里我加了几个测试字段 createTime、remark、money // 文档教程: https://un…

深度强化实车部署教程

强化學習仿真實車部署 前言 这里讲一下如何部署 有两种方式部署&#xff1a; 第一种实车远程控制&#xff1a;即通过roscore中的IP设置实现远程控制&#xff1b;具体可以参考turtlebot3的PC连接turtlebot3并控制的教程&#xff1b;我使用的是这种方法&#xff1b; 第二种直…

一条命令重启supervisor所有RUNNING状态的进程

supervisorctl status | grep RUNNING | awk {print $1} | xargs -n1 supervisorctl restart

选择适合的项目管理系统,了解有哪些选择和推荐

随着科技的进步和全球竞争的加剧&#xff0c;项目管理已经成为企业成功的关键要素。为了更好地组织和监控项目&#xff0c;许多企业和组织正在采用项目管理系统(PMS)。本文将探讨项目管理系统的主要组成部分以及其在实际应用中的优势。 “项目管理系统有哪些?国际上比较常见的…

GCC版本升高到11.3后编译之前同样的C++代码出现的若干错误

目录 1 gtest-death-test.cc:1301:24: error: ‘dummy’ may be used uninitialized 2 error: ‘void* memcpy(void*, const void*, size_t)’ copying an object of non-trivial type ‘Eigen::internal::Packet4c’ 3 error: comparison is always true due to limited ra…

京东开源的、高效的企业级表格可视化搭建解决方案:DripTable

DripTable 是京东零售推出的一款用于企业级中后台的动态列表解决方案&#xff0c;项目基于 React 和 JSON Schema&#xff0c;旨在通过简单配置快速生成页面动态列表来降低列表开发难度、提高工作效率。 DripTable 目前包含以下子项目&#xff1a;drip-table、drip-table-gene…

JDK8:Stream流0基础使用与深入理解,Stream流源码分析

文章目录 一、概述二、集合操作演进对比1、JDK7传统方式2、JDK8 使用Stream3、小结 三、流实现思想1、外部迭代2、内部迭代 四、函数式编程五、流操作详解1、流的分类&#xff08;1&#xff09;中间操作&#xff08;2&#xff09;终止操作 2、构建流&#xff08;1&#xff09;基…

元素内容必须由格式正确的字符数据或标记组成

mybatis报错&#xff1a; 元素内容必须由格式正确的字符数据或标记组成 代码&#xff1a; 原因分析&#xff1a; 经过查找&#xff0c;使用 解决方案&#xff1a; 使用<![CDATA[ SQL语句 ]]> 将含有<、>、<、>的sql语句包含进去。 第二种方法&#…