Rev 566 | Rev 572 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* MeshEdit is a small application which allows you to load and edit a mesh.
* The mesh will be stored in GEL's half edge based Manifold data structure.
* A number of editing operations are supported. Most of these are accessible from the
* console that pops up when you hit 'esc'.
*
* Created by J. Andreas Bærentzen on 15/08/08.
* Copyright 2008 __MyCompanyName__. All rights reserved.
*
*/
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <GL/glew.h>
#include <GLConsole/GLConsole.h>
#include <CGLA/eigensolution.h>
#include <CGLA/Vec2d.h>
#include <CGLA/Vec3d.h>
#include <CGLA/Mat3x3d.h>
#include <CGLA/Mat2x2d.h>
#include <CGLA/Mat2x3d.h>
#include <LinAlg/Matrix.h>
#include <LinAlg/Vector.h>
#include <LinAlg/LapackFunc.h>
#include <GLGraphics/gel_glut.h>
#include <HMesh/Manifold.h>
#include <HMesh/AttributeVector.h>
#include <HMesh/mesh_optimization.h>
#include <HMesh/curvature.h>
#include <HMesh/triangulate.h>
#include <HMesh/flatten.h>
#include <HMesh/dual.h>
#include <HMesh/load.h>
#include <HMesh/quadric_simplify.h>
#include <HMesh/smooth.h>
#include <HMesh/x3d_save.h>
#include <HMesh/obj_save.h>
#include <HMesh/off_save.h>
#include <HMesh/mesh_optimization.h>
#include <HMesh/triangulate.h>
#include <HMesh/close_holes.h>
#include <HMesh/caps_and_needles.h>
#include <HMesh/refine_edges.h>
#include <HMesh/subdivision.h>
#include <Util/Timer.h>
#include <Util/ArgExtracter.h>
#include "polarize.h"
#include "harmonics.h"
#include "VisObj.h"
using namespace std;
using namespace HMesh;
using namespace Geometry;
using namespace GLGraphics;
using namespace CGLA;
using namespace Util;
using namespace LinAlg;
using namespace CVarUtils;
inline VisObj& get_vis_obj(int i)
{
static VisObj vo[9];
return vo[i];
}
inline VisObj& avo()
{
static int& active = CreateCVar("active_mesh",0);
return get_vis_obj(active);
}
inline Manifold& active_mesh()
{
return avo().mesh();
}
inline GLViewController& active_view_control()
{
return avo().view_control();
}
// Single global instance so glut can get access
Trie CVarTrie;
GLConsole theConsole;
////////////////////////////////////////////////////////////////////////////////
bool MyConsoleHelp(std::vector<std::string> *args)
{
theConsole.Printf("");
theConsole.Printf("----------------- HELP -----------------");
theConsole.Printf("Press ESC key to open and close console");
theConsole.Printf("Press TAB to see the available commands and functions");
theConsole.Printf("Functions are shown in green and variables in yellow");
theConsole.Printf("Setting a value: [command] = value");
theConsole.Printf("Getting a value: [command]");
theConsole.Printf("Functions: [function] [arg1] [arg2] ...");
theConsole.Printf("Entering arg1=? or arg1=help will give a description.");
theConsole.Printf("History: Up and Down arrow keys move through history.");
theConsole.Printf("Tab Completion: TAB does tab completion and makes suggestions.");
theConsole.Printf("");
theConsole.Printf("Keyboard commands (when console is not active):");
theConsole.Printf("w : switch to display.render_mode = wireframe");
theConsole.Printf("i : switch to display.render_mode = isophotes");
theConsole.Printf("r : switch to display.render_mode = reflection");
theConsole.Printf("m : switch to display.render_mode = metallic");
theConsole.Printf("g : switch to display.render_mode = glazed");
theConsole.Printf("n : switch to display.render_mode = normal");
theConsole.Printf("h : switch to display.render_mode = harmonics");
theConsole.Printf("f : toggle smooth/flat shading");
theConsole.Printf("1-9 : switch between active meshes.");
theConsole.Printf("d : (display.render_mode = harmonics) diffuse light on and off");
theConsole.Printf("h : (display.render_mode = harmonics) highlight on and off ");
theConsole.Printf("+/- : (display.render_mode = harmonics) which eigenvector to show");
theConsole.Printf("q : quit program");
theConsole.Printf("ESC : open console");
theConsole.Printf("");
theConsole.Printf("Mouse: Left button rotates, middle zooms, right pans");
theConsole.Printf("----------------- HELP -----------------");
theConsole.Printf("");
return "";
}
bool wantshelp(std::vector<std::string> *args)
{
if(args->size() == 0)
return false;
string str = (*args)[0];
if(str=="help" || str=="HELP" || str=="Help" || str=="?")
return true;
return false;
}
/// Function that aligns two meshes.
bool console_align(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: align <dest> <src>");
theConsole.Printf("This function aligns dest mesh with src");
theConsole.Printf("In practice the GLViewController of src is copied to dst.");
theConsole.Printf("both arguments are mandatory and must be numbers between 1 and 9.");
theConsole.Printf("Note that results might be unexpexted if the meshes are not on the same scale");
return "";
}
int dest = 0;
if(args->size()>0){
istringstream a0((*args)[0]);
a0 >> dest;
--dest;
if(dest <0 || dest>8)
return "dest mesh out of range (1-9)";
}
else return "neither source nor destination mesh?!";
int src = 0;
if(args->size()>1){
istringstream a1((*args)[1]);
a1 >> src;
--src;
if(src <0 || src>8)
return "src mesh out of range (1-9)";
}
else return "no src mesh?";
get_vis_obj(dest).view_control() = get_vis_obj(src).view_control();
return "";
}
bool console_polarize(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: polarize");
return "";
}
int divisions = 10;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> divisions;
}
avo().save_old();
float vmin, vmax;
VertexAttributeVector<float> fun;
make_height_fun(active_mesh(), fun, vmin, vmax);
polarize_mesh(active_mesh(), fun, vmin, vmax, divisions);
return "";
}
bool console_flatten(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: flatten <floater|harmonic|barycentric>");
theConsole.Printf("This function flattens a meshs with a simple boundary. It is mostly for showing mesh");
theConsole.Printf("parametrization methods. The current mesh MUST have a SINGLE boundary loop");
theConsole.Printf("This loop is mapped to the unit circle in a regular fashion (equal angle intervals).");
theConsole.Printf("All non boundary vertices are placed at the origin. Then the system is relaxed iteratively");
theConsole.Printf("using the weight scheme given as argument.");
return "";
}
avo().save_old();
WeightScheme ws = BARYCENTRIC_W;
if(args->size()>0){
if((*args)[0] == "floater")
ws = FLOATER_W;
else if((*args)[0] == "harmonic")
ws = HARMONIC_W;
else if((*args)[0] == "lscm")
ws = LSCM_W;
}
else
return "";
flatten(active_mesh(), ws);
return "";
}
bool console_save(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: save <name.x3d|name.obj> ");
return "";
}
string& file_name = (*args)[0];
if(args->size() == 1){
if(file_name.substr(file_name.length()-4,file_name.length())==".obj"){
obj_save(file_name, active_mesh());
return "";
}
else if(file_name.substr(file_name.length()-4,file_name.length())==".off"){
off_save(file_name, active_mesh());
return "";
}
else if(file_name.substr(file_name.length()-4,file_name.length())==".x3d"){
x3d_save(file_name, active_mesh());
return "";
}
return "unknown format";
}
return "usage: save <name.x3d|name.obj> ";
}
bool MyConsoleLoad( std::vector<std::string> *vArgs )
{
std::string sFile = "cvars.xml";
std::vector< std::string > vAcceptedSubstrings;
if( vArgs->size() > 0 ) {
sFile = (*vArgs)[0];
for( size_t i = 1; i < vArgs->size(); i++ )
vAcceptedSubstrings.push_back( (*vArgs)[i] );
}
theConsole.Printf("Loading file from \"%s\".", sFile.c_str() );
if( !CVarUtils::Load( sFile, vAcceptedSubstrings) )
theConsole.Printf( "Error loading file.\n" );
return "";
}
bool console_refine_edges(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: refine.split_edges <length>");
theConsole.Printf("splits edges longer than <length>; default is 0.5 times average length");
return "";
}
avo().save_old();
float thresh = 0.5f;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> thresh;
}
float avg_length = average_edge_length(active_mesh());
refine_edges(active_mesh(), thresh * avg_length);
return "";
}
bool console_refine_faces(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: refine.split_faces ");
theConsole.Printf("usage: Takes no arguments. Inserts a vertex at the centre of each face.");
return "";
}
avo().save_old();
triangulate_by_vertex_face_split(active_mesh());
return "";
}
bool console_cc_subdivide(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: refine.catmull_clark ");
theConsole.Printf("Does one step of Catmull-Clark subdivision");
return "";
}
avo().save_old();
cc_split(active_mesh(),active_mesh());
cc_smooth(active_mesh());
return "";
}
bool console_doosabin_subdivide(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: refine.doo_sabin ");
theConsole.Printf("Does one step of Doo-Sabin Subdivision");
return "";
}
avo().save_old();
cc_split(active_mesh(),active_mesh());
dual(active_mesh());
return "";
}
bool console_dual(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: dual ");
theConsole.Printf("Produces the dual by converting each face to a vertex placed at the barycenter.");
return "";
}
avo().save_old();
dual(active_mesh());
return "";
}
bool console_minimize_curvature(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: optimize.minimize_curvature <anneal>");
theConsole.Printf("Flip edges to minimize mean curvature.");
theConsole.Printf("If anneal is true, simulated annealing (slow) is used rather than a greedy scheme");
return "";
}
avo().save_old();
bool anneal=false;
if(args->size() > 0)
{
istringstream a0((*args)[0]);
a0 >> anneal;
}
minimize_curvature(active_mesh(), anneal);
avo().post_create_display_list();
return "";
}
bool console_minimize_dihedral(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: optimize.minimize_dihedral <iter> <anneal> <use_alpha> <gamma> ");
theConsole.Printf("Flip edges to minimize dihedral angles.");
theConsole.Printf("Iter is the max number of iterations. anneal tells us whether to use ");
theConsole.Printf("simulated annealing and not greedy optimization. use_alpha (default=true) ");
theConsole.Printf("means to use angle and not cosine of anglegamma (default=4) is the power ");
theConsole.Printf("to which we raise the dihedral angle");
return "";
}
avo().save_old();
int iter = 1000;
if(args->size() > 0)
{
istringstream a0((*args)[0]);
a0 >> iter;
}
bool anneal = false;
if(args->size() > 1)
{
istringstream a0((*args)[1]);
a0 >> anneal;
}
bool use_alpha = true;
if(args->size() > 2)
{
istringstream a0((*args)[2]);
a0 >> use_alpha;
}
float gamma = 4.0f;
if(args->size() > 3)
{
istringstream a0((*args)[3]);
a0 >> gamma;
}
minimize_dihedral_angle(active_mesh(), iter, anneal, use_alpha, gamma);
return "";
}
bool console_maximize_min_angle(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: optimize.maximize_min_angle <thresh> <anneal>");
theConsole.Printf("Flip edges to maximize min angle - to make mesh more Delaunay.");
theConsole.Printf("If the dot product of the normals between adjacent faces < thresh");
theConsole.Printf("no flip will be made. anneal selects simulated annealing rather ");
theConsole.Printf("nthan greedy optimization.");
return "";
}
avo().save_old();
float thresh = 0.0f;
if(args->size() > 0)
{
istringstream a0((*args)[0]);
a0 >> thresh;
}
bool anneal = false;
if(args->size() > 1)
{
istringstream a0((*args)[1]);
a0 >> anneal;
}
maximize_min_angle(active_mesh(),thresh,anneal);
return "";
}
bool console_optimize_valency(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: optimize.valency <anneal> ");
theConsole.Printf("Optimizes valency for triangle meshes. Anneal selects simulated annealing rather than greedy optim.");
return "";
}
avo().save_old();
bool anneal = false;
if(args->size() > 0)
{
istringstream a0((*args)[0]);
a0 >> anneal;
}
optimize_valency(active_mesh(), anneal);
return "";
}
bool console_analyze(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: harmonics.analyze");
theConsole.Printf("Creates the Laplace Beltrami operator for the mesh and finds all eigensolutions.");
theConsole.Printf("It also projects the vertices onto the eigenvectors - thus transforming the mesh");
theConsole.Printf("to this basis.");
theConsole.Printf("Note that this will stall the computer for a large mesh - as long as we use Lapack.");
return "";
}
avo().harmonics_analyze_mesh();
return "";
}
bool console_partial_reconstruct(std::vector<std::string> *args)
{
if(args->size() != 3)
theConsole.Printf("usage: haramonics.partial_reconstruct <e0> <e1> <s>");
if(wantshelp(args)) {
theConsole.Printf("Reconstruct from projections onto eigenvectors. The two first arguments indicate");
theConsole.Printf("the eigenvector interval that we reconstruct from. The last argument is the ");
theConsole.Printf("scaling factor. Thus, for a vertex, v, the formula for computing the position, p, is:");
theConsole.Printf("for (i=e0; i<=e1;++i) p += proj[i] * Q[i][v] * s;");
theConsole.Printf("where proj[i] is the 3D vector containing the x, y, and z projections of the mesh onto");
theConsole.Printf("eigenvector i. Q[i][v] is the v'th coordinate of the i'th eigenvector.");
theConsole.Printf("Note that if vertex coordinates are not first reset, the result is probably unexpected.");
}
avo().save_old();
if(args->size() != 3)
return "";
int E0,E1;
float scale;
istringstream a0((*args)[0]);
a0 >> E0;
istringstream a1((*args)[1]);
a1 >> E1;
istringstream a2((*args)[2]);
a2 >> scale;
avo().harmonics_partial_reconstruct(E0,E1,scale);
return "";
}
bool console_reset_shape(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: harmonics.reset_shape ");
theConsole.Printf("Simply sets all vertices to 0,0,0. Call this before doing partial_reconstruct");
theConsole.Printf("unless you know what you are doing.");
return "";
}
avo().save_old();
avo().harmonics_reset_shape();
return "";
}
bool console_close_holes(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: cleanup.close_holes");
theConsole.Printf("This function closes holes. It simply follows the loop of halfvectors which");
theConsole.Printf("enclose the hole and add a face to which they all point.");
return "";
}
avo().save_old();
close_holes(active_mesh());
return "";
}
bool console_reload(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: load <file>");
theConsole.Printf("(Re)loads the current file if no argument is given, but");
theConsole.Printf("if an argument is given, then that becomes the current file");
return "";
}
avo().save_old();
if(!avo().reload(args->size() > 0 ? (*args)[0]:""))
return "failed to load";
return "";
}
bool console_add_mesh(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: add_mesh <file>");
theConsole.Printf("Loads the file but without clearing the mesh. Thus, the loaded mesh is added to the");
theConsole.Printf("current model.");
return "";
}
avo().save_old();
if(!avo().add_mesh(args->size() > 0 ? (*args)[0]:""))
return "failed to load";
return "";
}
bool console_valid(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: validity");
theConsole.Printf("Tests validity of Manifold");
return "";
}
if(valid(active_mesh()))
theConsole.Printf("Mesh is valid");
else
theConsole.Printf("Mesh is invalid - check console output");
return "";
}
bool console_info(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: info");
theConsole.Printf("Provides information about mesh.");
return "";
}
Vec3f p0, p7;
bbox(active_mesh(), p0, p7);
stringstream bbox_corners;
bbox_corners << p0 << " - " << p7 << endl;
theConsole.Printf("Bounding box corners : %s", bbox_corners.str().c_str());
map<int,int> val_hist;
for(VertexIDIterator vi = active_mesh().vertices_begin(); vi != active_mesh().vertices_end(); ++vi)
{
int val = valency(active_mesh(), *vi);
if(val_hist.find(val) == val_hist.end())
val_hist[val] = 0;
++val_hist[val];
}
theConsole.Printf("Valency histogam");
for(map<int,int>::iterator iter = val_hist.begin(); iter != val_hist.end(); ++iter)
{
stringstream vhl;
vhl << iter->first << ", " << iter->second;
theConsole.Printf("%d, %d", iter->first, iter->second);
}
theConsole.Printf("Mesh contains %d faces", active_mesh().active_faces());
theConsole.Printf("Mesh contains %d halfedges", active_mesh().active_halfedges());
theConsole.Printf("Mesh contains %d vertices", active_mesh().active_vertices());
return "";
}
bool console_simplify(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: simplify <fraction> ");
theConsole.Printf("Performs Garland Heckbert (quadric based) mesh simplification.");
theConsole.Printf("The only argument is the fraction of vertices to keep.");
return "";
}
avo().save_old();
float keep_fraction;
if(args->size() == 0) return "you must specify fraction of vertices to keep";
istringstream a0((*args)[0]);
a0 >> keep_fraction;
Vec3f p0, p7;
bbox(active_mesh(), p0, p7);
Vec3f d = p7-p0;
float s = 1.0/d.max_coord();
Vec3f pcentre = (p7+p0)/2.0;
for(VertexIDIterator vi = active_mesh().vertices_begin(); vi != active_mesh().vertices_end(); ++vi){
active_mesh().pos(*vi) = (active_mesh().pos(*vi) - pcentre) * s;
}
cout << "Timing the Garland Heckbert (quadric based) mesh simplication..." << endl;
Timer timer;
timer.start();
//simplify
quadric_simplify(active_mesh(),keep_fraction,0.0001f,true);
cout << "Simplification complete, process time: " << timer.get_secs() << " seconds" << endl;
//clean up the mesh, a lot of edges were just collapsed
active_mesh().cleanup();
for(VertexIDIterator vi = active_mesh().vertices_begin(); vi != active_mesh().vertices_end(); ++vi)
active_mesh().pos(*vi) = active_mesh().pos(*vi)*d.max_coord() + pcentre;
return "";
}
bool console_vertex_noise(std::vector<std::string> *args)
{
if(wantshelp(args))
{
theConsole.Printf("usage: noise.perturb_vertices <amplitude>");
theConsole.Printf("adds a random vector to each vertex. A random vector in the unit cube is generated and");
theConsole.Printf("to ensure an isotropic distribution, vectors outside the unit ball are discarded.");
theConsole.Printf("The vector is multiplied by the average edge length and then by the amplitude specified.");
theConsole.Printf("If no amplitude is specified, the default (0.5) is used.");
return "";
}
avo().save_old();
float avg_length = average_edge_length(active_mesh());
float noise_amplitude = 0.5f;
if(args->size() > 0) {
istringstream a0((*args)[0]);
a0 >> noise_amplitude;
}
gel_srand(0);
for(VertexIDIterator vi = active_mesh().vertices_begin(); vi != active_mesh().vertices_end(); ++vi){
Vec3f v;
do{
v = Vec3f(gel_rand(),gel_rand(),gel_rand());
v /= (float)(GEL_RAND_MAX);
}
while(sqr_length(v) > 1.0);
v -= Vec3f(0.5);
v *= 2.0;
v *= noise_amplitude;
v *= avg_length;
active_mesh().pos(*vi) += v;
}
return "";
}
bool console_perpendicular_vertex_noise(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: noise.perturb_vertices_perpendicular <amplitude>");
theConsole.Printf("adds the normal times a random scalar times amplitude times");
theConsole.Printf("times average edge length to the vertex. (default amplitude=0.5)");
return "";
}
avo().save_old();
float avg_length = average_edge_length(active_mesh());
float noise_amplitude = 0.5;
if(args->size() > 0)
{
istringstream a0((*args)[0]);
a0 >> noise_amplitude;
}
VertexAttributeVector<Vec3f> normals(active_mesh().total_vertices());
for(VertexIDIterator vi = active_mesh().vertices_begin(); vi != active_mesh().vertices_end(); ++vi)
normals[*vi] = normal(active_mesh(), *vi);
gel_srand(0);
for(VertexIDIterator vi = active_mesh().vertices_begin(); vi != active_mesh().vertices_end(); ++vi)
{
float rval = 0.5-gel_rand() / float(GEL_RAND_MAX);
active_mesh().pos(*vi) += normals[*vi]*rval*noise_amplitude*avg_length*2.0;
}
return "";
}
bool console_noisy_flips(std::vector<std::string> *args)
{
if(wantshelp(args)){
theConsole.Printf("usage: noise.perturb_topology <iter>");
theConsole.Printf("Perform random flips. iter (default=1) is the number of iterations.");
theConsole.Printf("mostly for making nasty synthetic test cases.");
return "";
}
avo().save_old();
int iter = 1;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> iter;
}
randomize_mesh(active_mesh(), iter);
return "";
}
//bool console_bridge_faces(std::vector<std::string> *args)
//{
// avo().save_old();
//
// FaceID f0 = *(active_mesh().faces_begin());
//
// vector<pair<VertexID, VertexID> > connect(2);
//
// HalfEdgeWalker hw0 = active_mesh().halfedgewalker(f0);
//
// VertexID v;
// VertexIDIterator vid=active_mesh().vertices_begin();
// for(int i=0;i<4;++i)
// ++vid;
// v = *vid;
//
// FaceID f1 = active_mesh().merge_one_ring(v);
// HalfEdgeWalker hw1 = active_mesh().halfedgewalker(f1);
//
// for(int i=0;i<2;++i)
// {
// connect[i].first = hw0.vertex();
// connect[i].second = hw1.opp().vertex();
// hw0 = hw0.next().next();
// hw1 = hw1.next();
// }
// active_mesh().bridge_faces(f0, f1, connect);
// return "";
//}
// returns the edges first coordinate is endpoint index from A curve, second from B
// edges are in reversed order - but probably it does not matter
// values in pointsA and pointsB vectors should be sorted
static vector<Vec2i> andreas_problem_dtw(vector<double> &pointsA, vector<double> &pointsB)
{
// Hi Andreas,
// I realized that probably you do not need as much symmetry (and it is also hard to define)
// but rather to avoid skew faces.
// So as a skew measure we may choose for example the difference of middle points of this
// face on both curves. For a triangle middle point is just a vertex from one side and the middle
// of the edge from the other side, and for a quad is just a middle of two edges.
// Each new matching adds a new face (either a triangle or a quad) so a cost is just the skew
// measure of this face.
// Try this implementation and check if it suits you.
LinAlg::CMatrix DTW(pointsA.size(),pointsB.size());
LinAlg::CMatrixType<Vec2i> backtrack(pointsA.size(),pointsB.size());
DTW[0][0] = 0;
for(int i = 1; i < pointsA.size(); i++)
{
DTW[i][0] = DTW[i-1][0] + abs( (pointsA[i-1]+pointsA[i])/2 - pointsB[0] );
backtrack[i][0] = Vec2i(i-1,0);
}
for(int j = 1; j < pointsB.size(); j++)
{
DTW[0][j] = DTW[0][j-1] + abs( pointsA[0] - (pointsB[j-1]+pointsB[j])/2 );
backtrack[0][j] = Vec2i(0,j-1);
}
for(int i = 1; i < pointsA.size(); i++)
{
for(int j = 1; j < pointsB.size(); j++)
{
DTW[i][j] = DTW[i-1][j] + abs((pointsA[i-1]+pointsA[i])/2 - pointsB[j]);
backtrack[i][j] = Vec2i(i-1,j);
double costj = DTW[i][j-1] + abs(pointsA[i] - (pointsB[j-1]+pointsB[j])/2);
if(DTW[i][j] > costj)
{
DTW[i][j] = costj;
backtrack[i][j] = Vec2i(i,j-1);
}
double costij = DTW[i-1][j-1] + abs((pointsA[i-1] + pointsA[i])/2 - (pointsB[j-1] + pointsB[j])/2);
if(DTW[i][j] > 2*costij)
{
DTW[i][j] = costij;
backtrack[i][j] = Vec2i(i-1,j-1);
}
}
}
// backtracking
int acti = pointsA.size()- 1;
int actj = pointsB.size()- 1;
vector<Vec2i> toret;
toret.push_back(Vec2i(acti,actj));
while(acti > 0 || actj > 0)
{
double nacti = backtrack[acti][actj][0];
double nactj = backtrack[acti][actj][1];
toret.push_back(Vec2i(nacti,nactj));
acti = nacti;
actj = nactj;
}
return toret;
}
void bridge_one_rings(Manifold& m, VertexID vid0, VertexID vid1, Vec3f X, Vec3f Y)
{
// Obtain the one ring loop: Vertex ID's and angles in above defined coordinate system
vector<VertexID> vid0_nvec;
vector<double> vid0_navec;
HalfEdgeWalker hw0 = m.halfedgewalker(vid0);
for(;!hw0.full_circle(); hw0 = hw0.circulate_vertex_ccw())
{
VertexID nv = hw0.vertex();
vid0_nvec.push_back(nv);
Vec3f ndir = m.pos(nv)-m.pos(vid0);
vid0_navec.push_back(atan2(dot(Y,ndir),dot(X,ndir)));
if(vid0_navec.back() < 0) vid0_navec.back() += 2 * M_PI;
}
// Obtain the one ring loop for other vertex: Vertex ID's and angles in above defined coordinate system
vector<VertexID> vid1_nvec;
vector<double> vid1_navec;
HalfEdgeWalker hw1 = m.halfedgewalker(vid1);
for(;!hw1.full_circle(); hw1 = hw1.circulate_vertex_cw())
{
VertexID nv = hw1.vertex();
vid1_nvec.push_back(nv);
Vec3f ndir = m.pos(nv)-m.pos(vid1);
vid1_navec.push_back(atan2(dot(Y,ndir),dot(X,ndir)));
if(vid1_navec.back() < 0) vid1_navec.back() += 2 * M_PI;
}
// Find the closest pair of points.
int N0=vid0_nvec.size();
int N1=vid1_nvec.size();
pair<int,int> closest_pair;
double closest_dist = FLT_MAX;
for(int i=0;i<N0;++i)
for(int j=0;j<N1;++j)
{
float dist = abs(vid0_navec[i]-vid1_navec[j]);
if(dist<closest_dist)
{
closest_dist = dist;
closest_pair = pair<int,int>(i,j);
}
}
// Renumber vertices in first one ring so that the closest pair vertex is first
vector<VertexID> vid0_nvec_temp(N0);
vector<double> vid0_navec_temp(N0);
for(int i=0;i<N0;++i)
{
int j = (i+closest_pair.first)%N0;
vid0_nvec_temp[i] = vid0_nvec[j];
vid0_navec_temp[i] = vid0_navec[j] + (vid0_navec[j]<vid0_navec[closest_pair.first]?2*M_PI:0);
}
vid0_nvec = vid0_nvec_temp;
vid0_navec = vid0_navec_temp;
// Renumber vertices in second one ring so that the closest pair vertex is first
vector<VertexID> vid1_nvec_temp(N1);
vector<double> vid1_navec_temp(N1);
for(int i=0;i<N1;++i)
{
int j = (i+closest_pair.second)%N1;
vid1_nvec_temp[i] = vid1_nvec[j];
vid1_navec_temp[i] = vid1_navec[j] + (vid1_navec[j]<vid1_navec[closest_pair.second]?2*M_PI:0);
}
vid1_nvec = vid1_nvec_temp;
vid1_navec = vid1_navec_temp;
// Pair vertices in the two one rings and build input for bridge operation.
vector<Vec2i> connect_int = andreas_problem_dtw(vid0_navec, vid1_navec);
int N_conn = connect_int.size();
vector<pair<VertexID, VertexID> > connect(N_conn);
for(int i=connect_int.size()-1;i>-1; --i)
{
connect[N_conn-i-1].first = vid0_nvec[connect_int[i][0]];
connect[N_conn-i-1].second = vid1_nvec[connect_int[i][1]];
}
// Merge the two one rings producing two faces.
FaceID f0 = m.merge_one_ring(vid0);
FaceID f1 = m.merge_one_ring(vid1);
// Bridge the just created faces.
m.bridge_faces(f0, f1, connect);
}
bool console_bridge_onerings(std::vector<std::string> *args)
{
avo().save_old();
Manifold& m = active_mesh();
// Get input (vertex to bridge from).
int iter = 1;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> iter;
}
//Find the one endpoint of bridge op.
VertexIDIterator vid0=m.vertices_begin();
Vec3f norm0 = normal(m,*vid0);
for(int i=0;i<iter;++i)
++vid0;
// Find the other end point.
VertexIDIterator vid1=m.vertices_begin();
for(;vid1 != m.vertices_end();++vid1)
{
Vec3f norm1 = normal(m,*vid1);
if(dot(norm0, norm1) < - 0.9)
break;
}
// Get the X and Y axes on which to project the vectors from vertex to neighbor.
Vec3f dir = m.pos(*vid0)-m.pos(*vid1);
Vec3f X,Y;
orthogonal(dir, X, Y);
bridge_one_rings(m, *vid0, *vid1, X, Y);
return "";
}
bool console_merge_one_ring(std::vector<std::string> *args)
{
avo().save_old();
float t0=0;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> t0;
}
float t1=2.1;
if(args->size() > 1){
istringstream a0((*args)[1]);
a0 >> t1;
}
if(t0>0.0)
{
int did_work;
do
{
did_work = 0;
// cout << "merge FACES \n\n\n" <<endl;
//
vector<HalfEdgeID> heid_vec;
for(HalfEdgeIDIterator heidit = active_mesh().halfedges_begin();
heidit != active_mesh().halfedges_end(); ++heidit)
heid_vec.push_back(*heidit);
random_shuffle(heid_vec.begin(), heid_vec.end());
for(int i=0;i<heid_vec.size();++i)
if(active_mesh().in_use(heid_vec[i]))
{
HalfEdgeID heid = heid_vec[i];
HalfEdgeWalker hew = active_mesh().halfedgewalker(heid);
float A0 = area(active_mesh(), hew.face());
float A1 = area(active_mesh(), hew.opp().face());
float P0 = perimeter(active_mesh(), hew.face());
float P1 = perimeter(active_mesh(), hew.opp().face());
float l = length(active_mesh(), hew.halfedge());
if(((P0+P1-2*l)/(A0+A1)) < (0.5 *(P0/A0+P1/A1)))
{
Vec3f N0 = normal(active_mesh(),hew.face());
Vec3f N1 = normal(active_mesh(),hew.opp().face());
if(dot(N0,N1)>t0)
if(active_mesh().merge_faces(hew.face(), hew.halfedge()))
did_work++;
}
}
cout << "COLLAPSE\n\n\n" <<endl;
int did_work_2=0;
do
{
did_work_2=0;
random_shuffle(heid_vec.begin(), heid_vec.end());
for(int i=0;i<heid_vec.size();++i)
if(active_mesh().in_use(heid_vec[i]))
{
HalfEdgeID heid = heid_vec[i];
HalfEdgeWalker hew = active_mesh().halfedgewalker(heid);
if(valency(active_mesh(), hew.opp().vertex()) == 2 &&
valency(active_mesh(), hew.vertex()) > 2)
{
if(precond_collapse_edge(active_mesh(), hew.halfedge()))
{
active_mesh().collapse_edge(hew.halfedge(), false);
++did_work_2;
}
}
}
}
while(did_work_2);
// vector<VertexID> vid_vec;
// for(VertexIDIterator vidit = active_mesh().vertices_begin();
// vidit != active_mesh().vertices_end(); ++vidit)
// vid_vec.push_back(*vidit);
// random_shuffle(vid_vec.begin(), vid_vec.end());
// for(int i=0;i<vid_vec.size(); ++i)
// if(active_mesh().in_use(vid_vec[i]))
// {
// VertexID vid = vid_vec[i];
// vector<Vec3f> normals;
// Vec3f avg_norm(0);
// float area_sum = 0;
// HalfEdgeWalker hew = active_mesh().halfedgewalker(vid);
// for(;!hew.full_circle(); hew = hew.circulate_vertex_ccw())
// if(hew.face() != InvalidFaceID)
// {
// normals.push_back(normal(active_mesh(), hew.face()));
// float A = area(active_mesh(),hew.face());
// avg_norm += normals.back()/A;
// area_sum += A;
// }
// avg_norm.normalize();
// float min_dot = 1.0;
// for(int i=0;i<normals.size(); ++i)
// {
// float d = dot(avg_norm, normals[i]);
// if(d<min_dot)
// min_dot = d;
// }
// if(min_dot > t0)
// {
// FaceID fid = active_mesh().merge_one_ring(vid,t1*sqrt(M_PI*area_sum));
// if(fid != InvalidFaceID)
// did_work += 1;
// }
// }
active_mesh().cleanup();
valid(active_mesh());
}while(did_work);
}
else
{
VertexIDIterator vidit = active_mesh().vertices_begin();
active_mesh().merge_one_ring(*vidit);
}
active_mesh().cleanup();
valid(active_mesh());
return "";
}
bool console_experiment(std::vector<std::string> *args)
{
Manifold& m = active_mesh();
HalfEdgeID h0, h1;
for(HalfEdgeIDIterator hid = m.halfedges_begin(); hid != m.halfedges_end(); ++hid)
{
HalfEdgeWalker hew = m.halfedgewalker(*hid);
if(sqr_length(m.pos(hew.vertex())-Vec3f(1,0,0))<0.001 &&
sqr_length(m.pos(hew.opp().vertex())-Vec3f(1,1,0))<0.001 &&
hew.face() == InvalidFaceID)
h0 = hew.halfedge();
if(sqr_length(m.pos(hew.vertex())-Vec3f(1,1,0))<0.001 &&
sqr_length(m.pos(hew.opp().vertex())-Vec3f(1,0,0))<0.001 &&
hew.face() == InvalidFaceID)
h1 = hew.halfedge();
}
m.stitch_boundary_edges(h0,h1);
return "";
}
bool console_laplacian_smooth(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: smooth.laplacian <weight> <iter>");
theConsole.Printf("Perform Laplacian smoothing. weight is the scaling factor for the Laplacian.");
theConsole.Printf("default weight = 1.0. Default number of iterations = 1");
return "";
}
avo().save_old();
float t=1.0;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> t;
}
int iter = 1;
if(args->size()>1){
istringstream a0((*args)[1]);
a0 >> iter;
}
/// Simple laplacian smoothing with an optional weight.
for(int i=0;i<iter;++i)
laplacian_smooth(active_mesh(), t);
return "";
}
bool console_mean_curvature_smooth(std::vector<std::string> *args){
if(wantshelp(args)) {
theConsole.Printf("usage: smooth.mean_curvature <weight> <iter>");
theConsole.Printf("Perform mean curvature smoothing. weight is the scaling factor for the");
theConsole.Printf("mean curvature vector which has been normalized by dividing by edge lengths");
theConsole.Printf("this allows for larger steps as suggested by Desbrun et al.");
theConsole.Printf("default weight = 1.0. Default number of iterations = 1");
return "";
}
avo().save_old();
double t=1.0;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> t;
}
int iter=1;
if(args->size() > 1){
istringstream a0((*args)[1]);
a0 >> iter;
}
VertexAttributeVector<Vec3d> new_pos(active_mesh().total_vertices());
for(int j = 0; j < iter; ++j){
for(VertexIDIterator v = active_mesh().vertices_begin(); v != active_mesh().vertices_end(); ++v) {
Vec3d m;
double w_sum;
unnormalized_mean_curvature_normal(active_mesh(), *v, m, w_sum);
new_pos[*v] = Vec3d(active_mesh().pos(*v)) + (t * m/w_sum);
}
for(VertexIDIterator v = active_mesh().vertices_begin(); v != active_mesh().vertices_end(); ++v)
active_mesh().pos(*v) = Vec3f(new_pos[*v]);
}
return "";
}
double angle_defect(const Manifold& m, VertexID v)
{
if(boundary(m, v))
return 1000;
Vec3f vertex(m.pos(v));
vector<Vec3d> edges;
for(HalfEdgeWalker w = m.halfedgewalker(v); !w.full_circle(); w = w.circulate_vertex_cw()){
Vec3d e(normalize(m.pos(w.vertex()) - vertex));
edges.push_back(e);
}
int N=edges.size();
double angle_sum = 0;
for(int i=0;i<N;++i){
double dot_prod =
s_max(-1.0, s_min(1.0, dot(edges[i],edges[(i+1)%N])));
angle_sum += acos(dot_prod);
}
return fabs(2*M_PI - angle_sum);
}
bool console_experimental_smooth(std::vector<std::string> *args)
{
if(wantshelp(args)){
theConsole.Printf("usage: smooth.experimental <weight> <iter>");
theConsole.Printf("Perform experimental smoothing. weight is the scaling factor for the");
theConsole.Printf("experimetnal vector which has been normalized by dividing by edge lengths");
theConsole.Printf("this allows for larger steps as suggested by Desbrun et al.");
theConsole.Printf("default weight = 1.0. Default number of iterations = 1");
return "";
}
avo().save_old();
float t = 1.0f;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> t;
}
int iter = 1;
if(args->size() > 1){
istringstream a0((*args)[1]);
a0 >> iter;
}
Vec3f p0_before, p7_before;
bbox(active_mesh(), p0_before, p7_before);
double avg_area = 0.0;
for(FaceIDIterator f = active_mesh().faces_begin(); f != active_mesh().faces_end(); ++f)
avg_area += area(active_mesh(), *f);
avg_area /= active_mesh().active_faces();
for(int j = 0; j < iter; ++j){
VertexAttributeVector<Vec3d> new_pos(active_mesh().total_vertices());
for(VertexIDIterator v = active_mesh().vertices_begin(); v != active_mesh().vertices_end(); ++v) {
double w_sum=0;
Vec3d m(0);
HalfEdgeWalker walker = active_mesh().halfedgewalker(*v);
for(; !walker.full_circle(); walker = walker.circulate_vertex_cw()){
double area_left = area(active_mesh(), walker.face());
double area_right = area(active_mesh(), walker.opp().face());
double w = 0.5 * (area_left + area_right);
m += w * Vec3d(active_mesh().pos(walker.vertex()) - active_mesh().pos(*v));
w_sum += w;
}
double t2 = pow( (w_sum / walker.no_steps()) / avg_area, .25);
if(t2 > 1e-10)
new_pos[*v] = Vec3d(active_mesh().pos(*v)) + (t * t2 * m/w_sum);
}
for(VertexIDIterator v = active_mesh().vertices_begin(); v != active_mesh().vertices_end(); ++v)
active_mesh().pos(*v) = Vec3f(new_pos[*v]);
for(HalfEdgeIDIterator h = active_mesh().halfedges_begin(); h != active_mesh().halfedges_end(); ++h){
Vec3d mcna, mcnb;
double ws;
HalfEdgeWalker walker = active_mesh().halfedgewalker(*h);
unnormalized_mean_curvature_normal(active_mesh(), walker.vertex(), mcna, ws);
unnormalized_mean_curvature_normal(active_mesh(), walker.opp().vertex(), mcnb, ws);
if(sqr_length(mcna) < 1 && sqr_length(mcnb) < 1){
if(precond_collapse_edge(active_mesh(), *h))
active_mesh().collapse_edge(*h, false);
}
}
//new manifold cleanup call
active_mesh().cleanup();
maximize_min_angle(active_mesh(), 0.95f, false);
}
Vec3f p0, p7;
bbox(active_mesh(), p0, p7);
for(VertexIDIterator vi = active_mesh().vertices_begin(); vi != active_mesh().vertices_end(); ++vi)
active_mesh().pos(*vi) = (p7_before - p0_before) * (active_mesh().pos(*vi) - p0) / (p7-p0) + p0_before;
return "";
}
bool console_taubin_smooth(std::vector<std::string> *args)
{
if(wantshelp(args)){
theConsole.Printf("usage: smooth.taubin <iter>");
theConsole.Printf("Perform Taubin smoothing. iter (default=1) is the number of iterations.");
return "";
}
avo().save_old();
int iter = 1;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> iter;
}
/// Taubin smoothing is similar to laplacian smoothing but reduces shrinkage
taubin_smooth(active_mesh(), iter);
return "";
}
bool console_fvm_smooth(std::vector<std::string> *args)
{
if(wantshelp(args)){
theConsole.Printf("usage: smooth.fuzzy_vector_median <iter>");
theConsole.Printf("Smooth normals using fuzzy vector median smoothing. iter (default=1) is the number of iterations");
theConsole.Printf("This function does a very good job of preserving sharp edges.");
return "";
}
avo().save_old();
int iter=1;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> iter;
}
// Fuzzy vector median smoothing is effective when it comes to preserving sharp edges.
fvm_smooth(active_mesh(), iter);
return "";
}
bool console_triangulate(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: triangulate");
theConsole.Printf("This function triangulates all non triangular faces of the mesh.");
theConsole.Printf("you may want to call it after hole closing. For a polygon it simply connects");
theConsole.Printf("the two closest vertices in a recursive manner until only triangles remain");
return "";
}
avo().save_old();
shortest_edge_triangulate(active_mesh());
active_mesh().cleanup();
valid(active_mesh());
return "";
}
bool console_remove_caps(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: cleanup.remove_caps thresh");
theConsole.Printf("Remove caps (triangles with one very big angle). The thresh argument is the fraction of PI to");
theConsole.Printf("use as threshold for big angle. Default is 0.85. Caps are removed by flipping.");
return "";
}
avo().save_old();
float t = 0.85f;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> t;
}
remove_caps(active_mesh(), static_cast<float>(M_PI) *t);
active_mesh().cleanup();
return "";
}
bool console_remove_needles(std::vector<std::string> *args)
{
if(wantshelp(args)){
theConsole.Printf("usage: cleanup.remove_needles <thresh>");
theConsole.Printf("Removes very short edges by collapse. thresh is multiplied by the average edge length");
theConsole.Printf("to get the length shorter than which we collapse. Default = 0.1");
return "";
}
avo().save_old();
float thresh = 0.1f;
if(args->size() > 0){
istringstream a0((*args)[0]);
a0 >> thresh;
}
float avg_length = average_edge_length(active_mesh());
remove_needles(active_mesh(), thresh * avg_length);
active_mesh().cleanup();
return "";
}
bool console_undo(std::vector<std::string> *args)
{
if(wantshelp(args)) {
theConsole.Printf("usage: undo");
theConsole.Printf("This function undoes one operation. Repeated undo does nothing");
return "";
}
avo().restore_old();
return "";
}
void reshape(int W, int H)
{
active_view_control().reshape(W,H);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
static string& display_render_mode = CreateCVar<string>("display.render_mode","");
static int& display_smooth= CreateCVar<int>("display.smooth_shading",1);
static float& display_gamma = CreateCVar<float>("display.gamma", 2.2);
glPushMatrix();
//cout << display_render_mode << endl;
avo().display(display_render_mode, display_smooth, display_gamma);
glPopMatrix();
glUseProgram(0);
theConsole.RenderConsole();
glutSwapBuffers();
}
void animate()
{
//usleep( (int)1e4 );
active_view_control().try_spin();
glutPostRedisplay();
}
void mouse(int button, int state, int x, int y)
{
Vec2i pos(x,y);
if (state==GLUT_DOWN)
{
if (button==GLUT_LEFT_BUTTON)
active_view_control().grab_ball(ROTATE_ACTION,pos);
else if (button==GLUT_MIDDLE_BUTTON)
active_view_control().grab_ball(ZOOM_ACTION,pos);
else if (button==GLUT_RIGHT_BUTTON)
active_view_control().grab_ball(PAN_ACTION,pos);
}
else if (state==GLUT_UP)
active_view_control().release_ball();
}
void motion(int x, int y) {
Vec2i pos(x,y);
active_view_control().roll_ball(Vec2i(x,y));
}
void keyboard_spec(int key, int x, int y)
{
int mod = glutGetModifiers();
if( theConsole.IsOpen() ) {
// If shift held, scroll the console
if( mod == GLUT_ACTIVE_SHIFT ) {
switch (key){
case GLUT_KEY_UP:
theConsole.ScrollDownLine();
break;
case GLUT_KEY_DOWN:
theConsole.ScrollUpLine();
break;
}
} else {
theConsole.SpecialFunc( key );
}
}
}
void keyboard(unsigned char key, int x, int y)
{
if(theConsole.IsOpen())
{
//avo().save_old();
switch(key) {
case '\033':
theConsole.ToggleConsole();
default:
theConsole.KeyboardFunc(key);
break;
}
if(key == 13) avo().post_create_display_list();
}
else {
string& display_render_mode = GetCVarRef<string>("display.render_mode");
int& display_smooth = GetCVarRef<int>("display.smooth_shading");
int& active = GetCVarRef<int>("active_mesh");
switch(key) {
case 'q': exit(0);
case '\033':
theConsole.ToggleConsole();
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
active = key - '1'; break;
case 'f': display_smooth = !display_smooth; break;
case 'w':
display_render_mode = "wire"; break;
case 'n':
display_render_mode = "normal"; break;
case 'i':
display_render_mode = "isophotes"; break;
case 'r':
display_render_mode = "reflection"; break;
case 'h':
display_render_mode = "harmonics"; break;
case 't':
display_render_mode = "toon"; break;
case 'g':
display_render_mode = "glazed"; break;
case 'a':
display_render_mode = "ambient_occlusion"; break;
case 'c':
display_render_mode = "copper"; break;
case 'C':
display_render_mode = "curvature_lines"; break;
case 'M':
display_render_mode = "mean_curvature"; break;
case 'G':
display_render_mode = "gaussian_curvature"; break;
}
if(display_render_mode.substr(0,3) == "har")
avo().harmonics_parse_key(key);
if(key != '\033') avo().post_create_display_list();
}
}
void init_glut(int argc, char** argv)
{
glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH|GLUT_ALPHA);
glutInitWindowSize(WINX, WINY);
glutInit(&argc, argv);
glutCreateWindow("MeshEdit");
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutSpecialFunc(keyboard_spec);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutIdleFunc(animate);
}
void init_gl()
{
glewInit();
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
// Set the value of a uniform
//glUniform2f(glGetUniformLocation(prog_P0,"WIN_SCALE"), win_size_x/2.0, win_size_y/2.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(1,1,1, 0.f);
glColor4f(1.0f, 1.0f, 1.0f, 0.f);
float material[4] = {1,1,1,1};
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material);
glEnable(GL_DEPTH_TEST);
// CreateCVar("help", MyConsoleHelp );
CreateCVar("harmonics.reset_shape", console_reset_shape);
CreateCVar("harmonics.analyze", console_analyze);
CreateCVar("harmonics.partial_reconstruct", console_partial_reconstruct);
CreateCVar("simplify", console_simplify);
CreateCVar("smooth.mean_curvature", console_mean_curvature_smooth);
CreateCVar("smooth.experimental", console_experimental_smooth);
CreateCVar("smooth.laplacian", console_laplacian_smooth);
CreateCVar("smooth.taubin", console_taubin_smooth);
CreateCVar("smooth.fuzzy_vector_median", console_fvm_smooth);
CreateCVar("optimize.valency", console_optimize_valency);
CreateCVar("optimize.minimize_dihedral_angles", console_minimize_dihedral);
CreateCVar("optimize.minimize_curvature", console_minimize_curvature);
CreateCVar("optimize.maximize_min_angle", console_maximize_min_angle);
CreateCVar("cleanup.close_holes", console_close_holes);
CreateCVar("load_mesh", console_reload);
CreateCVar("add_mesh", console_add_mesh);
CreateCVar("cleanup.remove_caps", console_remove_caps);
CreateCVar("cleanup.remove_needles", console_remove_needles);
CreateCVar("triangulate", console_triangulate);
CreateCVar("refine.split_edges", console_refine_edges);
CreateCVar("refine.split_faces", console_refine_faces);
CreateCVar("refine.catmull_clark", console_cc_subdivide);
CreateCVar("refine.doo_sabin", console_doosabin_subdivide);
CreateCVar("save_mesh", console_save);
CreateCVar("noise.perturb_vertices", console_vertex_noise);
CreateCVar("noise.perturb_vertices_perpendicular", console_perpendicular_vertex_noise);
CreateCVar("noise.perturb_topology", console_noisy_flips);
CreateCVar("dual", console_dual);
CreateCVar("merge_one_ring", console_merge_one_ring);
CreateCVar("bridge", console_bridge_onerings);
CreateCVar("flatten", console_flatten);
CreateCVar("align", console_align);
CreateCVar("undo", console_undo);
CreateCVar("validity", console_valid);
CreateCVar("info", console_info);
CreateCVar("experiment", console_experiment );
CreateCVar("polarize", console_polarize );
}
int main(int argc, char** argv)
{
ArgExtracter ae(argc, argv);
init_glut(argc, argv);
init_gl();
Harmonics::init();
if(argc>1){
vector<string> files;
ae.get_all_args(files);
for(int i=1;i<files.size();++i)
get_vis_obj(i-1).reload(files[i]);
}
glutMainLoop();
return 0;
}