diff --git a/Libs/Mesh/Mesh.cpp b/Libs/Mesh/Mesh.cpp index e1dec00eb8..0d040e4843 100644 --- a/Libs/Mesh/Mesh.cpp +++ b/Libs/Mesh/Mesh.cpp @@ -101,6 +101,7 @@ Mesh& Mesh::write(const std::string &pathname) auto writer = vtkSmartPointer::New(); writer->SetFileName(pathname.c_str()); writer->SetInputData(this->mesh); + writer->WriteArrayMetaDataOff(); // needed for older readers to read these files writer->Update(); return *this; } diff --git a/Libs/Project/Project.cpp b/Libs/Project/Project.cpp index 675e509f79..c089ae59be 100644 --- a/Libs/Project/Project.cpp +++ b/Libs/Project/Project.cpp @@ -321,7 +321,7 @@ void Project::store_subjects() groomed_present = true; int count = 0; while (groomed_files.size() > groomed_columns.size()) { - groomed_columns.push_back(this->get_new_file_column(GROOMED_PREFIX, count)); + groomed_columns.push_back(this->get_new_file_column(GROOMED_PREFIX, count++)); } this->set_list(groomed_columns, i, groomed_files); @@ -355,6 +355,7 @@ void Project::store_subjects() this->get_new_file_column(std::string(LOCAL_PARTICLES) + "_", count)); world_columns.push_back( this->get_new_file_column(std::string(WORLD_PARTICLES) + "_", count)); + count++; } this->set_list(local_columns, i, local_files); @@ -765,11 +766,13 @@ int Project::get_or_create_worksheet(std::string name) //--------------------------------------------------------------------------- std::string Project::get_new_file_column(std::string name, int idx) { + return name + std::to_string(idx+1); + if (idx == 0) { - return name + "_file"; + return name + "file"; } else { - return name + "_file_" + std::to_string(idx); + return name + "file_" + std::to_string(idx); } } diff --git a/Studio/src/Application/Analysis/AnalysisTool.cpp b/Studio/src/Application/Analysis/AnalysisTool.cpp index 944a709bde..5c1f4aa8ef 100644 --- a/Studio/src/Application/Analysis/AnalysisTool.cpp +++ b/Studio/src/Application/Analysis/AnalysisTool.cpp @@ -188,9 +188,9 @@ void AnalysisTool::on_reconstructionButton_clicked() connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, SIGNAL(result_ready()), this, SLOT(handle_reconstruction_complete())); - connect(worker, &ShapeworksWorker::error_message, this, &AnalysisTool::handle_error); - connect(worker, &ShapeworksWorker::warning_message, this, &AnalysisTool::handle_warning); - connect(worker, &ShapeworksWorker::message, this, &AnalysisTool::handle_message); + connect(worker, &ShapeworksWorker::error_message, this, &AnalysisTool::error); + connect(worker, &ShapeworksWorker::warning_message, this, &AnalysisTool::warning); + connect(worker, &ShapeworksWorker::message, this, &AnalysisTool::message); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); thread->start(); @@ -433,7 +433,7 @@ bool AnalysisTool::compute_stats() int point_size = points[0].size(); for (auto&& p : points) { if (p.size() != point_size) { - this->handle_error( + emit error( "Inconsistency in data, particle files must contain the same number of points"); return false; } @@ -864,26 +864,6 @@ ShapeHandle AnalysisTool::create_shape_from_points(StudioParticles points) return shape; } -//--------------------------------------------------------------------------- -void AnalysisTool::handle_error(std::string message_string) -{ - STUDIO_LOG_ERROR(QString::fromStdString(message_string)); - emit error(message_string); -} - -//--------------------------------------------------------------------------- -void AnalysisTool::handle_warning(std::string message_string) -{ - STUDIO_LOG_ERROR(QString::fromStdString(message_string)); - emit error(message_string); -} - -//--------------------------------------------------------------------------- -void AnalysisTool::handle_message(std::string message_string) -{ - STUDIO_LOG_MESSAGE(QString::fromStdString(message_string)); - emit message(message_string); -} //--------------------------------------------------------------------------- void AnalysisTool::set_feature_map(const std::string& feature_map) diff --git a/Studio/src/Application/Analysis/AnalysisTool.h b/Studio/src/Application/Analysis/AnalysisTool.h index 10643b6719..8723d0baa0 100644 --- a/Studio/src/Application/Analysis/AnalysisTool.h +++ b/Studio/src/Application/Analysis/AnalysisTool.h @@ -124,10 +124,6 @@ public Q_SLOTS: //! Set the currently selected feature map void set_feature_map(const std::string& feature_map); - void handle_error(std::string message); - void handle_warning(std::string message); - void handle_message(std::string message); - void group_changed(); bool groups_active(); @@ -150,8 +146,9 @@ public Q_SLOTS: void update_view(); void pca_update(); void progress(int); - void message(std::string); - void error(std::string); + void message(QString); + void error(QString); + void warning(QString); void reconstruction_complete(); private: diff --git a/Studio/src/Application/CMakeLists.txt b/Studio/src/Application/CMakeLists.txt index 7f9a0eb5e6..df4f68ed92 100644 --- a/Studio/src/Application/CMakeLists.txt +++ b/Studio/src/Application/CMakeLists.txt @@ -112,8 +112,17 @@ FILE(GLOB STUDIO_ANALYSIS_HDRS Analysis/*.h) FILE(GLOB STUDIO_GROOM_SRCS Groom/*.cpp) FILE(GLOB STUDIO_GROOM_HDRS Groom/*.h) -FILE(GLOB STUDIO_UTILS_SRCS Utils/*.cpp) -FILE(GLOB STUDIO_UTILS_HDRS Utils/*.h) +SET(STUDIO_UTILS_SRCS + Utils/StackWalker.cpp + Utils/WindowsCrashHandler.cpp + Utils/StudioUtils.cpp + ) + +SET(STUDIO_UTILS_HDRS + Utils/StackWalker.h + Utils/WindowsCrashHandler.h + Utils/StudioUtils.h + ) SET(STUDIO_INTERFACE_SRCS Interface/SplashScreen.cpp diff --git a/Studio/src/Application/Data/MeshManager.cpp b/Studio/src/Application/Data/MeshManager.cpp index 64650b36d1..afdebe691c 100644 --- a/Studio/src/Application/Data/MeshManager.cpp +++ b/Studio/src/Application/Data/MeshManager.cpp @@ -99,7 +99,7 @@ void MeshManager::check_error_status(MeshHandle mesh) this->error_emitted_ = true; std::string message = "Error during mesh construction:\n\n" + mesh->get_error_message() + "\n\nFurther messages will be suppressed\n"; - emit error_encountered(message); + emit error_encountered(QString::fromStdString(message)); } } diff --git a/Studio/src/Application/Data/MeshManager.h b/Studio/src/Application/Data/MeshManager.h index bb4cc3d8fa..f6a580bfee 100644 --- a/Studio/src/Application/Data/MeshManager.h +++ b/Studio/src/Application/Data/MeshManager.h @@ -64,10 +64,10 @@ public Q_SLOTS: void new_mesh(); - void error_encountered(std::string message); + void error_encountered(QString message); void progress(int); - void status(std::string); + void status(QString); private: diff --git a/Studio/src/Application/Data/Session.cpp b/Studio/src/Application/Data/Session.cpp index e0629af7d0..dbf36ae821 100644 --- a/Studio/src/Application/Data/Session.cpp +++ b/Studio/src/Application/Data/Session.cpp @@ -57,7 +57,7 @@ void Session::handle_new_mesh() } //--------------------------------------------------------------------------- -void Session::handle_message(std::string s) +void Session::handle_message(QString s) { emit message(s); } @@ -379,14 +379,9 @@ bool Session::load_light_project(QString filename) } } - //this->calculate_reconstructed_samples(); - this->parameters().set("view_state", Visualizer::MODE_RECONSTRUCTION_C); this->parameters().set("tool_state", Session::ANALYSIS_C); - //this->preferences_.set_preference("display_state", -// QString::fromStdString(Visualizer::MODE_RECONSTRUCTION_C)); -// this->preferences_.set_preference("tool_state", QString::fromStdString(Session::ANALYSIS_C)); this->renumber_shapes(); this->project_->store_subjects(); @@ -594,6 +589,7 @@ bool Session::load_point_files(std::vector local, std::vectorshapes_.size() <= i) { auto shape = QSharedPointer(new Shape); std::shared_ptr subject = std::make_shared(); + subject->set_number_of_domains(domains_per_shape); shape->set_mesh_manager(this->mesh_manager_); shape->set_subject(subject); this->project_->get_subjects().push_back(subject); diff --git a/Studio/src/Application/Data/Session.h b/Studio/src/Application/Data/Session.h index 5126db92d4..852f6bcea5 100644 --- a/Studio/src/Application/Data/Session.h +++ b/Studio/src/Application/Data/Session.h @@ -121,7 +121,7 @@ Q_OBJECT; public Q_SLOTS: void handle_clear_cache(); void handle_new_mesh(); - void handle_message(std::string s); + void handle_message(QString s); void handle_thread_complete(); signals: @@ -130,7 +130,8 @@ public Q_SLOTS: void points_changed(); void update_display(); void new_mesh(); - void message(std::string s); + void message(QString); + void error(QString); public: // constants diff --git a/Studio/src/Application/Groom/GroomTool.cpp b/Studio/src/Application/Groom/GroomTool.cpp index 0dd4d70b2d..eb762c2b04 100644 --- a/Studio/src/Application/Groom/GroomTool.cpp +++ b/Studio/src/Application/Groom/GroomTool.cpp @@ -98,7 +98,7 @@ void GroomTool::on_autopad_checkbox_stateChanged(int state) } //--------------------------------------------------------------------------- -void GroomTool::handle_error(std::string msg) +void GroomTool::handle_error(QString msg) { this->groom_is_running_ = false; emit error_message(msg); @@ -253,7 +253,7 @@ void GroomTool::on_run_groom_button_clicked() connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, &ShapeworksWorker::finished, this, &GroomTool::handle_thread_complete); connect(this->groom_.data(), &QGroom::progress, this, &GroomTool::handle_progress); - connect(worker, SIGNAL(error_message(std::string)), this, SLOT(handle_error(std::string))); + connect(worker, &ShapeworksWorker::error_message, this, &GroomTool::handle_error); connect(worker, &ShapeworksWorker::message, this, &GroomTool::message); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); thread->start(); @@ -267,8 +267,7 @@ void GroomTool::handle_thread_complete() { emit progress(95); - std::string duration = QString::number(this->timer_.elapsed() / 1000.0, 'f', - 1).toStdString(); + QString duration = QString::number(this->timer_.elapsed() / 1000.0, 'f', 1); emit message("Groom Complete. Duration: " + duration + " seconds"); // trigger reload of meshes diff --git a/Studio/src/Application/Groom/GroomTool.h b/Studio/src/Application/Groom/GroomTool.h index 83ca9d5549..ab9a5aabfe 100644 --- a/Studio/src/Application/Groom/GroomTool.h +++ b/Studio/src/Application/Groom/GroomTool.h @@ -50,8 +50,8 @@ Q_OBJECT; Q_SIGNALS: void groom_start(); void groom_complete(); - void error_message(std::string); - void message(std::string); + void error_message(QString); + void message(QString); void progress(int); public Q_SLOTS: @@ -74,7 +74,7 @@ public Q_SLOTS: void handle_thread_complete(); void handle_progress(int val); - void handle_error(std::string msg); + void handle_error(QString msg); private: diff --git a/Studio/src/Application/Optimize/OptimizeTool.cpp b/Studio/src/Application/Optimize/OptimizeTool.cpp index a487f0bc74..a27463f14f 100644 --- a/Studio/src/Application/Optimize/OptimizeTool.cpp +++ b/Studio/src/Application/Optimize/OptimizeTool.cpp @@ -93,14 +93,14 @@ OptimizeTool::~OptimizeTool() {} //--------------------------------------------------------------------------- -void OptimizeTool::handle_error(std::string msg) +void OptimizeTool::handle_error(QString msg) { emit error_message(msg); this->update_run_button(); } //--------------------------------------------------------------------------- -void OptimizeTool::handle_warning(std::string msg) +void OptimizeTool::handle_warning(QString msg) { emit warning_message(msg); } @@ -109,7 +109,7 @@ void OptimizeTool::handle_warning(std::string msg) void OptimizeTool::handle_progress(int val, QString progress_message) { emit progress(val); - emit status(progress_message.toStdString()); + emit status(progress_message); auto particles = this->optimize_->GetParticles(); this->session_->update_particles(particles); @@ -126,8 +126,7 @@ void OptimizeTool::handle_optimize_complete() this->session_->get_project()->store_subjects(); emit progress(100); - std::string duration = QString::number(this->elapsed_timer_.elapsed() / 1000.0, 'f', - 1).toStdString(); + QString duration = QString::number(this->elapsed_timer_.elapsed() / 1000.0, 'f', 1); emit message("Optimize Complete. Duration: " + duration + " seconds"); emit optimize_complete(); this->update_run_button(); @@ -178,8 +177,8 @@ void OptimizeTool::on_run_optimize_button_clicked() connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, SIGNAL(result_ready()), this, SLOT(handle_optimize_complete())); connect(this->optimize_.data(), &QOptimize::progress, this, &OptimizeTool::handle_progress); - connect(worker, SIGNAL(error_message(std::string)), this, SLOT(handle_error(std::string))); - connect(worker, SIGNAL(message(std::string)), this, SLOT(handle_message(std::string))); + connect(worker, &ShapeworksWorker::error_message, this, &OptimizeTool::handle_error); + connect(worker, &ShapeworksWorker::message, this, &OptimizeTool::handle_message); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); connect(worker, SIGNAL(finished()), thread, SLOT(quit())); @@ -189,7 +188,7 @@ void OptimizeTool::on_run_optimize_button_clicked() } //--------------------------------------------------------------------------- -void OptimizeTool::handle_message(std::string s) +void OptimizeTool::handle_message(QString s) { emit message(s); } diff --git a/Studio/src/Application/Optimize/OptimizeTool.h b/Studio/src/Application/Optimize/OptimizeTool.h index 24b7ece2db..da250f2ee3 100644 --- a/Studio/src/Application/Optimize/OptimizeTool.h +++ b/Studio/src/Application/Optimize/OptimizeTool.h @@ -53,9 +53,9 @@ public Q_SLOTS: void on_restoreDefaults_clicked(); void handle_optimize_complete(); void handle_progress(int val, QString message); - void handle_error(std::string); - void handle_warning(std::string); - void handle_message(std::string); + void handle_error(QString); + void handle_warning(QString); + void handle_message(QString); void update_ui_elements(); @@ -65,11 +65,11 @@ public Q_SLOTS: void optimize_start(); void optimize_complete(); - void error_message(std::string); - void warning_message(std::string); + void error_message(QString); + void warning_message(QString); void progress(int); - void message(std::string); - void status(std::string); + void message(QString); + void status(QString); private: diff --git a/Studio/src/Application/Utils/StudioUtils.cpp b/Studio/src/Application/Utils/StudioUtils.cpp new file mode 100644 index 0000000000..b386226adb --- /dev/null +++ b/Studio/src/Application/Utils/StudioUtils.cpp @@ -0,0 +1,24 @@ +#include + +#include + +namespace shapeworks { + +bool StudioUtils::ask_multiple_domains_as_single(QWidget* parent, std::shared_ptr project) +{ + bool single = true; + if (project->get_number_of_domains_per_subject() > 0) { + QMessageBox::StandardButton reply; + reply = QMessageBox::question(parent, "Multiple Domains", + "This export contains multiple domains.\n\n" + "Would you like each domain exported separately?\n\n" + "Each will be suffixed with the domain name.", + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::Yes) { + single = false; + } + } + return single; +} + +} \ No newline at end of file diff --git a/Studio/src/Application/Utils/StudioUtils.h b/Studio/src/Application/Utils/StudioUtils.h new file mode 100644 index 0000000000..916d20908c --- /dev/null +++ b/Studio/src/Application/Utils/StudioUtils.h @@ -0,0 +1,13 @@ +#pragma once + +#include +class QWidget; + +namespace shapeworks { + +class StudioUtils { +public: + static bool ask_multiple_domains_as_single(QWidget *parent, std::shared_ptr project); +}; + +} // namespace shapeworks \ No newline at end of file diff --git a/Studio/src/Application/Visualization/ShapeWorksStudioApp.cpp b/Studio/src/Application/Visualization/ShapeWorksStudioApp.cpp index e0b2b33218..099af9d93a 100644 --- a/Studio/src/Application/Visualization/ShapeWorksStudioApp.cpp +++ b/Studio/src/Application/Visualization/ShapeWorksStudioApp.cpp @@ -34,6 +34,7 @@ #include #include #include +#include // ui #include @@ -146,8 +147,11 @@ ShapeWorksStudioApp::ShapeWorksStudioApp() this, &ShapeWorksStudioApp::handle_progress); connect(this->analysis_tool_.data(), SIGNAL(reconstruction_complete()), this, SLOT(handle_reconstruction_complete())); + connect(this->analysis_tool_.data(), &AnalysisTool::message, this, &ShapeWorksStudioApp::handle_message); + connect(this->analysis_tool_.data(), &AnalysisTool::warning, + this, &ShapeWorksStudioApp::handle_warning); connect(this->analysis_tool_.data(), &AnalysisTool::error, this, &ShapeWorksStudioApp::handle_error); @@ -184,8 +188,8 @@ ShapeWorksStudioApp::ShapeWorksStudioApp() this, &ShapeWorksStudioApp::handle_groom_start); connect(this->groom_tool_.data(), &GroomTool::groom_complete, this, &ShapeWorksStudioApp::handle_groom_complete); - connect(this->groom_tool_.data(), SIGNAL(error_message(std::string)), - this, SLOT(handle_error(std::string))); + connect(this->groom_tool_.data(), &GroomTool::error_message, + this, &ShapeWorksStudioApp::handle_error); connect(this->groom_tool_.data(), &GroomTool::message, this, &ShapeWorksStudioApp::handle_message); connect(this->groom_tool_.data(), &GroomTool::progress, @@ -201,10 +205,10 @@ ShapeWorksStudioApp::ShapeWorksStudioApp() connect(this->optimize_tool_.data(), &OptimizeTool::optimize_start, this, &ShapeWorksStudioApp::handle_optimize_start); - connect(this->optimize_tool_.data(), SIGNAL(error_message(std::string)), - this, SLOT(handle_error(std::string))); - connect(this->optimize_tool_.data(), SIGNAL(warning_message(std::string)), - this, SLOT(handle_warning(std::string))); + connect(this->optimize_tool_.data(), &OptimizeTool::error_message, + this, &ShapeWorksStudioApp::handle_error); + connect(this->optimize_tool_.data(), &OptimizeTool::warning_message, + this, &ShapeWorksStudioApp::handle_warning); connect(this->optimize_tool_.data(), &OptimizeTool::message, this, &ShapeWorksStudioApp::handle_message); connect(this->optimize_tool_.data(), &OptimizeTool::status, @@ -416,7 +420,7 @@ void ShapeWorksStudioApp::on_action_import_triggered() auto filenames = QFileDialog::getOpenFileNames(this, tr("Import Files..."), this->preferences_.get_last_directory(), tr( - "Supported types (*.nrrd *.nii *.nii.gz *.mha *.vtk *.ply *.vtp *.obj *stl)")); + "Supported types (*.nrrd *.nii *.nii.gz *.mha *.vtk *.ply *.vtp *.obj *.stl)")); if (filenames.size() == 0) { // was cancelled @@ -720,36 +724,36 @@ void ShapeWorksStudioApp::handle_pca_update() } //--------------------------------------------------------------------------- -void ShapeWorksStudioApp::handle_message(std::string str) +void ShapeWorksStudioApp::handle_message(QString str) { if (str != this->current_message_) { - STUDIO_LOG_MESSAGE(QString::fromStdString(str)); + STUDIO_LOG_MESSAGE(str); } - this->ui_->statusbar->showMessage(QString::fromStdString(str)); + this->ui_->statusbar->showMessage(str); this->current_message_ = str; } //--------------------------------------------------------------------------- -void ShapeWorksStudioApp::handle_status(std::string str) +void ShapeWorksStudioApp::handle_status(QString str) { - this->ui_->statusbar->showMessage(QString::fromStdString(str)); + this->ui_->statusbar->showMessage(str); this->current_message_ = str; } //--------------------------------------------------------------------------- -void ShapeWorksStudioApp::handle_error(std::string str) +void ShapeWorksStudioApp::handle_error(QString str) { - STUDIO_LOG_ERROR(QString::fromStdString(str)); - QMessageBox::critical(this, "Critical Error", str.c_str()); + STUDIO_LOG_ERROR(str); + QMessageBox::critical(this, "Critical Error", str); this->handle_message(str); //this->handle_progress(100); } //--------------------------------------------------------------------------- -void ShapeWorksStudioApp::handle_warning(std::string str) +void ShapeWorksStudioApp::handle_warning(QString str) { - STUDIO_LOG_MESSAGE(QString::fromStdString(str)); - QMessageBox::warning(this, "Warning!", str.c_str()); + STUDIO_LOG_MESSAGE(str); + QMessageBox::warning(this, "Warning!", str); } //--------------------------------------------------------------------------- @@ -822,8 +826,8 @@ void ShapeWorksStudioApp::new_session() connect(this->session_.data(), SIGNAL(points_changed()), this, SLOT(handle_points_changed())); connect(this->session_.data(), SIGNAL(update_display()), this, SLOT(handle_display_setting_changed())); - connect(this->session_.data(), SIGNAL(message(std::string)), this, - SLOT(handle_message(std::string))); + connect(this->session_.data(), &Session::message, this, + &ShapeWorksStudioApp::handle_message); connect(this->session_.data(), SIGNAL(update_display()), this, SLOT(handle_display_setting_changed())); connect(this->session_.data(), &Session::new_mesh, this, &ShapeWorksStudioApp::handle_new_mesh); @@ -1222,7 +1226,7 @@ void ShapeWorksStudioApp::on_view_mode_combobox_currentIndexChanged(QString disp void ShapeWorksStudioApp::open_project(QString filename) { this->new_session(); - this->handle_message("Loading Project: " + filename.toStdString()); + this->handle_message("Loading Project: " + filename); this->handle_progress(-1); this->is_loading_ = true; @@ -1324,66 +1328,79 @@ void ShapeWorksStudioApp::on_action_preferences_triggered() //--------------------------------------------------------------------------- void ShapeWorksStudioApp::on_action_export_current_mesh_triggered() { + bool single = StudioUtils::ask_multiple_domains_as_single(this, this->session_->get_project()); + auto dir = preferences_.get_last_directory() + "/"; QString filename = QFileDialog::getSaveFileName(this, tr("Export Current Mesh"), - dir + "mesh", - tr("VTK files (*.vtk)")); + dir + "mesh", tr( + "Supported types (*.vtk *.ply *.vtp *.obj *.stl)")); if (filename.isEmpty()) { return; } this->preferences_.set_last_directory(QFileInfo(filename).absolutePath()); - auto poly_data = this->visualizer_->get_current_mesh(); - vtkPolyDataWriter* writer = vtkPolyDataWriter::New(); - writer->SetFileName(filename.toStdString().c_str()); - writer->SetInputData(poly_data); - writer->WriteArrayMetaDataOff(); - writer->Write(); + if (single) { + this->write_mesh(this->visualizer_->get_current_mesh(), filename); + this->handle_message("Wrote: " + filename); + } + else { + auto meshes = this->visualizer_->get_current_meshes_transformed(); + auto domain_names = session_->get_project()->get_domain_names(); + + if (meshes.empty()) { + this->handle_error("Error exporting mesh: not ready yet"); + return; + } + + QFileInfo fi(filename); + QString base = fi.path() + QDir::separator() + fi.completeBaseName(); + for (int domain = 0; domain < meshes.size(); domain++) { + QString name = + base + "_" + QString::fromStdString(domain_names[domain]) + "." + fi.completeSuffix(); + + if (!this->write_mesh(meshes[domain], name)) { + return; + } + this->handle_message("Wrote: " + name); + } + } } //--------------------------------------------------------------------------- -void ShapeWorksStudioApp::on_action_export_current_particles_triggered() +bool ShapeWorksStudioApp::write_mesh(vtkSmartPointer poly_data, QString filename) { - auto dir = preferences_.get_last_directory() + "/"; - QString filename = QFileDialog::getSaveFileName(this, tr("Export Current Particles"), - dir + "shape", - tr("Particle files (*.particles)")); - if (filename.isEmpty()) { - return; + if (!poly_data) { + this->handle_error("Error exporting mesh: not ready yet"); } - this->preferences_.set_last_directory(QFileInfo(filename).absolutePath()); - - auto particles = this->visualizer_->get_current_shape().get_combined_global_particles(); - - if (!ShapeWorksStudioApp::write_particle_file(filename.toStdString(), particles)) { - this->handle_error("Error writing particle file: " + filename.toStdString()); + try { + Mesh mesh(poly_data); + mesh.write(filename.toStdString()); } - this->handle_message("Successfully exported particle file"); + catch (std::exception& e) { + this->handle_error(e.what()); + return false; + } + return true; } //--------------------------------------------------------------------------- -void ShapeWorksStudioApp::on_action_export_mesh_scalars_triggered() +bool ShapeWorksStudioApp::write_scalars(vtkSmartPointer poly_data, QString filename) { - auto dir = preferences_.get_last_directory().toStdString() + "/"; - QString filename = QFileDialog::getSaveFileName(this, tr("Export Mesh Scalars"), - QString::fromStdString(dir) + "scalars", - tr("CSV files (*.csv)")); - if (filename.isEmpty()) { - return; + if (!poly_data || !poly_data->GetPointData()->GetScalars()) { + this->handle_error("Error, no scalars to export"); + return false; } - this->preferences_.set_last_directory(QFileInfo(filename).absolutePath()); - - auto poly_data = this->visualizer_->get_current_mesh(); std::ofstream output; output.open(filename.toStdString().c_str()); + if (output.bad()) { + this->handle_error("Error writing to file: " + filename); + return false; + } output << "point,x,y,z"; - auto scalars = poly_data->GetPointData()->GetScalars(); - scalars->SetName("scalar_values"); - - //poly_data->GetPointData()->AddArray(scalars); int num_arrays = poly_data->GetPointData()->GetNumberOfArrays(); + std::cerr << "number of arrays = " << num_arrays << "\n"; for (int i = 0; i < num_arrays; i++) { if (!poly_data->GetPointData()->GetArrayName(i)) { @@ -1391,7 +1408,6 @@ void ShapeWorksStudioApp::on_action_export_mesh_scalars_triggered() } else { output << "," << poly_data->GetPointData()->GetArrayName(i); - std::cout << "array: " << poly_data->GetPointData()->GetArrayName(i) << "\n"; } } @@ -1409,13 +1425,103 @@ void ShapeWorksStudioApp::on_action_export_mesh_scalars_triggered() for (int j = 0; j < num_arrays; j++) { output << "," << poly_data->GetPointData()->GetArray(j)->GetTuple(i)[0]; - //std::cout << "array: " << poly_data->GetPointData()->GetArrayName(i) << "\n"; } - output << "\n"; } output.close(); + return true; +} + +//--------------------------------------------------------------------------- +void ShapeWorksStudioApp::on_action_export_current_particles_triggered() +{ + bool single = StudioUtils::ask_multiple_domains_as_single(this, this->session_->get_project()); + + auto dir = preferences_.get_last_directory() + "/"; + QString filename = QFileDialog::getSaveFileName(this, tr("Export Current Particles"), + dir + "shape", + tr("Particle files (*.particles)")); + if (filename.isEmpty()) { + return; + } + this->preferences_.set_last_directory(QFileInfo(filename).absolutePath()); + + if (single) { + auto particles = this->visualizer_->get_current_shape().get_combined_global_particles(); + + if (!ShapeWorksStudioApp::write_particle_file(filename.toStdString(), particles)) { + this->handle_error("Error writing particle file: " + filename); + } + this->handle_message("Wrote: " + filename); + + } + else { + + auto domain_names = session_->get_project()->get_domain_names(); + + QFileInfo fi(filename); + QString base = fi.path() + QDir::separator() + fi.completeBaseName(); + for (int domain = 0; domain < domain_names.size(); domain++) { + QString name = + base + "_" + QString::fromStdString(domain_names[domain]) + "." + fi.completeSuffix(); + + auto shape = this->visualizer_->get_current_shape(); + auto particles = this->visualizer_->get_current_shape().get_world_particles(domain); + if (!ShapeWorksStudioApp::write_particle_file(name.toStdString(), + particles)) { + this->handle_error("Error writing particle file: " + name); + } + + this->handle_message("Wrote: " + name); + } + } + +} + +//--------------------------------------------------------------------------- +void ShapeWorksStudioApp::on_action_export_mesh_scalars_triggered() +{ + bool single = StudioUtils::ask_multiple_domains_as_single(this, this->session_->get_project()); + + auto dir = preferences_.get_last_directory().toStdString() + "/"; + QString filename = QFileDialog::getSaveFileName(this, tr("Export Mesh Scalars"), + QString::fromStdString(dir) + "scalars", + tr("CSV files (*.csv)")); + if (filename.isEmpty()) { + return; + } + this->preferences_.set_last_directory(QFileInfo(filename).absolutePath()); + + if (single) { + auto poly_data = this->visualizer_->get_current_mesh(); + this->write_scalars(poly_data, filename); + } + else { + + auto meshes = this->visualizer_->get_current_meshes_transformed(); + if (meshes.empty()) { + this->handle_error("Error exporting mesh: not ready yet"); + return; + } + + auto domain_names = session_->get_project()->get_domain_names(); + + QFileInfo fi(filename); + QString base = fi.path() + QDir::separator() + fi.completeBaseName(); + for (int domain = 0; domain < domain_names.size(); domain++) { + QString name = + base + "_" + QString::fromStdString(domain_names[domain]) + "." + fi.completeSuffix(); + + auto poly_data = meshes[domain]; + if (!this->write_scalars(poly_data, name)) { + return; + } + + this->handle_message("Wrote: " + name); + } + } + } //--------------------------------------------------------------------------- @@ -1601,7 +1707,7 @@ void ShapeWorksStudioApp::on_actionExport_PCA_Mesh_triggered() writer->WriteArrayMetaDataOff(); writer->Write(); } - this->handle_message("Successfully exported PCA Mesh files: " + filename.toStdString()); + this->handle_message("Successfully exported PCA Mesh files: " + filename); return; } auto shape = this->visualizer_->get_current_shape(); @@ -1614,7 +1720,7 @@ void ShapeWorksStudioApp::on_actionExport_PCA_Mesh_triggered() //writer->SetInputData(msh); writer->WriteArrayMetaDataOff(); writer->Write(); - this->handle_message("Successfully exported PCA Mesh file: " + filename.toStdString()); + this->handle_message("Successfully exported PCA Mesh file: " + filename); } //--------------------------------------------------------------------------- @@ -1637,7 +1743,7 @@ void ShapeWorksStudioApp::on_actionExport_Eigenvalues_triggered() out << values[i] << std::endl; } out.close(); - this->handle_message("Successfully exported eigenvalue EVAL file: " + filename.toStdString()); + this->handle_message("Successfully exported eigenvalue EVAL file: " + filename); } //--------------------------------------------------------------------------- @@ -1666,7 +1772,7 @@ void ShapeWorksStudioApp::on_actionExport_Eigenvectors_triggered() } out.close(); } - this->handle_message("Successfully exported eigenvalue EVAL file: " + filename.toStdString()); + this->handle_message("Successfully exported eigenvalue EVAL file: " + filename); } //--------------------------------------------------------------------------- @@ -1691,8 +1797,8 @@ void ShapeWorksStudioApp::on_actionExport_PCA_Mode_Points_triggered() double increment = range / half_steps; auto mean_pts = this->analysis_tool_->get_shape_points(mode, 0).get_combined_global_particles(); - std::string mean_name = basename + "_mean.particles"; - if (!ShapeWorksStudioApp::write_particle_file(mean_name, mean_pts)) { + QString mean_name = QString::fromStdString(basename) + "_mean.particles"; + if (!ShapeWorksStudioApp::write_particle_file(mean_name.toStdString(), mean_pts)) { this->handle_error("Error writing particle file: " + mean_name); return; } @@ -1706,7 +1812,7 @@ void ShapeWorksStudioApp::on_actionExport_PCA_Mode_Points_triggered() auto pts = this->analysis_tool_->get_shape_points(mode, -pca_value).get_combined_global_particles(); if (!ShapeWorksStudioApp::write_particle_file(minus_name, pts)) { - this->handle_error("Error writing particle file: " + minus_name); + this->handle_error("Error writing particle file: " + QString::fromStdString(minus_name)); return; } @@ -1714,7 +1820,7 @@ void ShapeWorksStudioApp::on_actionExport_PCA_Mode_Points_triggered() basename + "_mode_" + std::to_string(mode) + "_plus_" + pca_string + ".pts"; pts = this->analysis_tool_->get_shape_points(mode, pca_value).get_combined_global_particles(); if (!ShapeWorksStudioApp::write_particle_file(plus_name, pts)) { - this->handle_error("Error writing particle file: " + plus_name); + this->handle_error("Error writing particle file: " + QString::fromStdString(plus_name)); return; } } @@ -1760,7 +1866,7 @@ void ShapeWorksStudioApp::on_actionExport_Variance_Graph_triggered() this->handle_error("Error writing variance graph"); } else { - this->handle_message("Successfully exported Variance Graph: " + filename.toStdString()); + this->handle_message("Successfully exported Variance Graph: " + filename); } } @@ -1930,6 +2036,8 @@ void ShapeWorksStudioApp::dropEvent(QDropEvent* event) + //--------------------------------------------------------------------------- } + diff --git a/Studio/src/Application/Visualization/ShapeWorksStudioApp.h b/Studio/src/Application/Visualization/ShapeWorksStudioApp.h index 02245d88ae..8865f74e35 100644 --- a/Studio/src/Application/Visualization/ShapeWorksStudioApp.h +++ b/Studio/src/Application/Visualization/ShapeWorksStudioApp.h @@ -15,6 +15,9 @@ #include +#include +#include + // Forward Qt class declarations class Ui_ShapeWorksStudioApp; @@ -99,15 +102,15 @@ public Q_SLOTS: void handle_color_scheme(); void handle_pca_update(); - void handle_message(std::string str); - void handle_status(std::string str); - void handle_error(std::string str); - void handle_warning(std::string str); + void handle_message(QString str); + void handle_status(QString str); + void handle_error(QString str); + void handle_warning(QString str); void handle_progress(int amt); void handle_new_mesh(); void handle_clear_cache(); - void update_feature_map_selection(const QString &feature_map); + void update_feature_map_selection(const QString& feature_map); void show_splash_screen(); void about(); void keyboard_shortcuts(); @@ -176,6 +179,9 @@ public Q_SLOTS: void save_project(std::string filename); + bool write_mesh(vtkSmartPointer poly_data, QString filename); + bool write_scalars(vtkSmartPointer poly_data, QString filename); + /// designer form Ui_ShapeWorksStudioApp* ui_; @@ -204,7 +210,7 @@ public Q_SLOTS: QList recent_file_actions_; QProgressBar* progress_bar_; - std::string current_message_; + QString current_message_; std::string current_display_mode_; @@ -216,6 +222,5 @@ public Q_SLOTS: QElapsedTimer time_since_last_update_; qint64 last_render_ = -1; - }; } \ No newline at end of file diff --git a/Studio/src/Application/Visualization/ShapeWorksWorker.cpp b/Studio/src/Application/Visualization/ShapeWorksWorker.cpp index 22d864ab54..cdc008f7fb 100644 --- a/Studio/src/Application/Visualization/ShapeWorksWorker.cpp +++ b/Studio/src/Application/Visualization/ShapeWorksWorker.cpp @@ -46,53 +46,53 @@ void ShapeworksWorker::process() this->groom_->run(); } catch (itk::ExceptionObject& ex) { std::cerr << "ITK Exception: " << ex << std::endl; - emit error_message(std::string("ITK Exception: ") + ex.GetDescription()); + emit error_message(QString("ITK Exception: ") + ex.GetDescription()); return; } catch (std::runtime_error& e) { - emit error_message(std::string("Error: ") + e.what()); + emit error_message(QString("Error: ") + e.what()); return; } catch (std::exception& e) { - emit error_message(std::string("Error: ") + e.what()); + emit error_message(QString("Error: ") + e.what()); return; } catch (...) { - emit error_message(std::string("Error during grooming!")); + emit error_message(QString("Error during grooming!")); return; } if (this->groom_->get_aborted()) { - emit error_message(std::string("Groom Aborted!")); + emit error_message(QString("Groom Aborted!")); return; } break; case ShapeworksWorker::OptimizeType: try { - emit message(std::string("Loading data...")); + emit message("Loading data..."); this->optimize_parameters_->set_up_optimize(this->optimize_.data()); - emit message(std::string("Optimizing correspondence...")); + emit message("Optimizing correspondence..."); this->optimize_->Run(); } catch (std::runtime_error e) { std::cerr << "Exception: " << e.what() << "\n"; - emit error_message(std::string("Error: ") + e.what()); + emit error_message(QString("Error: ") + e.what()); return; } catch (itk::ExceptionObject& ex) { std::cerr << "ITK Exception: " << ex << std::endl; - emit error_message(std::string("ITK Exception: ") + ex.GetDescription()); + emit error_message(QString("ITK Exception: ") + ex.GetDescription()); return; } catch (std::exception& e) { - emit error_message(std::string("Error: ") + e.what()); + emit error_message(QString("Error: ") + e.what()); return; } catch (...) { - emit error_message(std::string("Error during optimization!")); + emit error_message("Error during optimization!"); return; } if (this->optimize_->GetAborted()) { - emit message(std::string("Optimization Aborted!")); + emit message("Optimization Aborted!"); return; } break; case ShapeworksWorker::ReconstructType: try { - emit message(std::string("Warping to mean space...")); + emit message("Warping to mean space..."); for (int i = 0; i < this->session_->get_domains_per_shape(); i++) { auto shapes = this->session_->get_shapes(); @@ -115,7 +115,7 @@ void ShapeworksWorker::process() emit warning_message(e.what()); } else { - emit error_message(std::string("Error: ") + e.what()); + emit error_message(QString("Error: ") + e.what()); return; } } catch (std::exception& e) { @@ -123,11 +123,11 @@ void ShapeworksWorker::process() emit warning_message(e.what()); } else { - emit error_message(std::string("Error: ") + e.what()); + emit error_message(QString("Error: ") + e.what()); return; } } catch (...) { - emit error_message(std::string("Error during optimization!")); + emit error_message(QString("Error during optimization!")); return; } break; diff --git a/Studio/src/Application/Visualization/ShapeWorksWorker.h b/Studio/src/Application/Visualization/ShapeWorksWorker.h index 0db88cbdec..8cfbfd481d 100644 --- a/Studio/src/Application/Visualization/ShapeWorksWorker.h +++ b/Studio/src/Application/Visualization/ShapeWorksWorker.h @@ -35,11 +35,11 @@ public Q_SLOTS: Q_SIGNALS: void result_ready(); - void error_message(std::string); - void warning_message(std::string); + void error_message(QString); + void warning_message(QString); + void message(QString); void step_made(int val); void finished(); - void message(std::string); private: QSharedPointer groom_; diff --git a/Studio/src/Application/Visualization/Viewer.cpp b/Studio/src/Application/Visualization/Viewer.cpp index 71f417fe27..c0db887782 100644 --- a/Studio/src/Application/Visualization/Viewer.cpp +++ b/Studio/src/Application/Visualization/Viewer.cpp @@ -392,11 +392,13 @@ void Viewer::compute_surface_differences(vtkSmartPointer magnitud point_locator->BuildLocator(); vtkFloatArray* surface_magnitudes = vtkFloatArray::New(); + surface_magnitudes->SetName("surface_difference"); surface_magnitudes->SetNumberOfComponents(1); surface_magnitudes->SetNumberOfTuples(poly_data->GetPoints()->GetNumberOfPoints()); vtkFloatArray* surface_vectors = vtkFloatArray::New(); surface_vectors->SetNumberOfComponents(3); + surface_vectors->SetName("surface_vectors"); surface_vectors->SetNumberOfTuples(poly_data->GetPoints()->GetNumberOfPoints()); for (unsigned int i = 0; i < surface_magnitudes->GetNumberOfTuples(); i++) { diff --git a/Studio/src/Application/Visualization/Visualizer.cpp b/Studio/src/Application/Visualization/Visualizer.cpp index d7148e4bd4..a3dd9ee9f5 100644 --- a/Studio/src/Application/Visualization/Visualizer.cpp +++ b/Studio/src/Application/Visualization/Visualizer.cpp @@ -103,7 +103,12 @@ void Visualizer::display_shape(ShapeHandle shape) //----------------------------------------------------------------------------- StudioParticles Visualizer::get_current_shape() { - return this->current_shape_; + auto shapes = this->lightbox_->get_shapes(); + if (shapes.size() > 0) { + return shapes[0]->get_particles(); + } + StudioParticles particles; + return particles; } //----------------------------------------------------------------------------- @@ -118,29 +123,42 @@ void Visualizer::handle_new_mesh() //----------------------------------------------------------------------------- vtkSmartPointer Visualizer::get_current_mesh() { + auto meshes = this->get_current_meshes_transformed(); + if (meshes.empty()) { + return nullptr; + } + vtkSmartPointer append = vtkSmartPointer::New(); + for (int domain = 0; domain < meshes.size(); domain++) { + append->AddInputData(meshes[domain]); + } + append->Update(); + vtkSmartPointer combined = append->GetOutput(); + return combined; +} + +//----------------------------------------------------------------------------- +std::vector> Visualizer::get_current_meshes_transformed() +{ + std::vector> list; auto shapes = this->lightbox_->get_shapes(); if (shapes.size() > 0) { - auto meshes = shapes[0]->get_meshes(this->display_mode_).meshes(); - if (meshes.size() == 1) { - return meshes[0]->get_poly_data(); - } - else { - vtkSmartPointer append = vtkSmartPointer::New(); + if (shapes[0]->get_meshes(this->display_mode_).valid()) { + auto meshes = shapes[0]->get_meshes(this->display_mode_).meshes(); + for (int domain = 0; domain < meshes.size(); domain++) { + if (!meshes[domain]->get_poly_data()) { + return list; + } // we have to transform each domain to its location in order to export an appended mesh auto filter = vtkSmartPointer::New(); filter->SetTransform(this->get_transform(shapes[0], domain)); filter->SetInputData(meshes[domain]->get_poly_data()); filter->Update(); - - append->AddInputData(filter->GetOutput()); + list.push_back(filter->GetOutput()); } - append->Update(); - vtkSmartPointer combined = append->GetOutput(); - return combined; } } - return nullptr; + return list; } //----------------------------------------------------------------------------- diff --git a/Studio/src/Application/Visualization/Visualizer.h b/Studio/src/Application/Visualization/Visualizer.h index 5120e9687f..f11c61858a 100644 --- a/Studio/src/Application/Visualization/Visualizer.h +++ b/Studio/src/Application/Visualization/Visualizer.h @@ -73,6 +73,7 @@ Q_OBJECT; void handle_new_mesh(); vtkSmartPointer get_current_mesh(); + std::vector> get_current_meshes_transformed(); //! Get the currently selected feature map const std::string& get_feature_map() const;