add kd tree class
add mesh decimation algorithm
This commit is contained in:
@@ -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
|
||||
|
||||
97
src/Mod/Mesh/App/Core/Decimation.cpp
Normal file
97
src/Mod/Mesh/App/Core/Decimation.cpp
Normal 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);
|
||||
}
|
||||
46
src/Mod/Mesh/App/Core/Decimation.h
Normal file
46
src/Mod/Mesh/App/Core/Decimation.h
Normal 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
|
||||
155
src/Mod/Mesh/App/Core/KDTree.cpp
Normal file
155
src/Mod/Mesh/App/Core/KDTree.cpp
Normal 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);
|
||||
}
|
||||
60
src/Mod/Mesh/App/Core/KDTree.h
Normal file
60
src/Mod/Mesh/App/Core/KDTree.h
Normal 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
|
||||
518
src/Mod/Mesh/App/Core/Simplify.h
Normal file
518
src/Mod/Mesh/App/Core/Simplify.h
Normal 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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
@@ -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();
|
||||
|
||||
@@ -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> §ions,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user