// MeshDoc.cpp : implementation of the CMeshDoc class // #include "stdafx.h" #include "Mesh.h" #include "MeshDoc.h" // STL stuff #include #include // CGAL (off) + obj IO #include #include "Lib/parser_obj.h" // subdivision #include "Lib/sqrt3.h" #include "Lib/quad-triangle.h" #include "Lib/SurfLab/Polyhedron_subdivision.h" #include ".\meshdoc.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CMeshDoc IMPLEMENT_DYNCREATE(CMeshDoc, CDocument) BEGIN_MESSAGE_MAP(CMeshDoc, CDocument) ON_COMMAND(ID_SUBDIVISION_SQRT3, OnSubdivisionSqrt3) ON_COMMAND(ID_SUBDIVISION_SQRT3_TWICE, OnSubdivisionSqrt3Twice) ON_COMMAND(ID_SUBDIVISION_QUAD, OnSubdivisionQuad) ON_UPDATE_COMMAND_UI(ID_SUBDIVISION_QUAD, OnUpdateSubdivisionQuad) ON_UPDATE_COMMAND_UI(ID_SUBDIVISION_SQRT3, OnUpdateSubdivisionSqrt3) ON_UPDATE_COMMAND_UI(ID_SUBDIVISION_SQRT3_TWICE, OnUpdateSubdivisionSqrt3Twice) ON_COMMAND(ID_SUBDIVISION_DOO, OnSubdivisionDoo) ON_UPDATE_COMMAND_UI(ID_SUBDIVISION_DOO, OnUpdateSubdivisionDoo) ON_COMMAND(ID_SUBDIVISION_LOOP, OnSubdivisionLoop) ON_UPDATE_COMMAND_UI(ID_SUBDIVISION_LOOP, OnUpdateSubdivisionLoop) ON_COMMAND(ID_SUBDIVISION_CATMULL32868, OnSubdivisionCatmull32868) ON_UPDATE_COMMAND_UI(ID_SUBDIVISION_CATMULL32868, OnUpdateSubdivisionCatmull32868) END_MESSAGE_MAP() // CMeshDoc construction/destruction CMeshDoc::CMeshDoc() { m_pMesh = NULL; } CMeshDoc::~CMeshDoc() { delete m_pMesh; ResetMeshProperties(); } // CMeshDoc serialization void CMeshDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) {} else {} } // CMeshDoc diagnostics #ifdef _DEBUG void CMeshDoc::AssertValid() const { CDocument::AssertValid(); } void CMeshDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); } #endif //_DEBUG // CMeshDoc commands // open document file BOOL CMeshDoc::OnOpenDocument(LPCTSTR lpszPathName) { if(!CDocument::OnOpenDocument(lpszPathName)) return FALSE; // get extension CString file = lpszPathName; CString extension = lpszPathName; extension = extension.Right(4); extension.MakeLower(); // set current path // path "c:\path\file.wrl" -> c:\path CString path = lpszPathName; path = path.Left(path.ReverseFind('\\')); SetCurrentDirectory(path); // allocate a new mesh m_pMesh = new Enriched_polyhedron; CGAL_assertion(m_pMesh != NULL); // off extension if(extension == ".off") { // read from stream std::ifstream stream(lpszPathName); if(!stream) { AfxMessageBox("Unable to open file"); return false; } stream >> *m_pMesh; } else if(extension == ".obj") { Parser_obj parser; parser.read(lpszPathName,m_pMesh); } else { AfxMessageBox("Unknown extension"); return false; } // update mesh properties in the status bar // and compute normals m_pMesh->compute_type(); UpdateMeshProperties(true,true); m_pMesh->compute_normals(); return TRUE; } // save file BOOL CMeshDoc::OnSaveDocument(LPCTSTR lpszPathName) { // Extension-based checking CString file = lpszPathName; // Extension CString extension = lpszPathName; extension = extension.Right(4); extension.MakeLower(); // Path "c:\path\file.wrl" -> c:\path CString path = lpszPathName; path = path.Left(path.ReverseFind('\\')); // Current path SetCurrentDirectory(path); TRACE("\nOpening document\n"); TRACE("File : %s\n",lpszPathName); TRACE("Path : %s\n",path); TRACE("Extension : %s\n",extension); // save off file if(extension == ".off") { // OFF extension TRACE("attempt to save OFF file %s...",lpszPathName); std::ofstream stream(lpszPathName); if(!stream) { TRACE("failed\n"); return false; } // save the mesh TRACE("ok\nsave mesh..."); stream << *m_pMesh; TRACE("..ok\n"); } // save obj file if(extension == ".obj") { StatusMessage("Saving OBJ file..."); m_pMesh->write_obj((char *)lpszPathName); StatusMessage("Saving OBJ file...done"); } return TRUE; } // sqrt3 subdivision void CMeshDoc::OnSubdivisionSqrt3() { BeginWaitCursor(); StatusMessage("Subdivide..."); CSubdivider_sqrt3,Enriched_kernel> subdivider; subdivider.subdivide(*m_pMesh,1); // one iteration m_pMesh->compute_normals(); UpdateMeshProperties(); UpdateAllViews(NULL); EndWaitCursor(); StatusMessage("Subdivide...done"); } void CMeshDoc::OnUpdateSubdivisionSqrt3(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pMesh != NULL && m_pMesh->is_pure_triangle()); } // sqrt3 subdivision applied twice // (boundary edges are trisected) void CMeshDoc::OnSubdivisionSqrt3Twice() { BeginWaitCursor(); StatusMessage("Subdivide..."); CSubdivider_sqrt3,Enriched_kernel> subdivider; subdivider.subdivide(*m_pMesh,2); // two iterations m_pMesh->compute_normals(); UpdateMeshProperties(); UpdateAllViews(NULL); EndWaitCursor(); StatusMessage("Subdivide...done"); } void CMeshDoc::OnUpdateSubdivisionSqrt3Twice(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pMesh != NULL && m_pMesh->is_pure_triangle()); } // quad/triangle subdivision void CMeshDoc::OnSubdivisionQuad() { double start = clock(); // subdivision engine CSubdivider_quad_triangle,Enriched_kernel> subdivider; BeginWaitCursor(); StatusMessage("Quad/triangle subdivision..."); // alloc a new mesh Enriched_polyhedron *pNewMesh = new Enriched_polyhedron; // subdivide once subdivider.subdivide(*m_pMesh,*pNewMesh,true); // copy bounding box (approximate, but fast) pNewMesh->copy_bounding_box(m_pMesh); // delete previous mesh delete m_pMesh; // set new mesh m_pMesh = pNewMesh; // compute normals m_pMesh->compute_normals(); UpdateMeshProperties(); EndWaitCursor(); float duration = (float)((clock()-start)/CLOCKS_PER_SEC); StatusMessage("Quad/triangle subdivision...done (%g s)",duration); UpdateAllViews(NULL); } void CMeshDoc::OnUpdateSubdivisionQuad(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pMesh != NULL); } // Doo-Sabin void CMeshDoc::OnSubdivisionDoo() { double start = clock(); BeginWaitCursor(); StatusMessage("Doo-Sabin subdivision..."); Polyhedron_subdivision >::DooSabin_subdivision(*m_pMesh,1); // compute normals m_pMesh->compute_normals(); UpdateMeshProperties(); EndWaitCursor(); float duration = (float)((clock()-start)/CLOCKS_PER_SEC); StatusMessage("Doo-Sabin subdivision...done (%g s)",duration); UpdateAllViews(NULL); } void CMeshDoc::OnUpdateSubdivisionDoo(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pMesh != NULL); } // Loop subdivision void CMeshDoc::OnSubdivisionLoop() { double start = clock(); BeginWaitCursor(); StatusMessage("Loop subdivision..."); Polyhedron_subdivision >::Loop_subdivision(*m_pMesh,1); // compute normals m_pMesh->compute_normals(); UpdateMeshProperties(); EndWaitCursor(); float duration = (float)((clock()-start)/CLOCKS_PER_SEC); StatusMessage("Loop subdivision...done (%g s)",duration); UpdateAllViews(NULL); } void CMeshDoc::OnUpdateSubdivisionLoop(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pMesh->is_pure_triangle()); } // Catmull-Clark void CMeshDoc::OnSubdivisionCatmull32868() { double start = clock(); BeginWaitCursor(); StatusMessage("Catmull-Clark subdivision..."); Polyhedron_subdivision >::CatmullClark_subdivision(*m_pMesh,1); // compute normals m_pMesh->compute_normals(); UpdateMeshProperties(); EndWaitCursor(); float duration = (float)((clock()-start)/CLOCKS_PER_SEC); StatusMessage("Catmull-Clark...done (%g s)",duration); UpdateAllViews(NULL); } void CMeshDoc::OnUpdateSubdivisionCatmull32868(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pMesh != NULL); } //******************************************* // User message in status bar //******************************************* void CMeshDoc::StatusMessage(char* fmt,...) { CWinApp *pApp = AfxGetApp(); if(pApp->m_pMainWnd != NULL) { char buffer[256]; CStatusBar* pStatus = (CStatusBar*)AfxGetApp()->m_pMainWnd->GetDescendantWindow( AFX_IDW_STATUS_BAR); // fill buffer va_list argptr; va_start(argptr,fmt); vsprintf(buffer,fmt,argptr); va_end(argptr); if(pStatus != NULL) { pStatus->SetPaneText(0,buffer); pStatus->UpdateWindow(); } } return; } //******************************************* // update mesh properties in status bar //******************************************* void CMeshDoc::UpdateMeshProperties(bool update_component, bool update_boundary) { CWinApp *pApp = AfxGetApp(); if(pApp->m_pMainWnd != NULL) { CStatusBar* pStatus = (CStatusBar*)AfxGetApp()->m_pMainWnd->GetDescendantWindow( AFX_IDW_STATUS_BAR); if(pStatus != NULL) { static unsigned int c = 0; if(update_component) c = m_pMesh->nb_components(); static unsigned int b = 0; if(update_boundary) b = m_pMesh->nb_boundaries(); unsigned int v = m_pMesh->size_of_vertices(); unsigned int e = m_pMesh->size_of_halfedges()/2; unsigned int f = m_pMesh->size_of_facets(); unsigned int g = m_pMesh->genus(c,v,f,e,b); // components CString components; components.Format("%d component%c",c,(c>1) ? 's' : ' '); // vertices CString vertices; vertices.Format("%d vertices",v); // facets CString facets; facets.Format("%d facets",f); // edges CString edges; edges.Format("%d edges",e); // boundaries CString boundaries; if(b == 0) boundaries.Format("no boundary"); else if(b == 1) boundaries.Format("1 boundary"); else boundaries.Format("%d boundaries",b); // genus CString genus; genus.Format("genus %d",g); pStatus->SetPaneText(1,components); pStatus->SetPaneText(2,vertices); pStatus->SetPaneText(3,facets); pStatus->SetPaneText(4,edges); pStatus->SetPaneText(5,boundaries); pStatus->SetPaneText(6,genus); pStatus->UpdateWindow(); } } } //******************************************* // reset mesh properties in status bar //******************************************* void CMeshDoc::ResetMeshProperties() { if(AfxGetApp()->m_pMainWnd != NULL) { CStatusBar* pStatus = (CStatusBar*)AfxGetApp()->m_pMainWnd->GetDescendantWindow( AFX_IDW_STATUS_BAR); if(pStatus != NULL) { pStatus->SetPaneText(1,CString("")); pStatus->SetPaneText(2,CString("")); pStatus->SetPaneText(3,CString("")); pStatus->SetPaneText(4,CString("")); pStatus->SetPaneText(5,CString("")); pStatus->SetPaneText(6,CString("")); pStatus->UpdateWindow(); } } }