/*
* main.cpp
* MeshProcessing
*
* Created by Cindy Grimm on 6/14/10.
* Copyright 2010 Washington University in St. Louis. All rights reserved.
*
*/
#ifdef WIN32
char g_strDataHome[] = "c://graphics/cmg/data/";
#else
char g_strDataHome[] = "/Users/cmg/graphics/cmg/Data/";
#endif
#include <fitting/MeshSimplification_QSlim.H>
#include <utils/Utils_IcosahedralSampler.H>
#include <utils/Utils_Dots.H>
#include "AddNoise.h"
#include "Smooth.h"
#include "ProjectMeshOnMesh.H"
extern void obj2ply(const char *in_str, const char *in_strOut, bool bFlipVOrder );
void Bats( const char * in_strBatNameDir, const char * in_strParams, const char *in_strOutParams )
{
ifstream in( in_strParams, ios::in );
ofstream out( in_strOutParams, ios::out );
if ( !in.good() ) {
cerr << "File " << in_strParams << " not found\n";
return;
} else if ( !out.good() ) {
cerr << "Unable to write to file " << in_strOutParams << "\n";
return;
}
string strName;
int iLevels = 0;
int iQS = 0;
double dPerc, dTemp1, dTemp2;
PMeshLite mesh;
R3Pt ptCenter, ptScale;
out.precision( 12 );
while ( !in.eof() ) {
in >> strName >> iLevels >> dPerc >> iQS >> dTemp1 >> dTemp2;
out << strName << " " << iLevels << " " << dPerc << " " << iQS << " ";
string strBatName = string(in_strBatNameDir) + strName + string(".ply");
mesh.Read( strBatName.c_str() );
ptCenter = mesh.GetCenter(ptScale);
const double dBBox = WINmax( ptScale[0], WINmax(ptScale[1], ptScale[2]) );
out << dBBox * 0.000001 << " " << dBBox * 0.2 << "\n";
out.flush();
}
out.close();
}
/*
void PDBMolecule( const string &in_strPDBFile, const string &in_strMesh, const int in_iSampling, const double in_dRadius )
{
ifstream in( in_strPDBFile.c_str(), ios::in );
if (!in.good() ) {
cerr << "Error opening file " << in_strPDBFile << "\n";
return;
}
std::vector< R3Pt > aptAtoms, aptBackbone, aptSrf;
string str, str1, str2, str3;
int iN1, iN2;
double d1, d2, d3;
char strLine[1025];
double dMinDist = 1e30;
while (!in.eof() ) {
in >> str;
if ( !str.compare(0, 4, "ATOM") ) {
in >> iN1 >> str1 >> str2 >> str3 >> iN2 >> d1 >> d2 >> d3;
ASSERT( in.good() );
bool bExists = false;
const R3Pt pt( d1, d2, d3 );
for ( int i = 0; i < aptAtoms.size(); i++ ) {
if ( ApproxEqual( aptAtoms[i], pt ) ) {
bExists = true;
break;
} else {
dMinDist = WINmin( dMinDist, Length( aptAtoms[i] - pt ) );
}
}
if ( bExists == true ) {
cout << "Duplicate " << pt << "\n";
} else {
aptAtoms.push_back( pt );
}
} else if ( !str.compare(0, 6, "HETATM") ) {
in >> iN1 >> str1 >> str2 >> str3 >> iN2 >> d1 >> d2 >> d3;
aptBackbone.push_back( R3Pt( d1, d2, d3 ) );
ASSERT( in.good() );
} else {
in.getline( strLine, 1025, "\n");
}
}
in.close();
double dAvgDist = 0.0;
for ( int i = 0; i < aptAtoms.size(); i++ ) {
double dClosest = 1e30;
for ( int j = 0; j < aptAtoms.size(); j++ ) {
if ( i != j )
dClosest = WINmin( dClosest, Length( aptAtoms[i] - aptAtoms[j] ) );
}
dAvgDist += dClosest;
}
dAvgDist /= (double) aptAtoms.size();
cout << "Found " << aptAtoms.size() << " atoms and " << aptBackbone.size() << " backbone atoms, min distance " << dMinDist << " avg dist " << dAvgDist << "\n";
std::vector< pair< R3Pt, R3Vec > > aptnorm;
Array< R3Vec > avecSamples;
UTILSIcosahedralSampler icosa;
icosa.GetSamples( in_iSampling, avecSamples );
cout << "Using " << avecSamples.num() << " sphere samples\n";
UTILSDotArray dots;
int iInside = 0;
for ( int i = 0; i < aptAtoms.size(); i++ ) {
bool bFirstNorm = false;
for ( int j = 0; j < avecSamples.num(); j++ ) {
bool bIsInside = false;
const R3Pt pt = aptAtoms[i] + avecSamples[j] * in_dRadius;
for ( int k = 0; k < aptAtoms.size(); k++ ) {
if ( i != k ) {
if ( Length( aptAtoms[k] - pt ) < in_dRadius ) {
bIsInside = true;
iInside++;
break;
}
}
}
if ( bIsInside == false ) {
if ( bFirstNorm == true ) {
dots.Add( pt, avecSamples[j] * in_dRadius, UTILSColor::GREEN );
aptnorm.push_back( std::pair< R3Pt, R3Vec >( pt, avecSamples[j] ) );
} else {
dots.Add( pt, UTILSColor::YELLOW );
aptSrf.push_back( pt );
}
bFirstNorm = true;
} else {
//dots.Add( pt, UTILSColor::GREY );
}
}
dots.Add( aptAtoms[i], UTILSColor::RED );
}
dots.Write( ( in_strMesh + "_dots.txt" ).c_str() );
cout << "Inside " << iInside << " of " << aptAtoms.size() * avecSamples.num() << " kept " << aptnorm.size() << " and " << aptSrf.size() << "\n";
ofstream out( ( in_strMesh + "_surface.txt" ).c_str(), ios::out );
for ( int i = 0; i < aptnorm.size(); i++ ) {
out << aptnorm[i].first << " " << aptnorm[i].second << "\n";
}
out.close();
ofstream outII( ( in_strMesh + "_surfaceII.txt" ).c_str(), ios::out );
for ( int i = 0; i < aptSrf.size(); i++ ) {
outII << aptSrf[i] << "\n";
}
outII.close();
}
*/
static void CheckMesh( const char *in_strIn, const char *in_strOut, const bool in_bTriangulate, const bool in_bPatchBig, const int in_iNMaxPatch )
{
PMeshLite mesh;
mesh.Read( in_strIn );
int count = 0;
while ( count++ < 3 ) {
const bool bCheck = MeshSimplification::CheckMesh( mesh, in_bPatchBig, in_iNMaxPatch );
const bool bTri = mesh.MakeTriangular();
if ( !bCheck || bTri ) {
cout << "\nFixed problems, trying again\n";
mesh.Write( in_strOut );
mesh.Clear();
mesh.Read( in_strOut );
} else {
break;
}
}
mesh.Write( in_strOut );
}
static void EdgeCollapse( const char *in_strIn, const char *in_strOut, const double in_dEdgeSize )
{
PMeshLite mesh;
mesh.Read( in_strIn );
int count = 0;
Array<Edge> aeCollapse;
cout << "Input mesh " << in_strIn << " edge length " << in_dEdgeSize << "\n";
while ( count++ < 3 ) {
double dMin = 1e30, dAvg = 0.0;
ForMeshEdge( mesh.Mesh(), e ) {
const double dLen = mesh.GetEdgeLength(e);
dAvg += dLen;
dMin = WINmin( dMin, dLen );
if ( dLen < in_dEdgeSize ) {
aeCollapse += e;
}
}EndFor;
cout << "Found " << aeCollapse.num() << " edges, min " << dMin << " avg " << dAvg / (double) mesh.NumEdges() << "\n";
if ( aeCollapse.num() == 0 ) {
break;
}
for ( int i = 0; i < aeCollapse.num(); i++ ) {
if ( mesh.Valid( aeCollapse[i] ) && mesh.IsOkToCollapse( aeCollapse[i] ) ) {
mesh.EdgeCollapse( aeCollapse[i] );
}
}
aeCollapse.need(0);
mesh.Renumber();
}
count = 0;
while ( count++ < 3 ) {
if ( MeshSimplification::CheckMesh( mesh, false ) )
break;
}
mesh.Write( in_strOut );
}
static void AngleFix( const char *in_strIn, const char *in_strOut, const double in_dAngSize )
{
PMeshLite mesh;
mesh.Read( in_strIn );
int count = 0;
Array<Face> afFix;
Array<Edge> aeFlip;
cout << "Input mesh " << in_strIn << " angle size " << in_dAngSize << "\n";
R3Polygon poly;
while ( count++ < 3 ) {
double dMin = 1e30, dAvg = 0.0;
double dAngsFound = 0.0;
ForMeshFace( mesh.Mesh(), f ) {
mesh.GetFacePolygon(f, poly);
int iBig = 0;
double dBig = 0.0;
bool bReplace = false;
for ( int i = 0; i < poly.Num_pts(); i++ ) {
const double dDot = Dot( UnitSafe(poly.PtWrap(i-1) - poly[i]), UnitSafe(poly.PtWrap(i+1) - poly[i]) );
const double dAng = acos( WINminmax( dDot, -1.0 + RNEpsilon_d, 1.0 - RNEpsilon_d ) );
dAvg += dAng;
dMin = WINmin( dMin, dAng );
dAngsFound += 1.0;
if ( dAng > dBig ) {
dBig = dAng;
iBig = i;
}
if ( dAng < in_dAngSize ) {
bReplace = true;
}
}
if ( bReplace == true ) {
afFix += f;
aeFlip += mesh.FaceOppositeEdge( f, mesh.FaceVertices(f)[iBig] );
}
}EndFor;
cout << "Found " << afFix.num() << " faces, min " << dMin << " avg " << dAvg / dAngsFound << "\n";
if ( afFix.num() == 0 ) {
break;
}
for ( int i = 0; i < afFix.num(); i++ ) {
if ( mesh.Valid( aeFlip[i] ) && mesh.IsOkToSwap( aeFlip[i] ) && aeFlip.index( aeFlip[i] ) == i ) {
mesh.EdgeSwap( aeFlip[i] );
}
}
afFix.need(0);
aeFlip.need(0);
}
count = 0;
while ( count++ < 3 ) {
if ( MeshSimplification::CheckMesh( mesh, false ) )
break;
}
mesh.Write( in_strOut );
}
typedef enum {
EXPONENTIALMAP,
EXPONENTIALMAPII,
GROWDISK,
MULTIDISK
} DiskType;
static void ExponentialMap( const std::string &in_strMesh,
const std::string &in_strOptionalBoundariesFile,
const std::string &in_strOutput,
const int in_iVertex,
const double in_dStopGrowBBox,
const DiskType in_dType
)
{
PMeshLite mesh;
mesh.Read( in_strMesh.c_str() );
if ( in_iVertex < 0 || in_iVertex >= mesh.NumVertices() ) {
cerr << "ERR ExponentialMap, expected vertex in range 0 to " << mesh.NumVertices()-1 << ", got " << in_iVertex << "\n";
}
if ( in_dStopGrowBBox < 0.0 ) {
cerr << "ERR ExponentialMap, expected eps grow to be positive, got " << in_dStopGrowBBox << "\n";
}
boost::dynamic_bitset<> abNoCross(mesh.NumVertices() );
Array< std::pair<Vertex,R2Pt> > avPtDisk;
abNoCross.reset();
ifstream in( in_strOptionalBoundariesFile.c_str(), ios::in );
int iI = 0;
if ( in.good() ) {
int iIndex = 0;
while ( in.good() && iIndex < mesh.NumVertices() ) {
in >> iI;
if ( iI == 1 ) abNoCross.set( iIndex );
iIndex++;
}
}
in.close();
if ( abNoCross.count() ) {
cout << "Using " << abNoCross.count() << " boundary vertices\n";
}
R3Pt ptScl, ptCenter;
ptCenter = mesh.GetCenter( ptScl );
Array<Vertex> avBdry;
Array<Edge> aeBdry;
Array<Vertex> avFrom;
Array<double> adDist, adEdgeLengths;
boost::dynamic_bitset<> abInDisk( mesh.NumFaces() ), abNoCrossEs( mesh.NumEdges() ), abTreeEdges( mesh.NumEdges() );
const double dStopGrow = in_dStopGrowBBox * (ptScl[0] + ptScl[1] + ptScl[2]) / 3.0;
cout << "Computing exponential map\n";
switch ( in_dType ) {
case EXPONENTIALMAP :
mesh.ExponentialMap( mesh.VertexFromIndexId(in_iVertex), abNoCross, avPtDisk, dStopGrow ); break;
case EXPONENTIALMAPII :
mesh.ExponentialMapII( mesh.VertexFromIndexId(in_iVertex), abNoCross, avPtDisk, 1e-12, dStopGrow ); break;
case GROWDISK :
avBdry = mesh.VertexVerticesOrdered(mesh.VertexFromIndexId(in_iVertex));
for ( int iE = 0; iE < avBdry.num(); iE++ ) {
const Edge e = mesh.Mesh().queryEdge(avBdry[iE], avBdry.wrap(iE+1));
if ( e != NULL ) {
aeBdry += e;
}
}
abInDisk.reset();
ForVertexFace( mesh.Mesh(), mesh.VertexFromIndexId(in_iVertex), f ) {
abInDisk.set( f->IndexId() );
}EndFor;
adEdgeLengths = mesh.EdgeLengths();
mesh.GrowShortestPathTree( mesh.VertexFromIndexId(in_iVertex), abNoCross, adEdgeLengths, avFrom, adDist, abTreeEdges );
for ( int i = 0; i < adDist.num(); i++ ) {
if ( adDist[i] > dStopGrow ) {
abNoCross.set(i);
}
}
abNoCrossEs.reset();
for ( int i = abNoCross.find_first(); i != -1; i = abNoCross.find_next(i) ) {
ForVertexEdge( mesh.Mesh(), mesh.VertexFromIndexId( i ), e ) {
if ( abNoCross[ mesh.EdgeVertex1(e)->IndexId() ] && abNoCross[ mesh.EdgeVertex2(e)->IndexId() ] ) {
abNoCrossEs.set( e->IndexId() );
}
}EndFor;
}
while ( mesh.GrowDisk( avBdry, aeBdry, abInDisk, abNoCrossEs ) )
;
break;
default :
cerr << "ERR: ExponentialMap, no known type\n";
}
cout << "Disk\n";
PMeshLite meshProj;
mesh.MakeFlattenedMesh( avPtDisk, meshProj );
cout << "Writing projected mesh\n";
meshProj.Write( ( in_strOutput + string("meshCutout.ply") ).c_str() );
cout << "Writing disk\n";
ofstream out( ( in_strOutput + string("disk.txt" ) ).c_str(), ios::out );
out << "# Format: Indices number from 0\n";
out << "# VertexIndex s t\n";
out << "# " << avPtDisk.num() << " vertices in disk\n";
Array<int> aiMap( mesh.NumVertices() );
aiMap.fill(-1);
mesh.Color( UTILSColor::WHITE );
UTILSColor col;
for ( int i = 0; i < avPtDisk.num(); i++ ) {
out << avPtDisk[i].first->IndexId() << " " << avPtDisk[i].second << "\n";
aiMap[ avPtDisk[i].first->IndexId() ] = i;
const double dLen = ::Length( avPtDisk[i].second - R2Pt(0,0) );
if ( !RNIsZero( dLen ) ) {
const double dRatio = (dLen / dStopGrow);
col.FromHLS( 180.0 + 180.0 * atan2( avPtDisk[i].second[1], avPtDisk[i].second[0] ) / M_PI, 0.1 + 0.75 * dRatio, 1.0 );
} else {
col = UTILSColor::BLACK;
}
avPtDisk[i].first->SetColor(col);
}
out.close();
col = UTILSColor(0.25, 0.0, 0.0);
for ( int i = abNoCross.find_first(); i != -1; i = abNoCross.find_next(i) ) {
mesh.VertexFromIndexId(i)->SetColor( col );
}
mesh.Write( ( in_strOutput + string("meshColored.bm") ).c_str() );
cout << "Writing mapping\n";
out.open( ( in_strOutput + string("in_disk.txt") ).c_str(), ios::out );
out << "# Format: Is vertex in the disk? What's it's location?\n";
out << "# 1 s t (vertex is in disk)\n";
out << "# -1 0 0 (vertex is not in disk)\n";
out << "# " << avPtDisk.num() << " vertices in disk, " << mesh.NumVertices() << " vertices in mesh\n";
for ( int i = 0; i < aiMap.num(); i++ ) {
if ( aiMap[i] == -1 ) {
out << "-1 0 0\n";
} else {
out << avPtDisk[ aiMap[i] ].first->IndexId() << " " << avPtDisk[ aiMap[i] ].second << "\n";
}
}
out.close();
cout << "Done\n";
}
static void ExponentialMapMulti( const std::string &in_strMesh,
const std::string &in_strOptionalBoundariesFile,
const std::string &in_strOutput,
const std::string &in_strVertices,
const double in_dStopGrowBBox
)
{
PMeshLite mesh;
mesh.Read( in_strMesh.c_str() );
Array<Vertex> avSources;
ifstream inVs( in_strVertices.c_str(), ios::in );
while (!inVs.eof() && inVs.good()) {
int iV = -1;
inVs >> iV;
if ( iV < 0 || iV >= mesh.NumVertices() ) {
if ( !inVs.eof() ) {
cerr << "ERR ExponentialMapMulti, expected vertex in range 0 to " << mesh.NumVertices()-1 << ", got " << iV << "\n";
return;
}
} else {
avSources += mesh.VertexFromIndexId(iV);
}
}
inVs.close();
if ( in_dStopGrowBBox < 0.0 ) {
cerr << "ERR ExponentialMapMulti, expected eps grow to be positive, got " << in_dStopGrowBBox << "\n";
}
if ( avSources.num() == 0 ) {
cerr << "ERR: ExponentialMapMulti, no vertices given\n";
}
boost::dynamic_bitset<> abNoCross(mesh.NumVertices() );
Array< Array< std::pair<Vertex,R2Pt> > > aavPtDisk( avSources.num() );
abNoCross.reset();
ifstream in( in_strOptionalBoundariesFile.c_str(), ios::in );
int iI = 0;
if ( in.good() ) {
int iIndex = 0;
while ( in.good() && iIndex < mesh.NumVertices() ) {
in >> iI;
if ( iI == 1 ) abNoCross.set( iIndex );
iIndex++;
}
}
in.close();
if ( abNoCross.count() ) {
cout << "Using " << abNoCross.count() << " boundary vertices\n";
}
R3Pt ptScl, ptCenter;
ptCenter = mesh.GetCenter( ptScl );
boost::dynamic_bitset<> abInDisk( mesh.NumFaces() ), abNoCrossEs( mesh.NumEdges() ), abTreeEdges( mesh.NumEdges() );
const double dStopGrow = in_dStopGrowBBox * (ptScl[0] + ptScl[1] + ptScl[2]) / 3.0;
cout << "Computing exponential maps\n";
mesh.ExponentialMapIIMultiSource( avSources, aavPtDisk, abNoCross, 1e-12, dStopGrow );
cout << "Disks\n";
PMeshLite meshProj;
char strN[10];
UTILSColor col;
Array<R2Pt_i> aiMap( mesh.NumVertices() );
aiMap.fill( R2Pt_i(-1,-1) );
mesh.Color( UTILSColor::WHITE );
for ( int iM = 0; iM < aavPtDisk.num(); iM++ ) {
mesh.MakeFlattenedMesh( aavPtDisk[iM], meshProj );
cout << "Writing projected mesh\n";
sprintf( strN, "_%d", iM );
meshProj.Write( ( in_strOutput + string("meshCutout") + string(strN) + string(".ply") ).c_str() );
cout << "Writing disk " << iM << "\n";
ofstream out( ( in_strOutput + string("disk") + string(strN) + string(".txt" ) ).c_str(), ios::out );
out << "# Format: Indices number from 0\n";
out << "# VertexIndex s t\n";
out << "# " << aavPtDisk[iM].num() << " vertices in disk\n";
for ( int i = 0; i < aavPtDisk[iM].num(); i++ ) {
out << aavPtDisk[iM][i].first->IndexId() << " " << aavPtDisk[iM][i].second << "\n";
aiMap[ aavPtDisk[iM][i].first->IndexId() ][0] = iM;
aiMap[ aavPtDisk[iM][i].first->IndexId() ][1] = i;
const double dLen = ::Length( aavPtDisk[iM][i].second - R2Pt(0,0) );
if ( !RNIsZero( dLen ) ) {
const double dRatio = (dLen / dStopGrow);
col.FromHLS( 180.0 + 180.0 * atan2( aavPtDisk[iM][i].second[1], aavPtDisk[iM][i].second[0] ) / M_PI, 0.1 + 0.75 * dRatio, 1.0 );
} else {
col = UTILSColor::Ramp( i, 0, aavPtDisk.num(), UTILSColor::INCREASING_GREY );
}
aavPtDisk[iM][i].first->SetColor(col);
}
out.close();
}
col = UTILSColor(0.25, 0.0, 0.0);
for ( int i = abNoCross.find_first(); i != -1; i = abNoCross.find_next(i) ) {
mesh.VertexFromIndexId(i)->SetColor( col );
}
mesh.Write( ( in_strOutput + string("meshColored.bm") ).c_str() );
cout << "Writing mapping\n";
ofstream out( ( in_strOutput + string("in_disk.txt") ).c_str(), ios::out );
out << "# Format: Is vertex in the disk? What's it's location?\n";
out << "# diskId vertexDiskIndex s t (vertex is in disk)\n";
out << "# -1 -1 0 0 (vertex is not in disk)\n";
out << "# " << aavPtDisk.num() << " disks, ";
for ( int iM = 0; iM < aavPtDisk.num(); iM++ ) {
out << aavPtDisk[iM].num() << ", ";
}
out << " vertices in each disk, " << mesh.NumVertices() << " vertices in mesh\n";
for ( int i = 0; i < aiMap.num(); i++ ) {
if ( aiMap[i][0] == -1 ) {
out << "-1 -1 0 0\n";
} else {
out << aiMap[i] << aavPtDisk[ aiMap[i][0] ][ aiMap[i][1] ].second << "\n";
}
}
out.close();
}
static void CalculateVolume( const string &in_strFileIn, const string &in_strFileOut, const string &in_strFillHoles )
{
ofstream out;
// Get names of files; either single or multiple
Array<string> astrMeshes;
char fname[1024];
if ( in_strFileIn.find(".txt") != string::npos ) {
ifstream in( in_strFileIn.c_str(), ios::in );
if ( !in.good() ) {
cerr << "ERR: CalculateVolume, unable to open file " << in_strFileIn << "\n";
return;
}
while (!in.eof()) {
in >> fname;
astrMeshes += fname;
}
in.close();
} else {
astrMeshes += in_strFileIn;
}
// Open file to write to, if need be
bool bOutputFile = true;
if ( in_strFileOut.compare("none") != 0 ) {
out.open( in_strFileOut.c_str(), ios::out );
if ( !out.good() ) {
cerr << "ERR: CalculateVolume, unable to write to file " << in_strFileOut << "\n";
return;
}
out.precision(14);
bOutputFile = true;
} else {
cout.precision(14);
bOutputFile = false;
}
// Fill holes, yes no
bool bFillHoles = false;
if ( in_strFillHoles.compare(0, 1, "t") == 0 || in_strFillHoles.compare(0, 1, "T") == 0 ) {
bFillHoles = true;
}
// Now read in mesh, calculate area
PMeshLite mesh;
R3Pt ptScale;
for ( int i = 0; i < astrMeshes.num(); i++ ) {
if ( bOutputFile ) {
cout << "Mesh: " << astrMeshes[i] << "\n";
cout.flush();
}
mesh.Read( astrMeshes[i].c_str() );
if ( bFillHoles ) {
mesh.PatchHoles( true );
}
mesh.MakeTriangular();
const R3Pt ptCenter = mesh.GetCenter( ptScale );
mesh.Transform( R4Matrix::Translation( R3Pt(0,0,0) - ptCenter ) );
const double dVol = mesh.GetVolume();
if ( bOutputFile ) {
out << astrMeshes[i] << " " << dVol << "\n";
} else {
cout << astrMeshes[i] << " " << dVol << "\n";
}
}
if ( bOutputFile ) {
out.close();
}
}
extern void DoAllMedialAxes();
static void CopyVertexData( const string &in_strOrig, const string &in_strCopyVs, const string &in_strOut )
{
PMeshLite meshOrig, meshCopy;
meshOrig.Read( in_strOrig.c_str() );
meshCopy.Read( in_strCopyVs.c_str() );
ForMeshVertex( meshOrig.Mesh(), v ) {
Vertex vCopy = meshCopy.VertexFromIndexId(v->IndexId());
v->SetLoc( vCopy->GetLoc() );
}EndFor;
meshOrig.Write( in_strOut.c_str() );
}
static void RemoveDanglingVertices( const string &in_strOrig, const string &in_strOut, const string &in_strOutVMap )
{
PMeshLite mesh;
ifstream inVs;
string strFixVsFile( in_strOrig );
if (strFixVsFile.find_last_of( "." ) != string::npos ) {
strFixVsFile.erase( strFixVsFile.find_last_of( "." ), string::npos );
strFixVsFile = strFixVsFile + string(".txt");
inVs.open( strFixVsFile.c_str(), ios::in );
}
string strFixVsFileOut( in_strOut );
if (strFixVsFileOut.find_last_of( "." ) != string::npos ) {
strFixVsFileOut.erase( strFixVsFileOut.find_last_of( "." ), string::npos );
strFixVsFileOut = strFixVsFileOut + string(".txt");
}
mesh.Read( in_strOrig.c_str() );
Array<int> aiVs;
mesh.RemoveDanglingVertices( aiVs );
mesh.Write( in_strOut.c_str() );
Array<bool> aiPaint( mesh.NumVertices() );
aiPaint.fill(false);
int iUsed;
if ( inVs.good() ) {
for ( int i = 0; i < aiVs.num(); i++ ) {
inVs >> iUsed;
if ( iUsed && aiVs[i] != -1 ) {
aiPaint[ aiVs[i] ] = true;
}
}
inVs.close();
ofstream out( strFixVsFileOut.c_str(), ios::out );
for ( int i = 0; i < aiPaint.num(); i++ ) {
if ( aiPaint[i] ) {
out << "1 ";
} else {
out << "0 ";
}
}
out.close();
}
if ( in_strOutVMap.compare("none") ) {
ofstream outMap( in_strOutVMap.c_str(), ios::out );
for ( int i = 0; i < aiVs.num(); i++ ) {
outMap << aiVs[i] << "\n";
}
outMap.close();
}
}
static void RunAll()
{
static char dir[] = "/Users/cmg/Dropbox/BatMeshes/Noseleaves/Meshes/";
static char fnamesNoses[28][100] = {"amecen/amecen001n",
"anogeo/anogeo001n",
"artjam/artjam001n",
"bracav/bracav001n",
"chivil/chivil001n",
"chraur/chraur001n",
"desrot/desrot001n",
"diayou/diayou001n",
"erosez/erosez001n",
"lepniv/lepniv002n",
"lonori/lonori001n",
"lontho/lontho001n",
"macmac/macmac001n",
"mimcre/mimcre001n",
"phydis/phydis001n",
"phyhas/phyhas001n",
"plahel/plahel001n",
"sphtox/sphtox001n",
"tonsil/tonsil001n",
"tracir/tracir001n",
"urobil/urobil001n",
"vambid/vambid001n",
"vamspe/vamspe001n"};
for ( int i = 0; i < 28; i++ ) {
const string strOrig = string(dir) + string(fnamesNoses[i]) + string("_fit_v1.ply");
const string strOut = string(dir) + string(fnamesNoses[i]) + string("_smoothed_v1.ply");
const string strCopy = string(dir) + string(fnamesNoses[i]) + string("_smoothed_v0.ply");
CopyVertexData(strOrig, strCopy, strOut );
}
}
int main( int argc, char **argv )
{
PMeshLite mesh;
const char cCommand = (argc > 1 ) ? argv[1][0] : 'h';
DiskType dType = EXPONENTIALMAP;
switch (cCommand) {
case 'z' : {
cout << "Sepcific to processing chicken hearts. If you're not running chicken hearts\n";
DoAllMedialAxes();
break;
}
case 'B' : {
cout << "Specific to processing bat ears. If you're not running bat ears, this is not a good idea\n";
cout << "batname inputparams outputparams\n";
if ( argc < 5 ) {
cerr << "ERR: Not enough parameters, not running\n";
} else {
Bats( argv[2], argv[3], argv[4] );
}
break;
}
case 'b' : {
cout << "Bounding box of mesh\n";
if ( argc < 3 ) {
cerr << "ERR: Not enough parameters - need name of mesh\n";
} else {
mesh.Read( argv[2] );
R3Pt ptCenter, ptScale;
ptCenter = mesh.GetCenter(ptScale);
cout << "Mesh " << argv[2] << " Center " << ptCenter << " scale " << ptScale << "\n";
}
break;
}
case 'q' : {
cout << "If using qslim, you will need to install the qslim program http://mgarland.org/software/qslim21.html\n";
if ( argc == 9 ) {
MeshSimplification::QSlim( argv[2], argv[3], argv[4], argv[5][0] == 't' ? true : false, atof( argv[6] ), atof( argv[7] ), atoi( argv[8] ) );
} else if ( argc == 8 ) {
MeshSimplification::QSlim( argv[2], argv[3], argv[4], argv[5][0] == 't' ? true : false, atof( argv[6] ), atof( argv[7] ), 10 );
} else if ( argc == 7 ) {
MeshSimplification::QSlim( argv[2], argv[3], argv[4], argv[5][0] == 't' ? true : false, atof( argv[6] ), 0.01, 10 );
} else if ( argc == 6 ) {
MeshSimplification::QSlim( argv[2], argv[3], argv[4], argv[5][0] == 't' ? true : false, 0.005, 0.01, 10 );
} else if ( argc == 5 ) {
MeshSimplification::QSlim( argv[2], argv[3], argv[4], false, 0.005, 0.05, 10 );
} else {
cerr << "ERR: Not enough arguments to QSlim; quitting. Run with no arguments to see parameters.\n";
}
break;
}
case 'Q' : {
cout << "If using qslim, you will need to install the qslim program http://mgarland.org/software/qslim21.html\n";
if ( argc >= 7 ) {
const bool bBigPatch = (argc == 8) ? (argv[7][0] == 't' ? true : false) : true;
if ( argv[5][0] == 'f' ) {
cout << "Target is number of faces\n";
MeshSimplification::QSlimTargetFaces( argv[2], argv[3], argv[4], bBigPatch, atoi( argv[6] ) );
} else if ( argv[5][0] == 'p' ) {
cout << "Target is percentage of original mesh\n";
const double dPerc = atof( argv[6] );
if ( dPerc <= 0.0 || dPerc >= 1.0 ) {
cout << "Bad target percentage; should be between 0 and 1 " << dPerc << "\n";
} else {
MeshSimplification::QSlimTargetPercentage( argv[2], argv[3], argv[4], bBigPatch, atof( argv[6] ) );
}
} else {
cout << "Need t or f (argv[5][0]=" << argv[5][0] << ")\n";
}
} else {
cerr << "ERR: Not enough arguments to QSlim; quitting. Run with no arguments to see parameters.\n";
}
break;
}
case 'c' : {
if ( argc == 4 ) {
CheckMesh( argv[2], argv[3], true, false, -1);
} else if ( argc == 5 ) {
CheckMesh( argv[2], argv[3], argv[4][0] == 't' ? true : false, false, -1 );
} else if ( argc == 6 ) {
CheckMesh( argv[2], argv[3], argv[4][0] == 't' ? true : false, argv[5][0] == 't' ? true : false, -1 );
} else if ( argc == 7 ) {
CheckMesh( argv[2], argv[3], argv[4][0] == 't' ? true : false, argv[5][0] == 't' ? true : false, atoi(argv[6]) );
} else {
cerr << "ERR Wrong number of arguments to CheckMesh: quitting. Run with no arguments to see parameters.\n";
}
break;
}
case 'e' : {
if ( argc == 5 ) {
EdgeCollapse( argv[2], argv[3], atof(argv[4] ) );
} else if ( argc == 4 ) {
EdgeCollapse( argv[2], argv[3], 1e-12 );
} else {
cout << "Wrong number of arguments to edge collapse: quitting. Run with no arguments to see parameters.\n";
}
break;
}
case 'a' : {
if ( argc == 5 ) {
AngleFix( argv[2], argv[3], atof(argv[4] ) );
} else if ( argc == 4 ) {
AngleFix( argv[2], argv[3], 1e-2 );
} else {
cout << "Wrong number of arguments to angle fix: quitting. Run with no arguments to see parameters.\n";
}
break;
}
case 'n' : {
if ( argc == 5 ) {
AddNoise( argv[2], argv[3], atof(argv[4] ) );
} else if ( argc == 4 ) {
AddNoise( argv[2], argv[3], 1e-1 );
} else {
cout << "Wrong number of arguments to add noise: quitting. Run with no arguments to see parameters.\n";
}
break;
}
case 's' : {
if ( argc == 6 ) {
Smooth( argv[2], argv[3], atoi( argv[4] ), atof(argv[5] ) );
} else if ( argc == 5 ) {
Smooth( argv[2], argv[3], atoi( argv[4] ), 1.0 );
} else if ( argc == 4 ) {
Smooth( argv[2], argv[3], 3, 1.0 );
} else {
cout << "Wrong number of arguments to smooth: quitting. Run with no arguments to see parameters.\n";
}
break;
}
case 'S' : {
if ( argc == 7 ) {
SmoothSubset( argv[2], argv[3], argv[4], atoi( argv[5] ), atof(argv[6] ) );
} else if ( argc == 6 ) {
SmoothSubset( argv[2], argv[3], argv[4], atoi( argv[4] ), 1.0 );
} else if ( argc == 5 ) {
SmoothSubset( argv[2], argv[3], argv[4], 3, 1.0 );
} else {
cout << "Wrong number of arguments to smooth: quitting. Run with no arguments to see parameters.\n";
}
break;
}
case 'S' : {
if ( argc == 7 ) {
SmoothSubset( argv[2], argv[3], argv[4], atoi( argv[5] ), atof(argv[6] ) );
} else if ( argc == 6 ) {
SmoothSubset( argv[2], argv[3], argv[4], atoi( argv[4] ), 1.0 );
} else if ( argc == 5 ) {
SmoothSubset( argv[2], argv[3], argv[4], 3, 1.0 );
} else {
cout << "Wrong number of arguments to smooth: quitting\n";
}
break;
}
case 'C' : {
if ( argc != 4 ) {
cout << "Wrong number of arguments to convert, expected C filein fileout: quitting\n";
} else {
cout << "Converting " << argv[2] << " to " << argv[3] << "\n";
std::string strIn(argv[2]), strOut(argv[3]);
std::transform(strIn.begin(), strIn.end(), strIn.begin(), ::toupper);
std::transform(strOut.begin(), strOut.end(), strOut.begin(), ::toupper);
if ( strIn.find(".OBJ") != string::npos && strOut.find(".PLY") != string::npos ) {
obj2ply( argv[2], argv[3], false );
} else {
mesh.Read( argv[2] );
mesh.Write( argv[3] );
}
}
break;
}
case 'R' : {
if ( argc != 4 ) {
cout << "Wrong number of arguments to reverse faces, expected R filein fileout: quitting\n";
} else {
cout << "Reversing faces of " << argv[2] << ", writing to " << argv[3] << "\n";
std::string strIn(argv[2]), strOut(argv[3]);
std::transform(strIn.begin(), strIn.end(), strIn.begin(), ::toupper);
std::transform(strOut.begin(), strOut.end(), strOut.begin(), ::toupper);
mesh.Read( argv[2], true );
mesh.Write( argv[3] );
}
break;
}
case 'd' : {
if ( argc < 6 ) {
cerr << "ERR: Too few arguments to disk growing, quitting. Run with no arguments to see parameters.\n";
} else {
if ( strlen(argv[1]) > 1 ) {
if ( argv[1][1] == '1' ) {
dType = EXPONENTIALMAP;
} else if ( argv[1][1] == '2' ) {
dType = EXPONENTIALMAPII;
} else if ( argv[1][1] == 'd' ) {
dType = GROWDISK;
} else if ( argv[1][1] == 'm' ) {
dType = MULTIDISK;
ExponentialMapMulti( string(argv[2]), argc < 7 ? string("") : string(argv[6]),
string( argv[3] ),
string( argv[5] ),
atof( argv[4] ));
}
}
if ( dType != MULTIDISK ) {
const int iVertex = atoi( argv[5] );
const double dStopGrow = atof( argv[4] );
ExponentialMap( string( argv[2] ), argc < 7 ? string("") : string(argv[6]),
string( argv[3] ),
iVertex, dStopGrow, dType
);
}
}
break;
}
case 'V' : {
if ( argc < 4 ) {
cerr << "ERR: Too few arguments to volume calculation, quitting. Run with no arguments to see parameters.\n";
} else {
CalculateVolume( string(argv[2]),
argc < 4 ? string("none") : string(argv[3]),
argc < 5 ? string("f") : string(argv[4]) );
}
break;
}
case 'i' : {
if (argc < 3) {
cerr << "ERR: Too few arguments to removing isolated vertices, quitting. Run with no arguments to see parameters.\n";
} else {
RemoveDanglingVertices( string(argv[2]),
argc < 4 ? string(argv[2]) : string(argv[3]),
argc < 5 ? string("none") : string(argv[4]) );
}
break;
}
case 'p' : {
if ( argc < 6 ) {
cerr << "ERR: Too few arguments to projection, quitting. Run with no arguments to see parameters.\n";
} else {
ProjectMeshOnMesh( string(argv[2]), string(argv[3]), string(argv[4]),
argc < 6 ? std::string() : string( argv[5] ),
argc < 7 ? 0.3 : atof(argv[6]),
argc < 8 ? 3 : floor( atof( argv[7] ) ) );
}
break;
}
case 'h' :
case 'H' :
default : {
cout << "Command not recognized\n\n";
cout << "Parameter options\n";
cout << "QSlim binary search\n";
cout << "q filenameData filenameInitialMesh filenameOutputMesh t/f avgPerc maxPerc maxCount\n";
cout << " t/f is whether or not to triangulate big holes, default true\n";
cout << " avgPerc/maxPerc are percentage of bounding box, default 0.005, 0.01, and 10\n\n";
cout << "QSlim target faces\n";
cout << "Q filenameData filenameInitialMesh filenameOutputMesh f/p NFaces/percentage t/f\n";
cout << " f/p: f is target number of faces, p is target percentage of original mesh\n";
cout << " NFaces/percentage: NFaces if f, percentage if p\n";
cout << " t/f is whether or not to triangulate big holes, default true\n\n";
cout << "Check mesh, fixing small topological/manifold mesh problems\n";
cout << "c filenameIn filenameOut t/f t/f nToPatch \n";
cout << " the first t/f is whether or not to ensure a triangulation, default t\n";
cout << " the second t/f is whether or not to patch big holes with a vertex, default f\n";
cout << " nToPatch is the maximum size hole to patch with a vertex. Default is -1, which will patch all holes\n";
cout << " filenameIn and filenameOut can be the same\n\n";
cout << "Collapse any small edges\n";
cout << "e filenameIn filenameOut minEdgeSize\n";
cout << " minEdgeSize is in absolute terms, default 1e-12\n";
cout << " filenameIn and filenameOut can be the same\n\n";
cout << "Try to fix angles that are very small\n";
cout << "a filenameIn filenameOut minAngleSize\n";
cout << " minAngleSize is in radians, default 1e-2\n";
cout << " filenameIn and filenameOut can be the same\n\n";
cout << "Print bounding box of mesh\n";
cout << "b filenameIn\n";
cout << "Add Gaussian noise\n";
cout << "n filenameIn filenameOut noise\n";
cout << " noise is in percentage of bounding box\n";
cout << " filenameIn and filenameOut can be the same\n\n";
cout << "Smooth. Applies Laplacian smoothing using Desbrun's cotan weights. Does volume preservation\n";
cout << " In general, more loops with percMove smaller will result in better volume preservation\n";
cout << "s filenameIn filenameOut loops percMove\n";
cout << "S filenameIn filenameOut vertexListToMove loops percMove\n";
cout << " vertexListToMove is an optional parameter that selects which vertices to move\n";
cout << " format is just a text file with zeros for vertices that don't move, ones for vertices that do (see MeshViewer_Lite)\n";
cout << " Loops is number of times to apply smoothing (default 3)\n";
cout << " percMove is the percentage to move to centroid, range 0-1 (default 1.0) \n";
cout << " filenameIn and filenameOut can be the same\n\n";
cout << "Convert/save as different mesh type\n";
cout << "C filenamein filenameout\n";
cout << " input types: off, ply, iv, obj, m (ours), bm (ours binary)\n";
cout << " output types: off, ply, iv, m, bm\n\n";
cout << "Reverse the orientation of the faces\n";
cout << "R filenamein filenameout\n";
cout << " input types: off, ply, iv, m (ours), bm (ours binary)\n";
cout << " output types: off, ply, iv, m, bm\n\n";
cout << "Cut a disk out of the mesh\n";
cout << "d filenamein filenameout percBBoxStop vertex filenameBoundaries \n";
cout << " filenamein: input types: off, ply, iv, m (ours), bm (ours binary)\n";
cout << " filenameout: Stub file/directory name for output files, which are:\n";
cout << " meshCutout.ply: The mesh piece cut out and flattened to the plane\n";
cout << " meshColored.ply: The original mesh colored by the disk\n";
cout << " disk.txt: A list of the vertices in the disk and their locations\n";
cout << " in_disk.txt: All vertices and whether or not they are in their disk (same order as mesh)\n";
cout << " percBBoxStop: When to stop growing the disk, as a percentage of the bounding box\n";
cout << " vertex: Index of the vertex to start at. Can use MeshViewer_Lite to pick the vertex (it writes the id to the command line)\n";
cout << " filenameBoundaries: (optional) a list of vertices not to cross. Format is:\n";
cout << " 1 0 1 1 1 etc, where 1 means don't cross.\n";
cout << " Can use MeshViewer_Lite to paint the boundaries\n\n";
cout << "Calculate the volume of a mesh or set of meshes\n";
cout << "V filenamein filenameout fillHoles(t/f)\n";
cout << " input types: off, ply, iv, m (ours), bm (ours binary)\n";
cout << " if filenameout ends in .txt, then it will read all of the filenames in the file\n";
cout << " and calculate the volume for each of them\n";
cout << " outputs the volume to the text file filenameout. If no filename given, or filename is none, writes it to stdout\n";
cout << " fillHoles is either t or f (defaults to f if not set). This will try to patch any boundary holes.\n\n";
cout << "Remove isolated vertices\n";
cout << "i meshIn meshOut renumberOut\n";
cout << " input types: off, ply, iv, m (ours), bm (ours binary)\n";
cout << " renumberOut: (optional) text file to write vertex renumber mapping to\n";
cout << " meshIn and meshOut can be the same\n\n";
cout << "Project the first mesh onto the second\n";
cout << "p meshToProject meshToProjectOn meshOut vsMove PercMove Iterations\n";
cout << " input types: off, ply, iv, m (ours), bm (ours binary)\n";
cout << " vsMove is a text file; Format 1 0 0 1 etc for vertices to move (see MeshViewer_Lite)\n";
cout << " PercMove is how far to move to the other mesh (0 to 1, default 0.3)\n";
cout << " Iterations is how often to iterate. Does one iteration of smoothing between three moves. Default is 3\n\n";
break;
}
}
return 0;
}
///Library/Frameworks/Intel_MKL.framework/Versions/Current/lib/32