Delaunay 三角化 学习3
简介
还是太菜从解析官方代码开始。
官方代码共有三个核心函数。
参考链接
http://blog.sina.com.cn/s/blog_6029f0330101irlh.html
http://paulbourke.net/papers/triangulate/triangulate.c 官方代码 ?
https://www.geeksforgeeks.org/equation-of-circle-when-three-points-on-the-circle-are-given/ 求圆心坐标的代码
首先关于二维的知道三点如何求解圆心
设定方程为 \(A(x^2 + y^2) + Dx + Ey + F = 0\)
如果知道不在一条直线上的三点\((x_1,y_1),(x_2,y_2),(x_3,y_3)\),那么我们可以使用克拉默法则来求解联立方程组
A\left(x^{2}+y^{2}\right)+D x+E y+F=0 \\
A\left(x_{1}^{2}+y_{1}^{2}\right)+D x_{1}+E y_{1}+F=0 \\
A\left(x_{2}^{2}+y_{2}^{2}\right)+D x_{2}+E y_{2}+F=0 \\
A\left(x_{3}^{2}+y_{3}^{2}\right)+D x_{3}+E y_{3}+F=0
\end{array}\right.
\]
则个构成关于A,D,E,F的四元线性方程组,有非零解的充要条件是系数行列式的值为0
x^{2}+y^{2} & x & y & 1 \\
x_{1}^{2}+y_{1}^{2} & x_{1} & y_{1} & 1 \\
x_{2}^{2}+y_{2}^{2} & x_{2} & y_{2} & 1 \\
x_{3}^{2}+y_{3}^{2} & x_{3} & y_{3} & 1
\end{array}\right|=0
\]
(至于为什么,有人知道可以发在评论区里面,我忘了)。
师弟的解答:如果不为0的话那么ADEF都为0一定是其中的解,那么就没有意义了。
当任意点(x,y)与已知三点的某一点重合和,则有两行完全相同行列式为0,表明已知三点满足这个方程。如果这样感觉应该有无穷多个解,四个未知量,只有三个方程组。
若将系数行列式按照第一行展开可得
x_{1} & y_{1} & 1 \\
x_{2} & y_{2} & 1 \\
x_{3} & y_{3} & 1
\end{array}\right|\left(x^{2}+y^{2}\right)-\left|\begin{array}{lll}
x_{1}^{2}+y_{1}^{2} & y_{1} & 1 \\
x_{2}^{2}+y_{2}^{2} & y_{2} & 1 \\
x_{3}^{2}+y_{3}^{2} & y_{3} & 1
\end{array}\right| x+\left|\begin{array}{lll}
x_{1}^{2}+y_{1}^{2} & x_{1} & 1 \\
x_{2}^{2}+y_{2}^{2} & x_{2} & 1 \\
x_{3}^{2}+y_{3}^{2} & x_{3} & 1
\end{array}\right| y-\left|\begin{array}{lll}
x_{1}^{2}+y_{1}^{2} & x_{1} & y_{1} \\
x_{2}^{2}+y_{2}^{2} & x_{2} & y_{2} \\
x_{3}^{2}+y_{3}^{2} & x_{3} & y_{3}
\end{array}\right|=0
\]
四个系数行列式依次对应着A,-D,E,-F的值。最特别的是三个方程求解出了四个未知量?其实不是的ADEF是呈现一个内在的比例的
x_{1} & y_{1} & 1 \\
x_{2} & y_{2} & 1 \\
x_{3} & y_{3} & 1
\end{array}\right| \neq 0
\]
对应着三点不共线。
QU
- 太神奇了为啥三个函数可以解除四个未知量。
最特别的是三个方程求解出了四个未知量?其实不是的ADEF是呈现一个内在的比例的关系。
那如何求解圆心坐标和半径呢?
第一个看的明白点,参考链接 https://www.geeksforgeeks.org/equation-of-circle-when-three-points-on-the-circle-are-given/
code
hh
/*
* @Author: your name
* @Date: 2020-12-08 15:18:17
* @LastEditTime: 2020-12-10 13:20:21
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \delaunay\cpp\cpp\Delaunay.h
*/
#ifndef Delaunay_H
#define Delaunay_H
#include <stdint.h>
#include <iostream>
#include <stdlib.h> // for C qsort
#include <cmath>
#include <time.h> // for random
#include <fstream>
#include <vector>
#include <algorithm>
const int MaxVertices = 500;
const int MaxTriangles = 1000;
const double EPSILON = 0.000001;
struct ITRIANGLE
{
int p1, p2, p3;
};
struct IEDGE
{
int p1, p2;
};
struct XYZ
{
double x, y, z;
};
//int XYZCompare(const void *v1, const void *v2);
int Triangulate(int nv, XYZ pxyz[], ITRIANGLE v[], int &ntri);
bool CircumCircle(double, double, double, double, double, double, double,
double, double &, double &, double &);
bool genPoints(std::vector<XYZ> &points, int numPoints);
void findCircle(double x1, double y1, double x2, double y2, double x3, double y3, double &xc, double &yc, double &r);
#endif
cc
#include "Delaunay.hh"
using namespace std;
// Function to find the circle on
// which the given three points lie
void findCircle(double x1, double y1, double x2, double y2, double x3, double y3, double &xc, double &yc, double &r)
{
double x12 = x1 - x2;
double x13 = x1 - x3;
double y12 = y1 - y2;
double y13 = y1 - y3;
double y31 = y3 - y1;
double y21 = y2 - y1;
double x31 = x3 - x1;
double x21 = x2 - x1;
// x1^2 - x3^2
double sx13 = pow(x1, 2) - pow(x3, 2);
// y1^2 - y3^2
double sy13 = pow(y1, 2) - pow(y3, 2);
double sx21 = pow(x2, 2) - pow(x1, 2);
double sy21 = pow(y2, 2) - pow(y1, 2);
double f = ((sx13) * (x12)
+ (sy13) * (x12)
+ (sx21) * (x13)
+ (sy21) * (x13))
/ (2 * ((y31) * (x12) - (y21) * (x13)));
double g = ((sx13) * (y12)
+ (sy13) * (y12)
+ (sx21) * (y13)
+ (sy21) * (y13))
/ (2 * ((x31) * (y12) - (x21) * (y13)));
double c = -pow(x1, 2) - pow(y1, 2) - 2 * g * x1 - 2 * f * y1;
// eqn of circle be x^2 + y^2 + 2*g*x + 2*f*y + c = 0
// where centre is (h = -g, k = -f) and radius r
// as r^2 = h^2 + k^2 - c
double h = -g;
double k = -f;
double sqr_of_r = h * h + k * k - c;
// r is the radius
r = sqrt(sqr_of_r);
cout << "Centre = (" << h << ", " << k << ")" << endl;
xc = h;
yc = k;
cout << "Radius = " << r << endl;
}
////////////////////////////////////////////////////////////////////////
// CircumCircle() :
// Return true if a point (xp,yp) is inside the circumcircle made up
// of the points (x1,y1), (x2,y2), (x3,y3)
// The circumcircle centre is returned in (xc,yc) and the radius r
// Note : A point on the edge is inside the circumcircle
////////////////////////////////////////////////////////////////////////
bool CircumCircle(double xp, double yp, double x1, double y1, double x2,
double y2, double x3, double y3, double &xc, double &yc, double &r)
{
double m1, m2, mx1, mx2, my1, my2;
double dx, dy, rsqr, drsqr;
/* Check for coincident points 防止三点共线 */
if (abs(y1 - y2) < EPSILON && abs(y2 - y3) < EPSILON){
return false;
}
// if (abs(x1 - x2) < EPSILON && abs(x2 - x3) < EPSILON){
// return false;
// }
findCircle(x1, y1, x2, y2, x3, y3, xc, yc, r);
dx = x2 - xc;
dy = y2 - yc;
rsqr = dx * dx + dy * dy;
dx = xp - xc;
dy = yp - yc;
drsqr = dx * dx + dy * dy;
return ((drsqr <= rsqr) ? true : false);
}
///////////////////////////////////////////////////////////////////////////////
// Triangulate() :
// Triangulation subroutine
// Takes as input NV vertices in array pxyz
// Returned is a list of ntri triangular faces in the array v
// These triangles are arranged in a consistent clockwise order.
// The triangle array 'v' should be malloced to 3 * nv
// The vertex array pxyz must be big enough to hold 3 more points
// The vertex array must be sorted in increasing x values say
//
// qsort(p,nv,sizeof(XYZ),XYZCompare);
///////////////////////////////////////////////////////////////////////////////
/**
* @description: v存储点的序列,每三个构成一个三角形
* @param {*}
* @return {*}
*/
int Triangulate(std::vector<XYZ> &pxyz, std::vector<ITRIANGLE> &v)
{
std::vector<int> complete;
std::vector<IEDGE> edges;
double xmin, ymin, xmax, ymax;
/*
Find the maximum and minimum vertex bounds.
This is to allow calculation of the bounding triangle
*/
int nv = pxyz.size();
xmin = pxyz[0].x;
ymin = pxyz[0].y;
xmax = xmin;
ymax = ymin;
for (int i = 1; i < pxyz.size(); i++)
{
if (pxyz[i].x < xmin)
xmin = pxyz[i].x;
if (pxyz[i].x > xmax)
xmax = pxyz[i].x;
if (pxyz[i].y < ymin)
ymin = pxyz[i].y;
if (pxyz[i].y > ymax)
ymax = pxyz[i].y;
}
double dx, dy, dmax, xmid, ymid;
dx = xmax - xmin;
dy = ymax - ymin;
dmax = (dx > dy) ? dx : dy;
xmid = (xmax + xmin) / 2.0;
ymid = (ymax + ymin) / 2.0;
/*
构建一个超级三角形包含所有的点.
超级三角形的点位于所有点的末尾。
超级三角形式三角形列表的第一个单位
*/
XYZ tmp;
tmp.x = xmid - 20 * dmax;;
tmp.y = ymid - dmax;
pxyz.push_back(tmp);
tmp.x = xmid;
tmp.y = ymid + 20 * dmax;;
pxyz.push_back(tmp);
tmp.x = xmid + 20 * dmax;
tmp.y = ymid - dmax;;
pxyz.push_back(tmp);
ITRIANGLE itmp;
itmp.p3 = pxyz.size() - 1;
itmp.p2 = pxyz.size() - 2;
itmp.p1 = pxyz.size() - 3;
v.push_back(itmp);
complete.push_back(false);
/*
加入现有顶点
*/
double xp = 0;
double yp = 0;
int ntri = 1;
for (int i = 0; i < nv; i++)
{
xp = pxyz[i].x;
yp = pxyz[i].y;
/*
设定边,如果顶点在圆的内部然后三角形的三个边加入边的buffer然后删除三角形
*/
double x1, y1, x2, y2, x3, y3, xc, yc, r;
bool inside = false;
edges.clear();
for (int j = 0; j < v.size(); j++)
{
if (complete[j])
continue;
x1 = pxyz[v[j].p1].x;
y1 = pxyz[v[j].p1].y;
x2 = pxyz[v[j].p2].x;
y2 = pxyz[v[j].p2].y;
x3 = pxyz[v[j].p3].x;
y3 = pxyz[v[j].p3].y;
inside = CircumCircle(xp, yp, x1, y1, x2, y2, x3, y3, xc, yc, r);
if (xc + r < xp)
// Suggested
// if (xc + r + EPSILON < xp)
complete[j] = true; //
if (inside)
{
IEDGE etmp;
etmp.p1 = v[j].p1;
etmp.p2 = v[j].p2;
edges.push_back(etmp);
etmp.p1 = v[j].p2;
etmp.p2 = v[j].p3;
edges.push_back(etmp);
etmp.p1 = v[j].p3;
etmp.p2 = v[j].p1;
edges.push_back(etmp);
v[j] = v[v.size() - 1];
v.pop_back();// 弹出最后一个算是删除了
complete[j] = complete[complete.size() - 1];
complete.pop_back();
j--;
}
}
/*
删除所有的包围中心边。得到一个周围边
Note: if all triangles are specified anticlockwise then all
interior edges are opposite pointing in direction.
*/
for (int j = 0; j < edges.size() - 1; j++)
{
for (int k = j + 1; k < edges.size(); k++)
{
if ((edges[j].p1 == edges[k].p2) && (edges[j].p2 == edges[k].p1))
{
edges[j].p1 = -1;
edges[j].p2 = -1;
edges[k].p1 = -1;
edges[k].p2 = -1;
}
/* Shouldn't need the following, see note above */
if ((edges[j].p1 == edges[k].p1) && (edges[j].p2 == edges[k].p2))
{
edges[j].p1 = -1;
edges[j].p2 = -1;
edges[k].p1 = -1;
edges[k].p2 = -1;
}
}
}
/*
Form new triangles for the current point
Skipping over any tagged edges.
All edges are arranged in clockwise order.
*/
for (int j = 0; j < edges.size(); j++)
{
if (edges[j].p1 < 0 || edges[j].p2 < 0)
continue;
ITRIANGLE tmp;
tmp.p1 = edges[j].p1;
tmp.p2 = edges[j].p2;
tmp.p3 = i;
v.push_back(tmp);
complete.push_back(false);
}
}
/*
Remove triangles with supertriangle vertices
These are triangles which have a vertex number greater than nv
*/
for (int i = 0; i < v.size(); i++)
{
if (v[i].p1 >= nv || v[i].p2 >= nv || v[i].p3 >= nv)
{
v[i] = v[v.size() - 1];
v.pop_back();
i--;
}
}
pxyz.pop_back();
pxyz.pop_back();
pxyz.pop_back();
return 0;
}
void randomize()
{
srand((time_t)time(NULL));
}
int random(int n)
{
return rand() % n;
}
bool cmp(XYZ &a, XYZ &b){
if(a.x < b.x){
return true;
}else{
return false;
}
}
void outputtriangle(std::vector<XYZ> &pxyz, std::vector<ITRIANGLE> &v)
{
std::ofstream off("triangle.off", std::ios::out);
if (!off.good())
{
std::cerr << "Error: Could not open file "
<< "triangle.off"
<< " for writing!" << std::endl;
off.close();
return;
}
// write header
off << "OFF" << std::endl;
off << pxyz.size() << " " << v.size() << " 0" << std::endl;
uint64_t n_vertices(pxyz.size());
// write vertices
for (u_int64_t v_it = 0; v_it < n_vertices; ++v_it)
{
off << pxyz[v_it].x << " " << pxyz[v_it].y << " " << pxyz[v_it].z << "\n";
}
// 清楚点数模式
off.unsetf(std::ios::fixed);
uint64_t n_faces(v.size());
for (uint64_t c_it = 0; c_it < n_faces; ++c_it)
{
off << "3 ";
off << v[c_it].p1 << " ";
off << v[c_it].p2 << " ";
off << v[c_it].p3 << " ";
off << "\n";
}
off << "End\n";
off.close();
return;
}
/**
* @description: 生成随机的点
* @param {*}
* @return {*}
*/
bool genPoints(std::vector<XYZ> &points, int numPoints){
int nv = 0;
bool b_Ok;
double x, y;
while(nv != numPoints){
do{
b_Ok = true;
x = (double)random(500);
y = (double)random(500);
for (int n_Cpt = 0; n_Cpt < points.size(); n_Cpt++)
{
if ((x == points[n_Cpt].x) && (y == points[n_Cpt].y)){
b_Ok = false;
}
} // to avoid similar points in the array
}while (!b_Ok);
nv++;
XYZ tmp;
tmp.x = x;
tmp.y = y;
tmp.z = 0;
// 生成一个符合不重复的点
points.push_back(tmp);
}
return true;
}
int main()
{
std::vector<ITRIANGLE> vTriangle;
std::vector<XYZ> points;
double x, y, z;
randomize();
int n_MaxPoints = 10; // points size you want
genPoints(points, n_MaxPoints);
sort(points.begin(), points.end(), cmp);
Triangulate(points, vTriangle);
outputtriangle(points, vTriangle); // use this fonction to trace the mesh (via OpenGL, DirectX, ...)
return 0;
}
小感悟
命运总是曲折离奇。我这个人有点难以专心看枯燥的东西,对着屏幕,对着ipad 还好一些,因为可以写写画画。
以后还是矩阵式学习,电脑代码,ipad思想草稿。
TIPS
QU: complete 有什么用?
AN: 在执行三角化之前用排序让所有的点沿着x 方向进行了排序,随着x增长,前面已近考虑了的且一定完备的三角形可以设定为true。
QU: 算法思想是什么简述:
AN:
http://paulbourke.net/papers/triangulate/ 这篇论文讲的很详细,先读懂论文,再看代码事半功倍。先看代码在看论文事倍功半。
subroutine triangulate
input : vertex list
output : triangle list
initialize the triangle list
determine the supertriangle
add supertriangle vertices to the end of the vertex list
add the supertriangle to the triangle list
for each sample point in the vertex list
initialize the edge buffer
for each triangle currently in the triangle list
calculate the triangle circumcircle center and radius
if the point lies in the triangle circumcircle then
add the three triangle edges to the edge buffer
remove the triangle from the triangle list
endif
endfor
delete all doubly specified edges from the edge buffer
this leaves the edges of the enclosing polygon only
add to the triangle list all triangles formed between the point
and the edges of the enclosing polygon
endfor
remove any triangles from the triangle list that use the supertriangle vertices
remove the supertriangle vertices from the vertex list
end
个人总结的思想:
Step 1:构建一个超级大的三角形,可以包含所有点的那种
Step 2: 将点按照某一维度排序,可以是x也可以是y
Step 3: 有一个三角形列表,超级大的三角形式第一个元素
-----
Step 4: 核心(可以读两遍)
现在我们要开始逐步加点了。
加入一个点,判断是否在所有的三角形列表中三角形的外接圆中,如果在三角形的外接圆中,将这个三角形的边加入“边临时数组”。将此三角形从三角形列表中删去。
对边临时数组进行遍历如果有同向或者反向的边,将其删除参考图1中的红色的线,因为红色的线是两个三角形共享的,所以会被删除。这样就剩下外围的包围框了是黑色厚的线条。
让所有的在边临时数组中的边和新加入的顶点相互连接构成新的三角形,并加入到三角形数组中。
如图2所示
-----
Step 5:去除三角形列表中和超级三角形有关的三角形。
Step 6: 删除掉点数组中关于超级三角形的点。
Step 7: 输出三角化网格

