add kd tree class

add mesh decimation algorithm
This commit is contained in:
wmayer
2017-11-14 11:37:30 +01:00
parent 0b33f977f7
commit 04ea295280
35 changed files with 5546 additions and 27 deletions

View File

@@ -5,7 +5,7 @@ endif(WIN32)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src/3rdParty
${CMAKE_SOURCE_DIR}/src/3rdParty/libkdtree
${Boost_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
${XercesC_INCLUDE_DIRS}
@@ -50,6 +50,8 @@ SET(Core_SRCS
Core/Builder.h
Core/Curvature.cpp
Core/Curvature.h
Core/Decimation.cpp
Core/Decimation.h
Core/Definitions.cpp
Core/Definitions.h
Core/Degeneration.cpp
@@ -64,6 +66,8 @@ SET(Core_SRCS
Core/Info.cpp
Core/Info.h
Core/Iterator.h
Core/KDTree.cpp
Core/KDTree.h
Core/MeshIO.cpp
Core/MeshIO.h
Core/MeshKernel.cpp

View File

@@ -0,0 +1,97 @@
/***************************************************************************
* Copyright (c) 2013 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include "Decimation.h"
#include "MeshKernel.h"
#include "Algorithm.h"
#include "Iterator.h"
#include "TopoAlgorithm.h"
#include <Base/Tools.h>
#include "Simplify.h"
using namespace MeshCore;
MeshSimplify::MeshSimplify(MeshKernel& mesh)
: myKernel(mesh)
{
}
MeshSimplify::~MeshSimplify()
{
}
void MeshSimplify::simplify(float tolerance, float reduction)
{
Simplify alg;
const MeshPointArray& points = myKernel.GetPoints();
for (std::size_t i = 0; i < points.size(); i++) {
Simplify::Vertex v;
v.p = points[i];
alg.vertices.push_back(v);
}
const MeshFacetArray& facets = myKernel.GetFacets();
for (std::size_t i = 0; i < facets.size(); i++) {
Simplify::Triangle t;
for (int j = 0; j < 3; j++)
t.v[j] = facets[i]._aulPoints[j];
alg.triangles.push_back(t);
}
int target_count = static_cast<int>(static_cast<float>(facets.size()) * (1.0f-reduction));
// Simplification starts
alg.simplify_mesh(target_count, tolerance);
// Simplification done
MeshPointArray new_points;
new_points.reserve(alg.vertices.size());
for (std::size_t i = 0; i < alg.vertices.size(); i++) {
new_points.push_back(alg.vertices[i].p);
}
std::size_t numFacets = 0;
for (std::size_t i = 0; i < alg.triangles.size(); i++) {
if (!alg.triangles[i].deleted)
numFacets++;
}
MeshFacetArray new_facets;
new_facets.reserve(numFacets);
for (std::size_t i = 0; i < alg.triangles.size(); i++) {
if (!alg.triangles[i].deleted) {
MeshFacet face;
face._aulPoints[0] = alg.triangles[i].v[0];
face._aulPoints[1] = alg.triangles[i].v[1];
face._aulPoints[2] = alg.triangles[i].v[2];
new_facets.push_back(face);
}
}
myKernel.Adopt(new_points, new_facets, true);
}

View File

@@ -0,0 +1,46 @@
/***************************************************************************
* Copyright (c) 2013 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef MESH_DECIMATION_H
#define MESH_DECIMATION_H
namespace MeshCore
{
class MeshKernel;
class MeshExport MeshSimplify
{
public:
MeshSimplify(MeshKernel&);
~MeshSimplify();
void simplify(float tolerance, float reduction);
private:
MeshKernel& myKernel;
};
} // namespace MeshCore
#endif // MESH_DECIMATION_H

View File

@@ -0,0 +1,155 @@
/***************************************************************************
* Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifdef _MSC_VER
# pragma warning(disable : 4396)
#endif
#ifndef _PreComp_
#endif
#include "KDTree.h"
#include <kdtree++/kdtree.hpp>
using namespace MeshCore;
struct Point3d
{
typedef float value_type;
Point3d(const Base::Vector3f& f, unsigned long i) : p(f), i(i)
{
}
inline value_type operator[](const int N) const
{
return p[N];
}
inline bool operator==(const Point3d& other) const
{
return (this->p) == (other.p);
}
inline bool operator!=(const Point3d& other) const
{
return (this->p) != (other.p);
}
inline void operator=(const Point3d& other)
{
this->p = other.p;
this->i = other.i;
}
Base::Vector3f p;
unsigned long i;
};
typedef KDTree::KDTree<3, Point3d> MyKDTree;
class MeshKDTree::Private
{
public:
MyKDTree kd_tree;
};
MeshKDTree::MeshKDTree(const std::vector<Base::Vector3f>& points) : d(new Private)
{
unsigned long index=0;
for (std::vector<Base::Vector3f>::const_iterator it = points.begin(); it != points.end(); ++it) {
d->kd_tree.insert(Point3d(*it, index++));
}
}
MeshKDTree::MeshKDTree(const MeshPointArray& points) : d(new Private)
{
unsigned long index=0;
for (MeshPointArray::_TConstIterator it = points.begin(); it != points.end(); ++it) {
d->kd_tree.insert(Point3d(*it, index++));
}
}
MeshKDTree::~MeshKDTree()
{
delete d;
}
bool MeshKDTree::IsEmpty() const
{
return d->kd_tree.empty();
}
void MeshKDTree::Clear()
{
d->kd_tree.clear();
}
void MeshKDTree::Optimize()
{
d->kd_tree.optimize();
}
unsigned long MeshKDTree::FindNearest(const Base::Vector3f& p, Base::Vector3f& n, float& dist) const
{
std::pair<MyKDTree::const_iterator, MyKDTree::distance_type> it =
d->kd_tree.find_nearest(Point3d(p,0));
if (it.first == d->kd_tree.end())
return ULONG_MAX;
unsigned long index = it.first->i;
n = it.first->p;
dist = it.second;
return index;
}
unsigned long MeshKDTree::FindNearest(const Base::Vector3f& p, float max_dist,
Base::Vector3f& n, float& dist) const
{
std::pair<MyKDTree::const_iterator, MyKDTree::distance_type> it =
d->kd_tree.find_nearest(Point3d(p,0), max_dist);
if (it.first == d->kd_tree.end())
return ULONG_MAX;
unsigned long index = it.first->i;
n = it.first->p;
dist = it.second;
return index;
}
unsigned long MeshKDTree::FindExact(const Base::Vector3f& p) const
{
MyKDTree::const_iterator it =
d->kd_tree.find_exact(Point3d(p,0));
if (it == d->kd_tree.end())
return ULONG_MAX;
unsigned long index = it->i;
return index;
}
void MeshKDTree::FindInRange(const Base::Vector3f& p, float range, std::vector<unsigned long>& indices) const
{
std::vector<Point3d> v;
d->kd_tree.find_within_range(Point3d(p,0), range, std::back_inserter(v));
indices.reserve(v.size());
for (std::vector<Point3d>::iterator it = v.begin(); it != v.end(); ++it)
indices.push_back(it->i);
}

View File

@@ -0,0 +1,60 @@
/***************************************************************************
* Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef MESH_KDTREE_H
#define MESH_KDTREE_H
#include "Elements.h"
namespace MeshCore
{
class MeshExport MeshKDTree
{
public:
MeshKDTree(const std::vector<Base::Vector3f>& points);
MeshKDTree(const MeshPointArray& points);
~MeshKDTree();
bool IsEmpty() const;
void Clear();
void Optimize();
unsigned long FindNearest(const Base::Vector3f& p, Base::Vector3f& n, float&) const;
unsigned long FindNearest(const Base::Vector3f& p, float max_dist,
Base::Vector3f& n, float&) const;
unsigned long FindExact(const Base::Vector3f& p) const;
void FindInRange(const Base::Vector3f&, float, std::vector<unsigned long>&) const;
private:
class Private;
Private* d;
MeshKDTree(const MeshKDTree&);
void operator= (const MeshKDTree&);
};
} // namespace MeshCore
#endif // MESH_KDTREE_H

View File

@@ -0,0 +1,518 @@
// http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html
// https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification
//
// MIT License
// Changes:
// * Use Base::Vector3f as vec3f class
// * Move global variables to a class to make the algorithm usable for multi-threading
// * Comment out printf statements
// * Fix compiler warnings
// * Remove macros loop,i,j,k
#include <vector>
#include <Base/Vector3D.h>
#define _USE_MATH_DEFINES
typedef Base::Vector3f vec3f;
class SymetricMatrix {
public:
// Constructor
SymetricMatrix(double c=0) { for (std::size_t i=0;i<10;++i ) m[i] = c; }
SymetricMatrix(double m11, double m12, double m13, double m14,
double m22, double m23, double m24,
double m33, double m34,
double m44) {
m[0] = m11; m[1] = m12; m[2] = m13; m[3] = m14;
m[4] = m22; m[5] = m23; m[6] = m24;
m[7] = m33; m[8] = m34;
m[9] = m44;
}
// Make plane
SymetricMatrix(double a,double b,double c,double d)
{
m[0] = a*a; m[1] = a*b; m[2] = a*c; m[3] = a*d;
m[4] = b*b; m[5] = b*c; m[6] = b*d;
m[7 ] =c*c; m[8 ] = c*d;
m[9 ] = d*d;
}
double operator[](int c) const { return m[c]; }
// Determinant
double det(int a11, int a12, int a13,
int a21, int a22, int a23,
int a31, int a32, int a33)
{
double det = m[a11]*m[a22]*m[a33] + m[a13]*m[a21]*m[a32] + m[a12]*m[a23]*m[a31]
- m[a13]*m[a22]*m[a31] - m[a11]*m[a23]*m[a32]- m[a12]*m[a21]*m[a33];
return det;
}
const SymetricMatrix operator+(const SymetricMatrix& n) const
{
return SymetricMatrix( m[0]+n[0], m[1]+n[1], m[2]+n[2], m[3]+n[3],
m[4]+n[4], m[5]+n[5], m[6]+n[6],
m[ 7]+n[ 7], m[ 8]+n[8 ],
m[ 9]+n[9 ]);
}
SymetricMatrix& operator+=(const SymetricMatrix& n)
{
m[0]+=n[0]; m[1]+=n[1]; m[2]+=n[2]; m[3]+=n[3];
m[4]+=n[4]; m[5]+=n[5]; m[6]+=n[6]; m[7]+=n[7];
m[8]+=n[8]; m[9]+=n[9];
return *this;
}
double m[10];
};
///////////////////////////////////////////
class Simplify
{
public:
struct Triangle { int v[3];double err[4];int deleted,dirty;vec3f n; };
struct Vertex { vec3f p;int tstart,tcount;SymetricMatrix q;int border;};
struct Ref { int tid,tvertex; };
std::vector<Triangle> triangles;
std::vector<Vertex> vertices;
std::vector<Ref> refs;
void simplify_mesh(int target_count, double tolerance_factor, double agressiveness=7);
private:
// Helper functions
double vertex_error(SymetricMatrix q, double x, double y, double z);
double calculate_error(int id_v1, int id_v2, vec3f &p_result);
bool flipped(vec3f p,int i0,int i1,Vertex &v0,Vertex &v1,std::vector<int> &deleted);
void update_triangles(int i0,Vertex &v,std::vector<int> &deleted,int &deleted_triangles);
void update_mesh(int iteration);
void compact_mesh();
};
//
// Main simplification function
//
// target_count : target nr. of triangles
// agressiveness : sharpness to increase the threashold.
// 5..8 are good numbers
// more iterations yield higher quality
//
void Simplify::simplify_mesh(int target_count, double tolerance_factor, double agressiveness)
{
(void)tolerance_factor;
// init
//printf("%s - start\n",__FUNCTION__);
//int timeStart=timeGetTime();
for (std::size_t i=0;i<triangles.size();++i)
triangles[i].deleted=0;
// main iteration loop
int deleted_triangles=0;
std::vector<int> deleted0,deleted1;
int triangle_count=triangles.size();
for (int iteration=0;iteration<100;++iteration)
{
// target number of triangles reached ? Then break
//printf("iteration %d - triangles %d\n",iteration,triangle_count-deleted_triangles);
if (triangle_count-deleted_triangles<=target_count)
break;
// update mesh once in a while
if (iteration%5==0)
{
update_mesh(iteration);
}
// clear dirty flag
for (std::size_t i=0;i<triangles.size();++i)
triangles[i].dirty=0;
//
// All triangles with edges below the threshold will be removed
//
// The following numbers works well for most models.
// If it does not, try to adjust the 3 parameters
//
double threshold = 0.000000001*pow(double(iteration+3),agressiveness);
//if (tolerance_factor < 1.0)
// threshold *= tolerance_factor;
// remove vertices & mark deleted triangles
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
if (t.err[3]>threshold)
continue;
if (t.deleted)
continue;
if (t.dirty)
continue;
for (std::size_t j=0;j<3;++j)
{
if (t.err[j]<threshold)
{
int i0=t.v[ j ]; Vertex &v0 = vertices[i0];
int i1=t.v[(j+1)%3]; Vertex &v1 = vertices[i1];
// Border check
if (v0.border != v1.border)
continue;
// Compute vertex to collapse to
vec3f p;
calculate_error(i0,i1,p);
deleted0.resize(v0.tcount); // normals temporarily
deleted1.resize(v1.tcount); // normals temporarily
// dont remove if flipped
if (flipped(p,i0,i1,v0,v1,deleted0))
continue;
if (flipped(p,i1,i0,v1,v0,deleted1))
continue;
// not flipped, so remove edge
v0.p=p;
v0.q=v1.q+v0.q;
int tstart=refs.size();
update_triangles(i0,v0,deleted0,deleted_triangles);
update_triangles(i0,v1,deleted1,deleted_triangles);
int tcount=refs.size()-tstart;
if (tcount<=v0.tcount)
{
// save ram
if (tcount)
memcpy(&refs[v0.tstart],&refs[tstart],tcount*sizeof(Ref));
}
else
{
// append
v0.tstart=tstart;
}
v0.tcount=tcount;
break;
}
}
// done?
if (triangle_count-deleted_triangles<=target_count)
break;
}
}
// clean up mesh
compact_mesh();
// ready
//int timeEnd=timeGetTime();
//printf("%s - %d/%d %d%% removed in %d ms\n",__FUNCTION__,
// triangle_count-deleted_triangles,
// triangle_count,deleted_triangles*100/triangle_count,
// timeEnd-timeStart);
}
// Check if a triangle flips when this edge is removed
bool Simplify::flipped(vec3f p, int i0, int i1,
Vertex &v0,
Vertex &v1,
std::vector<int> &deleted)
{
(void)i0; (void)v1;
int bordercount=0;
for (int k=0;k<v0.tcount;++k)
{
Triangle &t=triangles[refs[v0.tstart+k].tid];
if (t.deleted)
continue;
int s=refs[v0.tstart+k].tvertex;
int id1=t.v[(s+1)%3];
int id2=t.v[(s+2)%3];
if (id1==i1 || id2==i1) // delete ?
{
bordercount++;
deleted[k]=1;
continue;
}
vec3f d1 = vertices[id1].p-p; d1.Normalize();
vec3f d2 = vertices[id2].p-p; d2.Normalize();
if (fabs(d1.Dot(d2))>0.999)
return true;
vec3f n;
n = d1.Cross(d2);
n.Normalize();
deleted[k]=0;
if (n.Dot(t.n)<0.2)
return true;
}
return false;
}
// Update triangle connections and edge error after a edge is collapsed
void Simplify::update_triangles(int i0,Vertex &v,std::vector<int> &deleted,int &deleted_triangles)
{
vec3f p;
for (int k=0;k<v.tcount;++k)
{
Ref &r=refs[v.tstart+k];
Triangle &t=triangles[r.tid];
if (t.deleted)
continue;
if (deleted[k])
{
t.deleted=1;
deleted_triangles++;
continue;
}
t.v[r.tvertex]=i0;
t.dirty=1;
t.err[0]=calculate_error(t.v[0],t.v[1],p);
t.err[1]=calculate_error(t.v[1],t.v[2],p);
t.err[2]=calculate_error(t.v[2],t.v[0],p);
t.err[3]=std::min(t.err[0],std::min(t.err[1],t.err[2]));
refs.push_back(r);
}
}
// compact triangles, compute edge error and build reference list
void Simplify::update_mesh(int iteration)
{
if(iteration>0) // compact triangles
{
int dst=0;
for (std::size_t i=0;i<triangles.size();++i)
{
if (!triangles[i].deleted)
{
triangles[dst++]=triangles[i];
}
}
triangles.resize(dst);
}
//
// Init Quadrics by Plane & Edge Errors
//
// required at the beginning ( iteration == 0 )
// recomputing during the simplification is not required,
// but mostly improves the result for closed meshes
//
if (iteration == 0)
{
for (std::size_t i=0;i<vertices.size();++i)
vertices[i].q=SymetricMatrix(0.0);
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
vec3f n,p[3];
for (std::size_t j=0;j<3;++j)
p[j]=vertices[t.v[j]].p;
n = (p[1]-p[0]).Cross(p[2]-p[0]);
n.Normalize();
t.n=n;
for (std::size_t j=0;j<3;++j)
vertices[t.v[j]].q = vertices[t.v[j]].q+SymetricMatrix(n.x,n.y,n.z,-n.Dot(p[0]));
}
for (std::size_t i=0;i<triangles.size();++i)
{
// Calc Edge Error
Triangle &t=triangles[i];vec3f p;
for (std::size_t j=0;j<3;++j)
t.err[j] = calculate_error(t.v[j],t.v[(j+1)%3],p);
t.err[3]=std::min(t.err[0],std::min(t.err[1],t.err[2]));
}
}
// Init Reference ID list
for (std::size_t i=0;i<vertices.size();++i)
{
vertices[i].tstart=0;
vertices[i].tcount=0;
}
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
for (std::size_t j=0;j<3;++j)
vertices[t.v[j]].tcount++;
}
int tstart=0;
for (std::size_t i=0;i<vertices.size();++i)
{
Vertex &v=vertices[i];
v.tstart=tstart;
tstart+=v.tcount;
v.tcount=0;
}
// Write References
refs.resize(triangles.size()*3);
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
for (std::size_t j=0;j<3;++j)
{
Vertex &v=vertices[t.v[j]];
refs[v.tstart+v.tcount].tid=i;
refs[v.tstart+v.tcount].tvertex=j;
v.tcount++;
}
}
// Identify boundary : vertices[].border=0,1
if (iteration == 0)
{
std::vector<int> vcount,vids;
for (std::size_t i=0;i<vertices.size();++i)
vertices[i].border=0;
for (std::size_t i=0;i<vertices.size();++i)
{
Vertex &v=vertices[i];
vcount.clear();
vids.clear();
for (int j=0; j<v.tcount; ++j)
{
int k=refs[v.tstart+j].tid;
Triangle &t=triangles[k];
for (int k=0;k<3;++k)
{
std::size_t ofs=0; int id=t.v[k];
while(ofs<vcount.size())
{
if (vids[ofs]==id)
break;
ofs++;
}
if(ofs==vcount.size())
{
vcount.push_back(1);
vids.push_back(id);
}
else
{
vcount[ofs]++;
}
}
}
for (std::size_t j=0;j<vcount.size();++j) {
if (vcount[j]==1)
vertices[vids[j]].border=1;
}
}
}
}
// Finally compact mesh before exiting
void Simplify::compact_mesh()
{
int dst=0;
for (std::size_t i=0;i<vertices.size();++i)
{
vertices[i].tcount=0;
}
for (std::size_t i=0;i<triangles.size();++i)
{
if (!triangles[i].deleted)
{
Triangle &t=triangles[i];
triangles[dst++]=t;
for (std::size_t j=0;j<3;++j)
vertices[t.v[j]].tcount=1;
}
}
triangles.resize(dst);
dst=0;
for (std::size_t i=0;i<vertices.size();++i)
{
if (vertices[i].tcount)
{
vertices[i].tstart=dst;
vertices[dst].p=vertices[i].p;
dst++;
}
}
for (std::size_t i=0;i<triangles.size();++i)
{
Triangle &t=triangles[i];
for (std::size_t j=0;j<3;++j)
t.v[j]=vertices[t.v[j]].tstart;
}
vertices.resize(dst);
}
// Error between vertex and Quadric
double Simplify::vertex_error(SymetricMatrix q, double x, double y, double z)
{
return q[0]*x*x + 2*q[1]*x*y + 2*q[2]*x*z + 2*q[3]*x + q[4]*y*y
+ 2*q[5]*y*z + 2*q[6]*y + q[7]*z*z + 2*q[8]*z + q[9];
}
// Error for one edge
double Simplify::calculate_error(int id_v1, int id_v2, vec3f &p_result)
{
// compute interpolated vertex
SymetricMatrix q = vertices[id_v1].q + vertices[id_v2].q;
bool border = vertices[id_v1].border & vertices[id_v2].border;
double error=0;
double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7);
if (det != 0 && !border)
{
// q_delta is invertible
p_result.x = -1/det*(q.det(1, 2, 3, 4, 5, 6, 5, 7 , 8)); // vx = A41/det(q_delta)
p_result.y = 1/det*(q.det(0, 2, 3, 1, 5, 6, 2, 7 , 8)); // vy = A42/det(q_delta)
p_result.z = -1/det*(q.det(0, 1, 3, 1, 4, 6, 2, 5, 8)); // vz = A43/det(q_delta)
error = vertex_error(q, p_result.x, p_result.y, p_result.z);
}
else
{
// det = 0 -> try to find best result
vec3f p1=vertices[id_v1].p;
vec3f p2=vertices[id_v2].p;
vec3f p3=(p1+p2)/2;
double error1 = vertex_error(q, p1.x,p1.y,p1.z);
double error2 = vertex_error(q, p2.x,p2.y,p2.z);
double error3 = vertex_error(q, p3.x,p3.y,p3.z);
error = std::min(error1, std::min(error2, error3));
if (error1 == error)
p_result=p1;
if (error2 == error)
p_result=p2;
if (error3 == error)
p_result=p3;
}
return error;
}
///////////////////////////////////////////

View File

@@ -51,6 +51,7 @@
#include "Core/Triangulation.h"
#include "Core/Trim.h"
#include "Core/Visitor.h"
#include "Core/Decimation.h"
#include "Mesh.h"
#include "MeshPy.h"
@@ -940,6 +941,12 @@ void MeshObject::smooth(int iterations, float d_max)
_kernel.Smooth(iterations, d_max);
}
void MeshObject::decimate(float fTolerance, float fReduction)
{
MeshCore::MeshSimplify dm(this->_kernel);
dm.simplify(fTolerance, fReduction);
}
Base::Vector3d MeshObject::getPointNormal(unsigned long index) const
{
std::vector<Base::Vector3f> temp = _kernel.CalcVertexNormals();

View File

@@ -216,6 +216,7 @@ public:
void movePoint(unsigned long, const Base::Vector3d& v);
void setPoint(unsigned long, const Base::Vector3d& v);
void smooth(int iterations, float d_max);
void decimate(float fTolerance, float fReduction);
Base::Vector3d getPointNormal(unsigned long) const;
std::vector<Base::Vector3d> getPointNormals() const;
void crossSections(const std::vector<TPlane>&, std::vector<TPolylines> &sections,

View File

@@ -409,6 +409,19 @@ The argument int is the mode: 0=inner, 1=outer
smooth([iteration=1,maxError=FLT_MAX])</UserDocu>
</Documentation>
</Methode>
<Methode Name="decimate">
<Documentation>
<UserDocu>
Decimate the mesh
decimate(tolerance(Float), reduction(Float))
tolerance: maximum error
reduction: reduction factor must be in the range [0.0,1.0]
Example:
mesh.decimate(0.5, 0.1) # reduction by up to 10 percent
mesh.decimate(0.5, 0.9) # reduction by up to 90 percent
</UserDocu>
</Documentation>
</Methode>
<Methode Name="optimizeTopology" Const="true">
<Documentation>
<UserDocu>Optimize the edges to get nicer facets</UserDocu>

View File

@@ -353,7 +353,7 @@ PyObject* MeshPy::offset(PyObject *args)
getMeshObjectPtr()->offsetSpecial2(Float);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::offsetSpecial(PyObject *args)
@@ -366,7 +366,7 @@ PyObject* MeshPy::offsetSpecial(PyObject *args)
getMeshObjectPtr()->offsetSpecial(Float,zmax,zmin);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::crossSections(PyObject *args)
@@ -1074,7 +1074,7 @@ PyObject* MeshPy::flipNormals(PyObject *args)
getMeshObjectPtr()->flipNormals();
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::hasNonUniformOrientedFacets(PyObject *args)
@@ -1119,7 +1119,7 @@ PyObject* MeshPy::harmonizeNormals(PyObject *args)
getMeshObjectPtr()->harmonizeNormals();
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::countComponents(PyObject *args)
@@ -1143,7 +1143,7 @@ PyObject* MeshPy::removeComponents(PyObject *args)
}
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::fillupHoles(PyObject *args)
@@ -1184,7 +1184,7 @@ PyObject* MeshPy::fixIndices(PyObject *args)
getMeshObjectPtr()->validateIndices();
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::fixDeformations(PyObject *args)
@@ -1198,7 +1198,7 @@ PyObject* MeshPy::fixDeformations(PyObject *args)
getMeshObjectPtr()->validateDeformations(fMaxAngle, fEpsilon);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::fixDegenerations(PyObject *args)
@@ -1211,7 +1211,7 @@ PyObject* MeshPy::fixDegenerations(PyObject *args)
getMeshObjectPtr()->validateDegenerations(fEpsilon);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::removeDuplicatedPoints(PyObject *args)
@@ -1223,7 +1223,7 @@ PyObject* MeshPy::removeDuplicatedPoints(PyObject *args)
getMeshObjectPtr()->removeDuplicatedPoints();
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::removeDuplicatedFacets(PyObject *args)
@@ -1235,7 +1235,7 @@ PyObject* MeshPy::removeDuplicatedFacets(PyObject *args)
getMeshObjectPtr()->removeDuplicatedFacets();
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::refine(PyObject *args)
@@ -1247,7 +1247,7 @@ PyObject* MeshPy::refine(PyObject *args)
getMeshObjectPtr()->refine();
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::optimizeTopology(PyObject *args)
@@ -1261,7 +1261,7 @@ PyObject* MeshPy::optimizeTopology(PyObject *args)
getMeshObjectPtr()->optimizeTopology(fMaxAngle);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::optimizeEdges(PyObject *args)
@@ -1274,7 +1274,7 @@ PyObject* MeshPy::optimizeEdges(PyObject *args)
getMeshObjectPtr()->optimizeEdges();
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::splitEdges(PyObject *args)
@@ -1286,7 +1286,7 @@ PyObject* MeshPy::splitEdges(PyObject *args)
getMeshObjectPtr()->splitEdges();
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::splitEdge(PyObject *args)
@@ -1321,7 +1321,7 @@ PyObject* MeshPy::splitEdge(PyObject *args)
getMeshObjectPtr()->splitEdge(facet, neighbour, v);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::splitFacet(PyObject *args)
@@ -1351,7 +1351,7 @@ PyObject* MeshPy::splitFacet(PyObject *args)
getMeshObjectPtr()->splitFacet(facet, v1, v2);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::swapEdge(PyObject *args)
@@ -1381,7 +1381,7 @@ PyObject* MeshPy::swapEdge(PyObject *args)
getMeshObjectPtr()->swapEdge(facet, neighbour);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::collapseEdge(PyObject *args)
@@ -1411,7 +1411,7 @@ PyObject* MeshPy::collapseEdge(PyObject *args)
getMeshObjectPtr()->collapseEdge(facet, neighbour);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::collapseFacet(PyObject *args)
@@ -1429,7 +1429,7 @@ PyObject* MeshPy::collapseFacet(PyObject *args)
getMeshObjectPtr()->collapseFacet(facet);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::insertVertex(PyObject *args)
@@ -1452,7 +1452,7 @@ PyObject* MeshPy::insertVertex(PyObject *args)
getMeshObjectPtr()->insertVertex(facet, v);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::snapVertex(PyObject *args)
@@ -1475,7 +1475,7 @@ PyObject* MeshPy::snapVertex(PyObject *args)
getMeshObjectPtr()->snapVertex(facet, v);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::printInfo(PyObject *args)
@@ -1513,7 +1513,7 @@ PyObject* MeshPy::collapseFacets(PyObject *args)
return 0;
}
Py_Return;
Py_Return;
}
PyObject* MeshPy::foraminate(PyObject *args)
@@ -1590,7 +1590,7 @@ PyObject* MeshPy::cut(PyObject *args)
polygon2d.Add(Base::Vector2d(it->x, it->y));
getMeshObjectPtr()->cut(polygon2d, proj, MeshObject::CutType(mode));
Py_Return;
Py_Return;
}
PyObject* MeshPy::trim(PyObject *args)
@@ -1624,7 +1624,7 @@ PyObject* MeshPy::trim(PyObject *args)
polygon2d.Add(Base::Vector2d(it->x, it->y));
getMeshObjectPtr()->trim(polygon2d, proj, MeshObject::CutType(mode));
Py_Return;
Py_Return;
}
PyObject* MeshPy::smooth(PyObject *args)
@@ -1639,7 +1639,20 @@ PyObject* MeshPy::smooth(PyObject *args)
getMeshObjectPtr()->smooth(iter, d_max);
} PY_CATCH;
Py_Return;
Py_Return;
}
PyObject* MeshPy::decimate(PyObject *args)
{
float fTol, fRed;
if (!PyArg_ParseTuple(args, "ff", &fTol,&fRed))
return NULL;
PY_TRY {
getMeshObjectPtr()->decimate(fTol, fRed);
} PY_CATCH;
Py_Return;
}
PyObject* MeshPy::nearestFacetOnRay(PyObject *args)

View File

@@ -200,7 +200,7 @@ void MeshSelection::prepareFreehandSelection(bool add,SoEventCallbackCB *cb)
freehand->setColor(1.0f, 0.0f, 0.0f);
freehand->setLineWidth(3.0f);
viewer->navigationStyle()->startSelection(freehand);
QBitmap cursor = QBitmap::fromData(QSize(CROSS_WIDTH, CROSS_HEIGHT), cross_bitmap);
QBitmap mask = QBitmap::fromData(QSize(CROSS_WIDTH, CROSS_HEIGHT), cross_mask_bitmap);
QCursor custom(cursor, mask, CROSS_HOT_X, CROSS_HOT_Y);

View File

@@ -1,6 +1,7 @@
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
${Boost_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
${XercesC_INCLUDE_DIRS}
)
@@ -40,6 +41,7 @@ if(BUILD_QT5)
else()
qt4_add_resources(Resource_SRCS Resources/Test.qrc)
endif()
SET(Resource_SRCS
${Resource_SRCS}
Resources/Test.qrc