Laplace mesh editing

实现算法:简易的Laplace mesh editing。(2016年3月5日

$实现过程及原理

Laplace 坐标(算子)

这里的表示相对坐标,是某一点相对于其1-领域点的中心点的位置,也就是Laplace坐标。为什么这样表示呢?这样的坐标有两个特点:

1.它的方向与法相比较接近。

2.它的大小接近于曲率。

对于一些网格,我们进行拖动一些点,但是又要使得它的形状和原先的尽可能相似,怎么处理呢?假设为已知大概位置的点,其变换后的大概位置是。考虑这样的能量函数:

先来看看这个函数的意义,第一部分表示Laplace坐标变化大小,也就是原先的形状变化大小,第二部分表示对于固定点的位置变化大小。我们最小化这个能像函数就能得到一组点,既能使得形状变化小,又能使得与固定位置变化小。

$实验步骤

1.对能量函数进行求导,求其最小值相当于计算线性方程组

2.编写程序,测试。(参见附录)

3.测试结果及软件使用方法

(1)选择一些移动点

(2)计算一些必要的参数

(3)改变选中点的位置,这几个按钮都可以

(4)点击进行编辑

(5)效果如下

(6)对比原图。

$实验注意事项:

1.在对能能量函数进行求解事需要细心,转化成最小二乘法求解线性方程组时每一步都得耐心检查,否则一失足而成千古恨,以至于后面全都错。

2.在对方程组进行输入时,每进行一步都得测试,以防带入变量或者参数错误而得不到正确的结果

3.整合每一块的计算结果,需要对程序的操作流程非常熟悉,得知道第一步该干什么第二步该干什么,如果两个函数的操作顺序反了可能得不到想要的结果,比如如果先对点进行移动在计算Laplace坐标时就是不对的。

4.该清除的量必须清除,否则上一次变换的结果可能对下一次变换产生影响,结果导致只有第一次变换是对的,之后的变换全是错的。

总而言之,需要对实验的基本原理和程序的基本流程非常清楚才能进行编程,否则只会浪费更多的时间。

 

$主要程序代码:

void RenderingWidget::edit_()

{

classfly();

compute_matrix();

get_b();

solve_system();

updateGL();

triple_A.clear();

}

void RenderingWidget::compute_parameter()

{

ptr_mesh_->laplace_position();//先计算laplace坐标

}

////////////分类/////////////

void RenderingWidget::classfly()

{

int k =0;

const std::vector<HE_vert*>& vertices = *(ptr_mesh_->get_vertex_list());

for(int i = 0;i<vertices.size();i++)

{

vertices[i]->point_type = other;

}

for (int i = 0; i<vertices.size(); i++)

{

if (vertices[i]->boundary_flag_ == BOUNDARY) vertices[i]->point_type = boundary;

}

for (int i = 0; i < current_index_.size(); i++)

{

vertices[current_index_[i]]->point_type = drag;

}

}

/////////////计算各种矩阵//////////////////

void RenderingWidget::compute_matrix()

{

const std::vector<HE_vert*>& vertices = *(ptr_mesh_->get_vertex_list());

float r1 = 1.0f / vertices.size();                 //所有点系数

float r2 = 1.0f / current_index_.size();    //拖动点系数

float r3 = 0;                         //边界点系数

for (int i = 0; i < vertices.size(); i++)         //没错

{

if (vertices[i]->boundary_flag_==BOUNDARY)

{

r3++;

}

}

if (r3>0)

{

r3 = 1.0f / r3;

}

int n = vertices.size();//点的个数

float lambda = 0.01;

float beta = 0.01;

for (int i = 0; i < n; i++)

{

triple_A.push_back(Triplet<float>(i, i, 1.0f*r1));

if (vertices[i]->point_type==boundary)//第二部分,边界点

{

triple_A.push_back(Triplet<float>(i, i, r3*beta));

}

if (vertices[i]->point_type == drag)

{

triple_A.push_back(Triplet<float>(i, i, r2*lambda));

}

for (int j = 0; j < vertices[i]->neighborIdx.size(); j++)

{

triple_A.push_back(Triplet<float>(i, vertices[i]->neighborIdx[j], -1.0f / vertices[i]->degree_*r1));

}

for (int j = 0; j < vertices[i]->degree_;j++)

{

int idx = vertices[i]->neighborIdx[j];

int dk = vertices[vertices[i]->neighborIdx[j]]->degree_;

triple_A.push_back(Triplet<float>(i, idx, -1.0f / dk*r1));

for (int k = 0; k < dk; k++)

{

triple_A.push_back(Triplet<float>(i, vertices[idx]->neighborIdx[k], 1.0f / dk / dk*r1));

}

}

}

SparseMatrix<float> A(n, n);

A.setFromTriplets(triple_A.begin(), triple_A.end());

solver.compute(A);//对A进行预分解

}

void RenderingWidget::get_b()

{

const std::vector<HE_vert*>& vertices = *(ptr_mesh_->get_vertex_list());

float r1 = 1.0f / vertices.size();                 //没错

float r2 = 1.0f / current_index_.size();    //没错

float r3 = 0;                         //没错

for (int i = 0; i < vertices.size(); i++)         //没错

{

if (vertices[i]->boundary_flag_==BOUNDARY)

{

r3++;

}

}

if (r3>0)

{

r3 = 1.0f / r3;

}

int n = vertices.size();

float lambda = 0.01;

float beta = 0.01;

VectorXf bx_(n), by_(n), bz_(n);

bx_.fill(0);

by_.fill(0);

bz_.fill(0);

for (int i = 0; i < n; i++)

{

if (vertices[i]->point_type == drag)//拖动点

{

bx_[i] += vertices[i]->position_[0] * r2*lambda;

by_[i] += vertices[i]->position_[1] * r2*lambda;

bz_[i] += vertices[i]->position_[2] * r2*lambda;

}

if (vertices[i]->boundary_flag_==BOUNDARY)//第二部分,边界点

{

bx_[i] += vertices[i]->position_[0] * r3*beta;

by_[i] += vertices[i]->position_[1] * r3*beta;

bz_[i] += vertices[i]->position_[2] * r3*beta;

}

bx_[i] += vertices[i]->ab_position[0] * r1;

by_[i] += vertices[i]->ab_position[1] * r1;

bz_[i] += vertices[i]->ab_position[2] * r1;

for (int j = 0; j < vertices[i]->degree_; j++)

{

int idx = vertices[i]->neighborIdx[j];

int dk = vertices[vertices[i]->neighborIdx[j]]->degree_;

bx_[i] -= vertices[idx]->ab_position[0] / dk*r1;

by_[i] -= vertices[idx]->ab_position[1] / dk*r1;

bz_[i] -= vertices[idx]->ab_position[2] / dk*r1;

}

}

SparseMatrix<float> A(n, n);

A.setFromTriplets(triple_A.begin(), triple_A.end());

bx.clear();

by.clear();

bz.clear();

for (int i = 0; i < n; i++)

{

bx.push_back(bx_(i));

by.push_back(by_(i));

bz.push_back(bz_(i));

}

}

void RenderingWidget::solve_system()

{

const vector<HE_vert*>& vertices = *(ptr_mesh_->get_vertex_list());

int n = vertices.size();

VectorXf bx_(n), by_(n), bz_(n);

bx_.fill(0);

by_.fill(0);

bz_.fill(0);

for (int i = 0; i < n; i++)

{

bx_[i] = bx[i];

by_[i] = by[i];

bz_[i] = bz[i];

}

bx_ = solver.solve(bx_);

by_ = solver.solve(by_);

bz_ = solver.solve(bz_);

for (int i = 0; i < vertices.size(); i++)

{

vertices[i]->position_[0] = bx_(i);

vertices[i]->position_[1] = by_(i);

vertices[i]->position_[2] = bz_(i);

}

}

时间: 2024-10-25 13:41:07

Laplace mesh editing的相关文章

Laplacian Mesh Editing 拉普拉斯形变(待回学校更新)

前言 因为实验需要用到拉普拉斯形变,但找了好久找到一个非常适合入门的资料.再此记录下我的学习过程,也算搬运翻译过来. Introduction / Basic Laplacian Mesh Representation 主要思想,构造一个delta coordinates表示模型. N(i)表示i的邻居,w_ij就是各种权重需要我们计算.如果权重全部是1,那么: 这里的d_i表示i的邻居个数.这种权重全为1的情况也叫做umberalla weighting 另外一种较出名的是cotangent

网格形变算法(Gradient-Based Deformation)

将三角网格上的顶点坐标(x,y,z)看作3个独立的标量场,那么网格上每个三角片都存在3个独立的梯度场.该梯度场是网格的微分属性,相当于网格的特征,在形变过程中年不发生变化(避免网格形变后畸形).那么当用户拖拽网格上的控制点集时,网格形变问题即变为求解以下式子: 根据变分法,上式最小化即求解泊松方程: 其中Φ为待求的网格形变后坐标,w为网格形变后的梯度场. 上式可以进一步表示为求解稀疏线性方程组: 其中L为网格的拉普拉斯算子,b为梯度场w在网格顶点处的散度值. 问题的关键是如何得到网格形变后的梯度

网格形变算法(Laplacian-Based Deformation)

网格上顶点的Laplace坐标(均匀权重)定义为:,其中di为顶点vi的1环邻域顶点数. 网格Laplace坐标可以用矩阵形式表示:△=LV,其中,那么根据网格的Laplace坐标通过求解稀疏线性方程组可以得到网格的顶点坐标. 基于网格Laplace形变算法的思想:网格上顶点的Laplace坐标作为网格的细节特征,其在网格形变前后的局部坐标系内不发生变化.Laplace形变问题可以用如下数学优化形式表达,那么问题的关键是如何得到网格形变后的Laplace坐标,或者说是每个顶点Laplace坐标的

基于泊松方程的网格变形算法实现

参考论文:Mesh Editing withPoisson-Based Gradient Field Manipulation. Yi ZhouYu, KunZhou 需要源代码请联系我:[email protected],发邮件的时候请说清楚三件事:你是谁? 你来自哪里? 用代码做什么? 实验效果: 这篇文章是发在SIGGRAPH2004上的,至今引用次数400+,可以说是几何处理领域一篇比较不错的文章,2003年PPére等人发了一篇关于图像处理的文章:Poisson ImageEditin

Femap 11.2 新功能:建模

在上篇中,我们介绍了Femap 11.2在几何编辑方面的功能增强,本次我们将进一步来了解Femap 11.2 在有限元建模方面的功能改进,更多功能欢迎访问西门子Femap官方网站 http://www.plm.automation.siemens.com/zh_cn/products/femap/index.shtml. Contact Manager Data Surface (接触管理器数据面) 当处理复杂装配体或组件时,零部件间的交互关系对于分析结果的准确性有着至关重要的影响,但随着模型的

Blender文档翻译:Operators tutorial(操作教程)

原文:https://wiki.blender.org/index.php/Dev:2.5/Source/Architecture/Operators/Tutorial 逐行解释操作如何工作的.首先解释网格细分(mesh subdivide),一个相对简单的算子.接下来,我们将解释一个更复杂的模态操作,3D视图缩放. 网络细分(Mesh Subdivide) 注册 我们必须做的第一件事是向窗口管理器注册操作符类型.为此,我们定义了一个函数,在启动时由窗口管理器调用. 1 void MESH_OT

拉普拉斯算子网格形变

网格顶点的拉普拉斯坐标定义为 ,公式中di代表顶点Vi的一环邻域顶点数量 网格的拉普拉斯坐标用矩阵表示, ,  然后通过网格的拉普拉斯坐标求解为稀疏线性方程组便可以得到形变后的网格顶点 初始化拉普拉斯矩阵 1 void mainNode::InitLpls() 2 { 3 int vertexNumber = m_vecAllVertex.size(); 4 //计算拉普拉斯坐标.方式一 5 for (int i = 0; i < vertexNumber; i++) 6 { 7 pVERTEX

hbot固件配置

又入了一台打印机,171到手,本来之前有更好的,无奈别人下手太快,只剩这台了. 175x135x180的样子. 创客的板,还带16g的闪迪内存卡,看到那会儿感觉赚大了! 拿到的时候不少螺丝松的,有的打印件也裂口了,拧紧螺丝,调平后打了打感觉操作很多不习惯, 连上电脑看固件原来是marlin1.0的!上github下载了1.19固件,网上似乎没有找到关于hbot的配置贴, 反正以前琢磨过不少次,直接上configuration.h 凭借经验改了改,基本能用了.之后有待调试. 这个平台用料很足,3点

Unity自定义mesh以及编译器

Star 自定义编辑器简易教程 an introduction to custom editors 原文地址 http://catlikecoding.com/unity/tutorials/star/ http://blog.csdn.net/lilanfei/article/details/7680802 简介 Introduction 这个教程将让你学会如何创建一个星型控件以及如何制作这个控件的自定义编辑器.你将学会: 动态的建立Mesh. 使用一个嵌套类. 建立一个自定义编辑器. 使用S