图1 上图

图2 上图
image

100个随机点生成的网格。因为改写了部分代码采用C++的STL可以更方便的构建更多点的方式。

1000个随机点生成的网格。很舒服。
Delaunay 三角化 学习3的更多相关文章
- Delaunay三角化算法
参考:<平面域中的Delaunay三角算法>
- 用canvas 实现个图片三角化(LOW POLY)效果
之前无意中看到Ovilia 用threejs做了个LOW POLY,也就是图片平面三角化的效果,觉得很惊艳,然后就自己花了点时间尝试了一下. 我是没怎么用过threejs,所以就直接用canvas的2 ...
- Schur 三角化定理的推论
将学习到什么 从 Schur 的酉三角化定理可以收获一批结果,在这一部分介绍重要的几个. 迹与行列式 相似矩阵具有相同的特征多项式, 从特征多项式一节中, 我们又知道,相似矩阵的迹以及行列式都是相 ...
- PCL贪婪投影三角化算法
贪婪投影三角化算法是一种对原始点云进行快速三角化的算法,该算法假设曲面光滑,点云密度变化均匀,不能在三角化的同时对曲面进行平滑和孔洞修复. 方法: (1)将三维点通过法线投影到某一平面 (2)对投影得 ...
- 20145213《Java程序设计》第三周学习总结
20145213<Java程序设计>第三周学习总结 教材学习内容总结 正所谓距离产生美,上周我还倾心于Java表面的基础语法.其简单的流程结构,屈指可数的基本类型分类,早已烂熟于心的运算符 ...
- 20145304 Java第三周学习报告
20145304 <Java程序设计>第三周学习总结 教材学习内容总结 1.定义类: 类定义时使用class关键词,建立实例要使用new关键词. 代码如下: /*定义类 书上例子 衣服的型 ...
- 20145330《Java程序设计》第三周学习总结
20145330 <Java程序设计>第三周学习总结 第三周知识的难度已经逐步上升,并且一周学习两章学习压力也逐渐加大,需要更高效率的来完成学习内容,合理安排时间. 类与对象 对象(Obj ...
- 20145337《Java程序设计》第三周学习总结
20145337 <Java程序设计>第三周学习总结 教材学习内容总结 类与对象 类与对象的关系:要产生对象必须先定义类,类是对象的设计图,对象是类的实例.我觉得在视频中对类与对象关系的描 ...
- 20145218 《Java程序设计》第三周学习总结
20145218 <Java程序设计>第三周学习总结 教材学习内容总结 定义类 编写程序要产生对象就要先定义类.类是对象的设计图,对象是类的实例.类定义时使用class关键词,建立实例时, ...
- 《Java程序设计》第三周学习总结
20145224-陈颢文 <Java程序设计>第三周学习总结 教材学习内容总结 一.定义类: ·类定义时使用class关键字,要对类中变量(值域成员/对象数据成员)行类型声明. class ...
随机推荐
- windows下jdk版本切换(bat)
1.jdk下载 Oracle官网 https://www.oracle.com/cn/ 资源->下载->Java下载 jdk当前最新版本 jdk22版本 jdk8版本 当前页面向下拉 2. ...
- Issue: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'
问题: Fatal error compiling: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImpor ...
- Web前端入门第 42 问:聊聊 CSS 元素上下左右(水平+垂直)同时居中有几种方法
影响元素位置的 CSS 属性基本介绍完毕(参考前几篇文章),现思考一个最常见的需求: 一个子元素,要摆放在盒子的正中央,使用 CSS 布局手段,究竟有多少种实现方式? 上下左右(水平方向.垂直方向)要 ...
- UnicodeDecodeError: ‘ascii‘ codec can‘t decode byte 0xe8 in position...解决方法
运行python程序,出现了以下错误: File "C:/��/python ѧϰ/god_mellonѧϰpython/untitled2/fofa_py2.py", line ...
- AxureRP大型综合商场APP高保真交互原型图含VR试衣间停车缴费会员卡楼层导购
大型综合商场APP AxureRP高保真交互原型图含VR试衣间停车缴费会员卡楼层导购,是一款完全原创的,关于大型综合商场的综合类应用APP,app中包含在线VR试衣间.楼层导购.会员码支付.在线停车缴 ...
- 操作系统综合题之“请填写信号量值并说明操作结果(正常、阻塞或唤醒。如阻塞或者唤醒,需说明阻塞或者被唤醒的是P1还是P2)(信号量操作流程-代码补充)”
1.问题:题36表是两个同步进程的模拟执行,生产者将物品放入共享缓冲区供消费者使用,缓冲区可放2件物品,使用2个信号量,并置初值为S1 = 2,S2=0.现已知操作情况,请填写信号量值并说明操作结果( ...
- Linux 下搭建 Spark3 + Jupyter 环境
最近想着来玩一玩大数据, 前段时间集中过了一遍 java, 最近又看了一些基础的 scala, 我感觉吧, 我都不想学. 还是觉得用 sql, javascript, python 这种脚本语言操作起 ...
- C语言一点五编程实战:纯 C 的模块化×继承×多态框架
本文将大量涉及C语言高级操作,如函数指针.结构体指针.二级指针.指针频繁引用解引用.typedef.static.inline和C语言项目结构等知识,请确保自己不会被上述知识冲击,如果没有这顾虑,请尽 ...
- 解决更新WIFI驱动后出现网络适配器黄色三角警告
更新WIFI驱动后出现网络适配器黄色三角警告问题的解决方案 在更新 Intel 无线网卡驱动后,遇到了网络适配器异常的问题,尤其是在曾经安装/卸载过 VMware 的电脑上.本篇文章将详细介绍这个问题 ...
- TVM Pass优化 -- 公共子表达式消除(Common Subexpr Elimination, CSE)
定义(What) 公共子表达式消除 就是如果表达式E的值已经计算的到了,并且自计算的到值后E的值就不再改变了,就说,表达式E在后续计算中是一个公共表达式. 简单说,该表达式上面已经执行过了,下面没必要 ...