一、 Delaunay三角网
- Delaunay三角网的特性:
(1)空圆性,任一三角形外接圆内部不包含其他点。
(2)最接近:以最近临的三点形成三角形,且各线段(三角形的边)皆不相交。
(3)唯一性:不论从区域何处开始构建,最终都将得到一致的结果。
(4)最优性:任意两个相邻三角形形成的凸四边形的对角线如果可以互换的话,那么两个三角形六个内角中最小的角度不会变大。
(5)最规则:如果将三角网中的每个三角形的最小角进行升序排列,则Delaunay三角网的排列得到的数值最大。
(6)区域性:新增、删除、移动某一个顶点时只会影响临近的三角形。
(7)具有凸多边形的外壳:三角网最外层的边界形成一个凸多边形的外壳。
- 算法
Delaunay剖分是一种三角剖分的标准,实现它有多种算法。本次采用Bowyer-Watson算法,算法的基本步骤是:
(1) 构造一个超级三角形,包含所有散点,放入三角形链表。
(2)将点集中的散点依次插入,在三角形链表中找出其外接圆包含
插入点的三角形(称为该点的影响三角形),删除影响三角形的公共边,将插入点同影响三角形的全部顶点连接起来,从而完成一个点在Delaunay三角形链表中的插入。
(3)根据优化准则对局部新形成的三角形进行优化。将形成的三角形放入Delaunay三角形链表。
(4)循环执行上述第2步,直到所有散点插入完毕。
(5)删除和超级三角形有关的三角形,形成凸包。
二、Voronoi Diagram
- Voronoi图的定义
又叫泰森多边形或Dirichlet图,它是由一组由连接两邻点直线的垂直平分线组成的连续多边形组成。 - Voronoi图的特点
(1)每个V多边形内有一个生成元;
(2)每个V多边形内点到该生成元距离短于到其它生成元距离;
(3)多边形边界上的点到生成此边界的生成元距离相等;
(4)邻接图形的Voronoi多边形界线以原邻接界线作为子集。 - Voronoi的应用
在计算几何学科中的重要地位,由于其根据点集划分的区域到点的距离最近的特点,其在地理学、气象学、结晶学、航天、核物理学、机器人等领域具有广泛的应用。如在障碍物点集中,规避障碍寻找最佳路径。 - 建立Voronoi图的方法
V图有着按距离划分邻近区域的普遍特性,应用范围广。生成V图的方法很多,常见的有分治法、扫描线算法和Delaunay三角剖分算法。
本次实验采用的是Delaunay三角剖分算法。主要是指生成Voronoi图时先生成其对偶元Delaunay三角网,再找出三角网每一三角形的外接圆圆心,最后连接相邻三角形的外接圆圆心,形成以每一三角形顶点为生成元的多边形网。如下图所示(来源于网络,如有侵权,请联系删除):
建立Voronoi图算法的关键是对离散数据点合理地连成三角网,即构建Delaunay三角网。 - 建立Voronoi图的步骤
(1)离散点自动构建三角网,即构建Delaunay三角网。对离散点和形成的三角形编号,记录每个三角形是由哪三个离散点构成的。
(2)计算每个三角形的外接圆圆心,并记录之。
(3)遍历三角形链表,寻找与当前三角形pTri三边共边的相邻三角形TriA,TriB和TriC。
(4)如果找到,则把寻找到的三角形的外心与pTri的外心连接,存入维诺边链表中。如果找不到,则求出最外边的中垂线射线存入维诺边链表中。
(5)遍历结束,所有维诺边被找到,根据边画出维诺图。
生成效果图:
相关代码下载链接:(等课程作业上交完,再分享吧,如有紧急需要,请联系我)
三、代码解析
-
新建项目
新建一个MFC程序项目,项目名Voronoi2。 -
资源视图的菜单设置
Voronoi图的生成主要分三步,鼠标点击屏幕获取点,根据点构造Delaunay三角形,根据三角形构造Voronoi图。为了重置视图,还需要增加一个清除屏幕按钮。
(步骤提示:资源视图—— 在菜单栏——编辑名称——属性——设置ID 。)
然后给每个菜单添加消息处理程序。在菜单按钮名右击添加。消息【鼠标点击屏幕获取点】是用来为数据开辟空间的,响应函数在Doc里面。因为要显示视图,所以消息【根据点构造Delaunay三角形】,【根据三角形构造Voronoi图】响应函数在View类里面。
-
封装类
新建类,本程序需要用到点,边和三角形的数据结构,放在自己新建的类头文件里,自己需要的函数,则是边编程边添加。
(步骤提示:解决方案资源管理器——右击——添加类——类名CVoronoidiagram——编辑函数)
在Doc文件里,给自己新建的类实例化对象:
class CVoronoi2Doc : public CDocument
{
(省略.。。。。)
// 实现
public:
CVoronoidiagram * m_CVor;
(省略。。。。)
}
只有触发消息【鼠标点击屏幕获取点】,才可以使用类CVoronoidiagram,并为数据开辟空间。
在CPP文件里,对新建的指针对象,构造函数进行初始化,析构函数进行释放
鼠标点击屏幕,获取点,并且在屏幕显示出来。先添加一个鼠标点击屏幕的触发消息响应。我这篇文章有详细操作,可以借鉴。
https://blog.csdn.net/weixin_44210987/article/details/90679214
//鼠标点击屏幕获取点
具体代码如下
Vec.h
#ifndef VEC_H
#define VEC_H
/*
Szymon Rusinkiewicz
Princeton University
Vec.h
Class for a constant-length vector
Supports the following operations:
vec v1; // Initialized to (0,0,0)
vec v2(1,2,3); // Initialized to (1,2,3)
vec v3(v2); // Copy constructor
float farray[3];
vec v4 = vec(farray); // Explicit: "v4 = farray" won't work
Vec<3,double> vd; // The "vec" used above is Vec<3,float>
point p1, p2, p3; // Same as vec
v3 = v1 + v2; // Also -, *, / (all componentwise)
v3 = 3.5f * v1; // Also vec * scalar, vec / scalar
// NOTE: scalar has to be the same type:
// it won't work to do double * vec<float>
v1 = min(v2,v3); // Componentwise min/max
v1 = sin(v2); // Componentwise - all the usual functions...
swap(v1,v2); // In-place swap
v3 = v1 DOT v2; // Actually operator^
v3 = v1 CROSS v2; // Actually operator%
float f = v1[0]; // Subscript
float *fp = v1; // Implicit conversion to float *
f = len(v1); // Length (also len2 == squared length)
f = dist(p1, p2); // Distance (also dist2 == squared distance)
normalize(v1); // Normalize (i.e., make it unit length)
// normalize(vec(0,0,0)) => vec(1,0,0)
v1 = trinorm(p1,p2,p3); // Normal of triangle
cout << v1 << endl; // iostream output in the form (1,2,3)
cin >> v2; // iostream input using the same syntax
Also defines the utility functions sqr, cube, sgn, fract, clamp, mix,
step, smoothstep, faceforward, reflect, and refract
*/
// Windows defines these as macros, which prevents us from using the
// type-safe versions from std::, as well as interfering with method defns
#undef min
#undef max
#include <cmath>
#include <iostream>
#include <algorithm>
using std::swap;
using std::sqrt;
// Let gcc optimize conditional branches a bit better...
#ifndef likely
# if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
# define likely(x) (x)
# define unlikely(x) (x)
# else
# define likely(x) (__builtin_expect((x), 1))
# define unlikely(x) (__builtin_expect((x), 0))
# endif
#endif
// Boost-like compile-time assertion checking
template <bool X> struct VEC_STATIC_ASSERTION_FAILURE;
template <> struct VEC_STATIC_ASSERTION_FAILURE<true>
{ void operator () () {} };
#define VEC_STATIC_CHECK(expr) VEC_STATIC_ASSERTION_FAILURE<bool(expr)>()
template <int D, class T = float>
class Vec {
protected:
T v[D];
public:
// Constructor for no arguments. Everything initialized to 0.
Vec() { for (int i = 0; i < D; i++) v[i] = T(0); }
// Uninitialized constructor - meant mostly for internal use
#define VEC_UNINITIALIZED ((void *) 0)
Vec(void *) {}
// Constructors for 2-4 arguments
Vec(T x, T y)
{ VEC_STATIC_CHECK(D == 2); v[0] = x; v[1] = y; }
Vec(T x, T y, T z)
{ VEC_STATIC_CHECK(D == 3); v[0] = x; v[1] = y; v[2] = z; }
Vec(T x, T y, T z, T w)
{ VEC_STATIC_CHECK(D == 4); v[0] = x; v[1] = y; v[2] = z; v[3] = w; }
// Constructor from anything that can be accessed using []
// Pretty aggressive, so marked as explicit.
template <class S> explicit Vec(const S &x)
{ for (int i = 0; i < D; i++) v[i] = T(x[i]); }
// No destructor or assignment operator needed
// Array reference and conversion to pointer - no bounds checking
const T &operator [] (int i) const
{ return v[i]; }
T &operator [] (int i)
{ return v[i]; }
operator const T * () const
{ return v; }
operator const T * ()
{ return v; }
operator T * ()
{ return v; }
// Member operators
Vec<D,T> &operator += (const Vec<D,T> &x)
{
for (int i = 0; i < D; i++)
#pragma omp atomic
v[i] += x[i];
return *this;
}
Vec<D,T> &operator -= (const Vec<D,T> &x)
{
for (int i = 0; i < D; i++)
#pragma omp atomic
v[i] -= x[i];
return *this;
}
Vec<D,T> &operator *= (const Vec<D,T> &x)
{
for (int i = 0; i < D; i++)
#pragma omp atomic
v[i] *= x[i];
return *this;
}
Vec<D,T> &operator *= (const T &x)
{
for (int i = 0; i < D; i++)
#pragma omp atomic
v[i] *= x;
return *this;
}
Vec<D,T> &operator /= (const Vec<D,T> &x)
{
for (int i = 0; i < D; i++)
#pragma omp atomic
v[i] /= x[i];
return *this;
}
Vec<D,T> &operator /= (const T &x)
{
for (int i = 0; i < D; i++)
#pragma omp atomic
v[i] /= x;
return *this;
}
// Set each component to min/max of this and the other vector
Vec<D,T> &min(const Vec<D,T> &x)
{
#pragma omp critical
for (int i = 0; i < D; i++)
if (x[i] < v[i]) v[i] = x[i];
return *this;
}
Vec<D,T> &max(const Vec<D,T> &x)
{
#pragma omp critical
for (int i = 0; i < D; i++)
if (x[i] > v[i]) v[i] = x[i];
return *this;
}
// Outside of class: + - * / % ^ << >>
// Some partial compatibility with valarrays and vectors
typedef T value_type;
size_t size() const
{ return D; }
T sum() const
{ T total = v[0];
for (int i = 1; i < D; i++) total += v[i];
return total; }
T avg() const
{ return sum() / D; }
T product() const
{ T total = v[0];
for (int i = 1; i < D; i++) total *= v[i];
return total; }
T min() const
{ T m = v[0];
for (int i = 1; i < D; i++)
if (v[i] < m) m = v[i];
return m; }
T max() const
{ T m = v[0];
for (int i = 1; i < D; i++)
if (v[i] > m) m = v[i];
return m; }
T *begin() { return &(v[0]); }
const T *begin() const { return &(v[0]); }
T *end() { return begin() + D; }
const T *end() const { return begin() + D; }
void clear() { for (int i = 0; i < D; i++) v[i] = T(0); }
bool empty() const
{ for (int i = 0; i < D; i++)
if (v[i]) return false;
return true; }
Vec<D,T> apply(T func(T)) const
{ Vec<D,T> result(VEC_UNINITIALIZED);
for (int i = 0; i < D; i++) result[i] = func(v[i]);
return result; }
Vec<D,T> apply(T func(const T&)) const
{ Vec<D,T> result(VEC_UNINITIALIZED);
for (int i = 0; i < D; i++) result[i] = func(v[i]);
return result; }
};
typedef Vec<3,float> vec;
typedef Vec<3, float> point;
typedef Vec<2, float> point2;
typedef Vec<2, float> vec2;
typedef Vec<3,float> vec3;
typedef Vec<4,float> vec4;
typedef Vec<2,int> ivec2;
typedef Vec<3,int> ivec3;
typedef Vec<4,int> ivec4;
// Nonmember operators that take two Vecs
template <int D, class T>
static inline const Vec<D,T> operator + (const Vec<D,T> &v1, const Vec<D,T> &v2)
{
Vec<D,T> result(VEC_UNINITIALIZED);
for (int i = 0; i < D; i++)
result[i] = v1[i] + v2[i];
return result;
}
template <int D, class T>
static inline const Vec<D,T> operator - (const Vec<D,T> &v1, const Vec<D,T> &v2)
{
Vec<D,T> result(VEC_UNINITIALIZED);
for (int i = 0; i < D; i++)
result[i] = v1[i] - v2[i];
return result;
}
template <int D, class T>
static inline const Vec<D,T> operator * (const Vec&l