From 47af82e2dac2d175c824e8ab7cb4386383ec50cb Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Wed, 1 Feb 2023 11:15:19 -0700 Subject: [PATCH 01/23] Restyle/reformat QOptimize --- Studio/Optimize/QOptimize.cpp | 102 ++++++++++++++-------------------- Studio/Optimize/QOptimize.h | 18 +++--- 2 files changed, 51 insertions(+), 69 deletions(-) diff --git a/Studio/Optimize/QOptimize.cpp b/Studio/Optimize/QOptimize.cpp index da88c712af..68c11d27fd 100644 --- a/Studio/Optimize/QOptimize.cpp +++ b/Studio/Optimize/QOptimize.cpp @@ -1,72 +1,62 @@ #include "QOptimize.h" -#include #include +#include + namespace shapeworks { -QOptimize::QOptimize(QObject* parent) : - QObject(parent), - Optimize() -{} +QOptimize::QOptimize(QObject* parent) : QObject(parent), Optimize() {} //--------------------------------------------------------------------------- -QOptimize::~QOptimize() -{} +QOptimize::~QOptimize() {} //--------------------------------------------------------------------------- -std::vector>> QOptimize::GetLocalPoints() -{ +std::vector>> QOptimize::GetLocalPoints() { QMutexLocker locker(&qmutex_); - return this->m_local_points; + return m_local_points; } //--------------------------------------------------------------------------- -std::vector>> QOptimize::GetGlobalPoints() -{ +std::vector>> QOptimize::GetGlobalPoints() { QMutexLocker locker(&qmutex_); - return this->m_global_points; + return m_global_points; } //--------------------------------------------------------------------------- -void QOptimize::UpdateExportablePoints() -{ +void QOptimize::UpdateExportablePoints() { QMutexLocker locker(&qmutex_); Optimize::UpdateExportablePoints(); } //--------------------------------------------------------------------------- -void QOptimize::SetIterationCallback() -{ - this->iterate_command_ = itk::MemberCommand::New(); - this->iterate_command_->SetCallbackFunction(this, &QOptimize::IterateCallback); - m_sampler->GetOptimizer()->AddObserver(itk::IterationEvent(), this->iterate_command_); +void QOptimize::SetIterationCallback() { + iterate_command_ = itk::MemberCommand::New(); + iterate_command_->SetCallbackFunction(this, &QOptimize::IterateCallback); + m_sampler->GetOptimizer()->AddObserver(itk::IterationEvent(), iterate_command_); } //--------------------------------------------------------------------------- -void QOptimize::IterateCallback(itk::Object* caller, const itk::EventObject& e) -{ - - if (this->m_aborted) { +void QOptimize::IterateCallback(itk::Object* caller, const itk::EventObject& e) { + if (m_aborted) { return; } // run superclass iterateCallback Optimize::IterateCallback(caller, e); - auto transform = this->m_sampler->GetParticleSystem()->GetTransform(); + auto transform = m_sampler->GetParticleSystem()->GetTransform(); if (transform(0, 0) != transform(0, 0)) { - //throw on NaN + // throw on NaN throw std::runtime_error("Optimize failed! Please try changing parameters."); } bool update = false; - if (!this->time_since_last_update_.isValid()) { + if (!time_since_last_update_.isValid()) { update = true; - } - else { - auto time_since = this->time_since_last_update_.elapsed(); + } else { + auto time_since = time_since_last_update_.elapsed(); if (time_since > 100) { update = true; } @@ -76,39 +66,35 @@ void QOptimize::IterateCallback(itk::Object* caller, const itk::EventObject& e) int stage_total_iterations = m_sampler->GetOptimizer()->GetMaximumNumberOfIterations(); int num_particles = m_sampler->GetParticleSystem()->GetNumberOfParticles(0); QString message; - if (this->m_optimizing) { + if (m_optimizing) { message = "Optimizing: "; - } - else { + } else { message = "Initializing: "; } - message = message + "Particles: " + QString::number(num_particles) + ", Iteration: " + - QString::number(stage_num_iterations) + " / " + QString::number(stage_total_iterations); + message = message + "Particles: " + QString::number(num_particles) + + ", Iteration: " + QString::number(stage_num_iterations) + " / " + QString::number(stage_total_iterations); if (update) { - this->time_since_last_update_.start(); + time_since_last_update_.start(); { QMutexLocker locker(&qmutex_); - this->m_local_points.clear(); - this->m_global_points.clear(); + m_local_points.clear(); + m_global_points.clear(); // copy particles - for (size_t d = 0; d < this->m_sampler-> - GetParticleSystem()->GetNumberOfDomains(); d++) { - + for (size_t d = 0; d < m_sampler->GetParticleSystem()->GetNumberOfDomains(); d++) { // blank set of points - this->m_local_points.push_back(std::vector>()); - this->m_global_points.push_back(std::vector>()); + m_local_points.push_back(std::vector>()); + m_global_points.push_back(std::vector>()); // for each particle - for (size_t j = 0; j < this->m_sampler-> - GetParticleSystem()->GetNumberOfParticles(d); j++) { - auto pos = this->m_sampler->GetParticleSystem()->GetPosition(j, d); - auto pos2 = this->m_sampler->GetParticleSystem()->GetTransformedPosition(j, d); - this->m_local_points[d].push_back(pos); - this->m_global_points[d].push_back(pos2); + for (size_t j = 0; j < m_sampler->GetParticleSystem()->GetNumberOfParticles(d); j++) { + auto pos = m_sampler->GetParticleSystem()->GetPosition(j, d); + auto pos2 = m_sampler->GetParticleSystem()->GetTransformedPosition(j, d); + m_local_points[d].push_back(pos); + m_global_points[d].push_back(pos2); } } } @@ -117,21 +103,20 @@ void QOptimize::IterateCallback(itk::Object* caller, const itk::EventObject& e) } //--------------------------------------------------------------------------- -std::vector QOptimize::GetParticles() -{ +std::vector QOptimize::GetParticles() { QMutexLocker locker(&qmutex_); std::vector particles; - int num_domains_per_subject = this->GetDomainsPerShape(); - int num_subjects = this->GetNumShapes() / num_domains_per_subject; + int num_domains_per_subject = GetDomainsPerShape(); + int num_subjects = GetNumShapes() / num_domains_per_subject; particles.resize(num_subjects); int subject = 0; int domain = 0; - for (int i = 0; i < this->m_local_points.size(); i++) { - particles[subject].set_local_particles(domain, this->m_local_points[i]); - particles[subject].set_world_particles(domain, this->m_global_points[i]); + for (int i = 0; i < m_local_points.size(); i++) { + particles[subject].set_local_particles(domain, m_local_points[i]); + particles[subject].set_world_particles(domain, m_global_points[i]); domain++; if (domain == num_domains_per_subject) { subject++; @@ -143,11 +128,10 @@ std::vector QOptimize::GetParticles() } //--------------------------------------------------------------------------- -std::vector>> QOptimize::GetProcrustesTransforms() -{ +std::vector>> QOptimize::GetProcrustesTransforms() { QMutexLocker locker(&qmutex_); auto transforms = Optimize::GetProcrustesTransforms(); return transforms; } -} +} // namespace shapeworks diff --git a/Studio/Optimize/QOptimize.h b/Studio/Optimize/QOptimize.h index 8d913be74d..2488ca1137 100644 --- a/Studio/Optimize/QOptimize.h +++ b/Studio/Optimize/QOptimize.h @@ -4,17 +4,17 @@ #include #include #endif -#include -#include #include +#include +#include namespace shapeworks { //! Wraps Optimize as a QObject class QOptimize : public QObject, public Optimize { -Q_OBJECT; + Q_OBJECT; -public: + public: QOptimize(QObject* parent = nullptr); virtual ~QOptimize(); @@ -27,22 +27,20 @@ Q_OBJECT; void UpdateExportablePoints() override; -protected: + protected: virtual void SetIterationCallback() override; virtual void IterateCallback(itk::Object* caller, const itk::EventObject&) override; -Q_SIGNALS: + Q_SIGNALS: void progress(int, QString); -private: - + private: itk::MemberCommand::Pointer iterate_command_; // for concurrent access QMutex qmutex_; QElapsedTimer time_since_last_update_; - }; -} +} // namespace shapeworks From 25fdc72d621d9f990f45a90647ef7ed665d7a2f5 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Wed, 1 Feb 2023 19:23:30 -0700 Subject: [PATCH 02/23] Adding generalized progress and status. Add estimated time to optimizer. --- Libs/Analyze/MeshManager.h | 2 +- Libs/Common/Logging.cpp | 36 ++++++++++++------- Libs/Common/Logging.h | 19 +++++++++- Libs/Optimize/Optimize.cpp | 45 ++++++++++++++++++++++-- Libs/Optimize/Optimize.h | 5 +++ Studio/Interface/ShapeWorksStudioApp.cpp | 9 +++-- Studio/Interface/ShapeWorksStudioApp.h | 5 ++- Studio/Interface/StudioLogger.cpp | 11 ++++-- Studio/Interface/StudioLogger.h | 4 +++ Studio/Optimize/OptimizeTool.cpp | 4 +-- Studio/Optimize/OptimizeTool.h | 2 +- Studio/Optimize/QOptimize.cpp | 15 +------- 12 files changed, 115 insertions(+), 42 deletions(-) diff --git a/Libs/Analyze/MeshManager.h b/Libs/Analyze/MeshManager.h index 6353deac08..12345bcc6a 100644 --- a/Libs/Analyze/MeshManager.h +++ b/Libs/Analyze/MeshManager.h @@ -76,7 +76,7 @@ class MeshManager : public QObject { void error_encountered(QString message); void progress(int); - void status(QString); + void status(std::string); private: std::shared_ptr reconstructors_ = std::make_shared(); diff --git a/Libs/Common/Logging.cpp b/Libs/Common/Logging.cpp index 878f62ef9f..1e8c98b015 100644 --- a/Libs/Common/Logging.cpp +++ b/Libs/Common/Logging.cpp @@ -10,9 +10,9 @@ namespace spd = spdlog; namespace shapeworks { //----------------------------------------------------------------------------- -static std::string create_header(const int line, const char *filename) { - const char *name = (strrchr(filename, '/') ? strrchr(filename, '/') + 1 : filename); - const char *name2 = (strrchr(name, '\\') ? strrchr(name, '\\') + 1 : name); +static std::string create_header(const int line, const char* filename) { + const char* name = (strrchr(filename, '/') ? strrchr(filename, '/') + 1 : filename); + const char* name2 = (strrchr(name, '\\') ? strrchr(name, '\\') + 1 : name); std::string header = "[" + std::string(name2) + "|" + std::to_string(line) + "]"; return header; } @@ -53,7 +53,7 @@ bool Logging::check_log_open() const { return log_open_; } std::string Logging::get_log_filename() const { return log_filename_; } //----------------------------------------------------------------------------- -void Logging::log_message(const std::string& message, const int line, const char *file) const { +void Logging::log_message(const std::string& message, const int line, const char* file) const { spd::info(message); if (log_open_) { spd::get("file")->info(message); @@ -72,7 +72,7 @@ void Logging::log_stack(const std::string& message) const { } //----------------------------------------------------------------------------- -void Logging::log_error(const std::string& message, const int line, const char *file) const { +void Logging::log_error(const std::string& message, const int line, const char* file) const { spd::error(message); if (log_open_) { spd::get("file")->error(message); @@ -83,7 +83,7 @@ void Logging::log_error(const std::string& message, const int line, const char * } //----------------------------------------------------------------------------- -void Logging::show_message(const std::string& message, const int line, const char *file) const { +void Logging::show_message(const std::string& message, const int line, const char* file) const { log_message(message, line, file); if (message_callback_) { message_callback_(message); @@ -91,15 +91,14 @@ void Logging::show_message(const std::string& message, const int line, const cha } //----------------------------------------------------------------------------- -void Logging::show_status(const std::string& message, const int line, const char *file) const { - log_message(message, line, file); - if (message_callback_) { - message_callback_(message); +void Logging::show_status(const std::string& message, const int line, const char* file) const { + if (status_callback_) { + status_callback_(message); } } //----------------------------------------------------------------------------- -void Logging::log_debug(const std::string& message, const int line, const char *file) const { +void Logging::log_debug(const std::string& message, const int line, const char* file) const { std::string str = create_header(line, file) + " " + message; spd::debug(str); if (log_open_) { @@ -113,7 +112,7 @@ void Logging::log_debug(const std::string& message, const int line, const char * } //----------------------------------------------------------------------------- -void Logging::log_warning(const std::string& message, const int line, const char *file) const { +void Logging::log_warning(const std::string& message, const int line, const char* file) const { spd::warn(message); if (log_open_) { spd::get("file")->warn(message); @@ -123,6 +122,13 @@ void Logging::log_warning(const std::string& message, const int line, const char } } +//----------------------------------------------------------------------------- +void Logging::show_progress(double value) { + if (progress_callback_) { + progress_callback_(value); + } +} + //----------------------------------------------------------------------------- void Logging::close_log() { if (!log_open_) { @@ -145,4 +151,10 @@ void Logging::set_warning_callback(const std::function& callb //----------------------------------------------------------------------------- void Logging::set_debug_callback(const std::function& callback) { debug_callback_ = callback; } +//----------------------------------------------------------------------------- +void Logging::set_status_callback(const std::function& callback) { status_callback_ = callback; } + +//----------------------------------------------------------------------------- +void Logging::set_progress_callback(const std::function& callback) { progress_callback_ = callback; } + } // namespace shapeworks diff --git a/Libs/Common/Logging.h b/Libs/Common/Logging.h index 6de6cace24..d858dcdb1c 100644 --- a/Libs/Common/Logging.h +++ b/Libs/Common/Logging.h @@ -102,6 +102,9 @@ class Logging { //! Log a message, use SW_STATUS macro void show_status(const std::string& message, const int line, const char* file) const; + //! Display progress (0-100) + void show_progress(double value); + //! Log a debug message, use SW_DEBUG macro void log_debug(const std::string& message, const int line, const char* file) const; @@ -120,9 +123,15 @@ class Logging { //! Set a warning callback function to be called whenever a warning is posted void set_warning_callback(const std::function& callback); - //! Set a debug messagecallback function to be called whenever a debug message is posted + //! Set a debug message callback function to be called whenever a debug message is posted void set_debug_callback(const std::function& callback); + //! Set a status callback function to be called whenever a status message is posted + void set_status_callback(const std::function& callback); + + //! Set a progress callback function to be called whenever a progress update is posted + void set_progress_callback(const std::function& callback); + private: //! Constructor Logging(); @@ -137,6 +146,11 @@ class Logging { std::function warning_callback_; std::function debug_callback_; + + std::function status_callback_; + + std::function progress_callback_; + }; //! Log stack macro @@ -169,6 +183,9 @@ class Logging { #define SW_STATUS(message, ...) \ shapeworks::Logging::Instance().show_status(fmt::format(message, ##__VA_ARGS__), __LINE__, __FILE__) +#define SW_PROGRESS(value) \ + shapeworks::Logging::Instance().show_progress(value); + //! Close session macro #define SW_CLOSE_LOG() shapeworks::Logging::Instance().close_log(); diff --git a/Libs/Optimize/Optimize.cpp b/Libs/Optimize/Optimize.cpp index 9fb3a14c1f..2ce3464e49 100644 --- a/Libs/Optimize/Optimize.cpp +++ b/Libs/Optimize/Optimize.cpp @@ -28,14 +28,13 @@ #include "Optimize.h" #include "OptimizeParameterFile.h" #include "OptimizeParameters.h" +#include "ParticleGoodBadAssessment.h" #include "ParticleImageDomain.h" #include "ParticleImplicitSurfaceDomain.h" #include "VtkMeshWrapper.h" #include "object_reader.h" #include "object_writer.h" -#include "ParticleGoodBadAssessment.h" - // pybind #include #include @@ -67,6 +66,9 @@ Optimize::Optimize() { this->m_sampler = std::make_shared(); } //--------------------------------------------------------------------------- bool Optimize::Run() { + m_start_time = std::chrono::system_clock::now(); + m_last_update_time = m_start_time; + // control number of threads int num_threads = tbb::info::default_concurrency(); const char* num_threads_env = getenv("TBB_NUM_THREADS"); @@ -967,6 +969,7 @@ void Optimize::IterateCallback(itk::Object*, const itk::EventObject&) { } } } + UpdateProgress(); } //--------------------------------------------------------------------------- @@ -2164,6 +2167,7 @@ void Optimize::SetSharedBoundaryEnabled(bool enabled) { m_sampler->SetSharedBoun //--------------------------------------------------------------------------- void Optimize::SetSharedBoundaryWeight(double weight) { m_sampler->SetSharedBoundaryWeight(weight); } +//--------------------------------------------------------------------------- void Optimize::ComputeTotalIterations() { total_particle_iterations_ = 0; @@ -2201,4 +2205,41 @@ void Optimize::ComputeTotalIterations() { } } +//--------------------------------------------------------------------------- +void Optimize::UpdateProgress() { + auto now = std::chrono::system_clock::now(); + + // compute time since last update in milliseconds + auto time_since_last_update = std::chrono::duration_cast(now - m_last_update_time); + + if (time_since_last_update.count() > 100) { + m_last_update_time = now; + std::chrono::duration elapsed_seconds = + std::chrono::duration_cast(now - m_start_time); + double seconds_per_iteration = elapsed_seconds.count() / current_particle_iterations_; + double seconds_remaining = seconds_per_iteration * (total_particle_iterations_ - current_particle_iterations_); + int hours = static_cast(seconds_remaining / 3600); + int minutes = static_cast((seconds_remaining - (hours * 3600)) / 60); + int seconds = static_cast(seconds_remaining - (hours * 3600) - (minutes * 60)); + + std::string message; + if (m_optimizing) { + message = "Optimizing: "; + } else { + message = "Initializing: "; + } + + int stage_num_iterations = m_sampler->GetOptimizer()->GetNumberOfIterations(); + int stage_total_iterations = m_sampler->GetOptimizer()->GetMaximumNumberOfIterations(); + int num_particles = m_sampler->GetParticleSystem()->GetNumberOfParticles(0); + + message = + fmt::format("Particles: {}, Iteration: {} / {}", num_particles, stage_num_iterations, stage_total_iterations); + message = fmt::format("{} ({:02d}:{:02d}:{:02d} remaining)", message, hours, minutes, seconds); + + double progress = current_particle_iterations_ * 100 / total_particle_iterations_; + SW_STATUS(message); + SW_PROGRESS(progress); + } +} } // namespace shapeworks diff --git a/Libs/Optimize/Optimize.h b/Libs/Optimize/Optimize.h index 3551eab98f..5ccfd28dd4 100644 --- a/Libs/Optimize/Optimize.h +++ b/Libs/Optimize/Optimize.h @@ -302,6 +302,8 @@ class Optimize { //! transform a point if necessary vnl_vector_fixed TransformPoint(int domain, vnl_vector_fixed input); + void UpdateProgress(); + protected: //! Set the iteration callback. Derived classes should override to set their own callback virtual void SetIterationCallback(); @@ -460,6 +462,9 @@ class Optimize { shapeworks::OptimizationVisualizer visualizer_; std::shared_ptr project_; + + std::chrono::system_clock::time_point m_start_time; + std::chrono::system_clock::time_point m_last_update_time; }; } // namespace shapeworks diff --git a/Studio/Interface/ShapeWorksStudioApp.cpp b/Studio/Interface/ShapeWorksStudioApp.cpp index 9acec6b0b0..514c276d9d 100644 --- a/Studio/Interface/ShapeWorksStudioApp.cpp +++ b/Studio/Interface/ShapeWorksStudioApp.cpp @@ -76,6 +76,8 @@ ShapeWorksStudioApp::ShapeWorksStudioApp() { connect(&logger_, &StudioLogger::error, this, &ShapeWorksStudioApp::handle_error); connect(&logger_, &StudioLogger::warning, this, &ShapeWorksStudioApp::handle_warning); connect(&logger_, &StudioLogger::debug, this, &ShapeWorksStudioApp::handle_debug); + connect(&logger_, &StudioLogger::status, this, &ShapeWorksStudioApp::handle_status); + connect(&logger_, &StudioLogger::progress, this, &ShapeWorksStudioApp::handle_progress); // default hide ui_->feature_widget->hide(); @@ -655,9 +657,10 @@ void ShapeWorksStudioApp::handle_message(std::string str) { } //--------------------------------------------------------------------------- -void ShapeWorksStudioApp::handle_status(QString str) { - status_bar_->set_message(MessageType::normal, str); - current_message_ = str; +void ShapeWorksStudioApp::handle_status(std::string str) { + auto qstr = QString::fromStdString(str); + status_bar_->set_message(MessageType::normal, qstr); + current_message_ = qstr; } //--------------------------------------------------------------------------- diff --git a/Studio/Interface/ShapeWorksStudioApp.h b/Studio/Interface/ShapeWorksStudioApp.h index 649174cc4e..df8dcb83eb 100644 --- a/Studio/Interface/ShapeWorksStudioApp.h +++ b/Studio/Interface/ShapeWorksStudioApp.h @@ -129,11 +129,10 @@ class ShapeWorksStudioApp : public QMainWindow { void handle_error(std::string str); void handle_warning(std::string str); void handle_debug(std::string str); + void handle_status(std::string str); + void handle_progress(int amt); void message_callback(std::string str); - - void handle_status(QString str); - void handle_progress(int amt); void handle_new_mesh(); void handle_clear_cache(); void handle_compare_settings_changed(); diff --git a/Studio/Interface/StudioLogger.cpp b/Studio/Interface/StudioLogger.cpp index e22e4ec143..7edbfb483a 100644 --- a/Studio/Interface/StudioLogger.cpp +++ b/Studio/Interface/StudioLogger.cpp @@ -14,16 +14,21 @@ void StudioLogger::register_callbacks() { Logging::Instance().set_warning_callback(warning_callback); auto debug_callback = std::bind(&StudioLogger::handle_debug, this, std::placeholders::_1); Logging::Instance().set_debug_callback(debug_callback); + auto status_callback = std::bind(&StudioLogger::handle_status, this, std::placeholders::_1); + Logging::Instance().set_status_callback(status_callback); + auto progress_callback = std::bind(&StudioLogger::handle_progress, this, std::placeholders::_1); + Logging::Instance().set_progress_callback(progress_callback); } //--------------------------------------------------------------------------- void StudioLogger::handle_message(std::string str) { Q_EMIT message(str); } - //--------------------------------------------------------------------------- void StudioLogger::handle_error(std::string str) { Q_EMIT error(str); } - //--------------------------------------------------------------------------- void StudioLogger::handle_warning(std::string str) { Q_EMIT warning(str); } - //--------------------------------------------------------------------------- void StudioLogger::handle_debug(std::string str) { Q_EMIT debug(str); } +//--------------------------------------------------------------------------- +void StudioLogger::handle_status(std::string str) { Q_EMIT status(str); } +//--------------------------------------------------------------------------- +void StudioLogger::handle_progress(double value) { Q_EMIT progress(static_cast(value)); } diff --git a/Studio/Interface/StudioLogger.h b/Studio/Interface/StudioLogger.h index ba2e0d3df6..91fbbc619a 100644 --- a/Studio/Interface/StudioLogger.h +++ b/Studio/Interface/StudioLogger.h @@ -17,10 +17,14 @@ class StudioLogger : public QObject { void handle_error(std::string str); void handle_warning(std::string str); void handle_debug(std::string str); + void handle_status(std::string str); + void handle_progress(double value); Q_SIGNALS: void message(std::string str); void error(std::string str); void warning(std::string str); void debug(std::string str); + void status(std::string str); + void progress(int value); }; diff --git a/Studio/Optimize/OptimizeTool.cpp b/Studio/Optimize/OptimizeTool.cpp index 8a5e3fb164..7cac360c53 100644 --- a/Studio/Optimize/OptimizeTool.cpp +++ b/Studio/Optimize/OptimizeTool.cpp @@ -113,8 +113,8 @@ void OptimizeTool::handle_progress(int val, QString progress_message) { return; } - Q_EMIT progress(val); - Q_EMIT status(progress_message); + //Q_EMIT progress(val); + //Q_EMIT status(progress_message.toStdString()); auto particles = optimize_->GetParticles(); session_->update_particles(particles); diff --git a/Studio/Optimize/OptimizeTool.h b/Studio/Optimize/OptimizeTool.h index bd2d3c67bd..e62b58a896 100644 --- a/Studio/Optimize/OptimizeTool.h +++ b/Studio/Optimize/OptimizeTool.h @@ -68,7 +68,7 @@ public Q_SLOTS: void optimize_complete(); void progress(int); - void status(QString); + void status(std::string); private: diff --git a/Studio/Optimize/QOptimize.cpp b/Studio/Optimize/QOptimize.cpp index 68c11d27fd..4c8c49069f 100644 --- a/Studio/Optimize/QOptimize.cpp +++ b/Studio/Optimize/QOptimize.cpp @@ -62,19 +62,6 @@ void QOptimize::IterateCallback(itk::Object* caller, const itk::EventObject& e) } } - int stage_num_iterations = m_sampler->GetOptimizer()->GetNumberOfIterations(); - int stage_total_iterations = m_sampler->GetOptimizer()->GetMaximumNumberOfIterations(); - int num_particles = m_sampler->GetParticleSystem()->GetNumberOfParticles(0); - QString message; - if (m_optimizing) { - message = "Optimizing: "; - } else { - message = "Initializing: "; - } - - message = message + "Particles: " + QString::number(num_particles) + - ", Iteration: " + QString::number(stage_num_iterations) + " / " + QString::number(stage_total_iterations); - if (update) { time_since_last_update_.start(); { @@ -98,7 +85,7 @@ void QOptimize::IterateCallback(itk::Object* caller, const itk::EventObject& e) } } } - Q_EMIT progress(current_particle_iterations_ * 100 / total_particle_iterations_, message); + Q_EMIT progress(0, ""); } } From 23688bc93c53f58823c3dd3acf41460a5b152629 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Wed, 1 Feb 2023 21:20:39 -0700 Subject: [PATCH 03/23] Adding --progress option to shapeworks optimize command --- Applications/shapeworks/Commands.cpp | 4 ++++ Libs/Optimize/Optimize.cpp | 11 +++++++++-- Libs/Optimize/Optimize.h | 3 +++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Applications/shapeworks/Commands.cpp b/Applications/shapeworks/Commands.cpp index 0dd129f7e6..4ec0e5caec 100644 --- a/Applications/shapeworks/Commands.cpp +++ b/Applications/shapeworks/Commands.cpp @@ -75,12 +75,14 @@ void OptimizeCommand::buildParser() { parser.prog(prog).description(desc); parser.add_option("--name").action("store").type("string").set_default("").help("Path to project file."); + parser.add_option("--progress").action("store_true").set_default(false).help("Show progress [default: false]."); Command::buildParser(); } bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData& sharedData) { const std::string& projectFile(static_cast(options.get("name"))); + bool show_progress = static_cast(options.get("progress")); if (projectFile.length() == 0) { std::cerr << "Must specify project name with --name \n"; @@ -90,6 +92,8 @@ bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData bool isProject = StringUtils::hasSuffix(projectFile, "xlsx") || StringUtils::hasSuffix(projectFile, "swproj"); Optimize app; + app.SetShowProgress(show_progress); + if (isProject) { try { // load spreadsheet project diff --git a/Libs/Optimize/Optimize.cpp b/Libs/Optimize/Optimize.cpp index 2ce3464e49..3acdf52ba7 100644 --- a/Libs/Optimize/Optimize.cpp +++ b/Libs/Optimize/Optimize.cpp @@ -2233,13 +2233,20 @@ void Optimize::UpdateProgress() { int stage_total_iterations = m_sampler->GetOptimizer()->GetMaximumNumberOfIterations(); int num_particles = m_sampler->GetParticleSystem()->GetNumberOfParticles(0); - message = - fmt::format("Particles: {}, Iteration: {} / {}", num_particles, stage_num_iterations, stage_total_iterations); + message = fmt::format("{}: Particles: {}, Iteration: {} / {}", message, num_particles, stage_num_iterations, + stage_total_iterations); message = fmt::format("{} ({:02d}:{:02d}:{:02d} remaining)", message, hours, minutes, seconds); double progress = current_particle_iterations_ * 100 / total_particle_iterations_; SW_STATUS(message); SW_PROGRESS(progress); + + if (m_show_progress) { + // show percentage complete + std::cout << fmt::format("{} ({:.2f}%) ", message, progress) << "\r"; + // flush stdout + std::cout.flush(); + } } } } // namespace shapeworks diff --git a/Libs/Optimize/Optimize.h b/Libs/Optimize/Optimize.h index 5ccfd28dd4..e45c174434 100644 --- a/Libs/Optimize/Optimize.h +++ b/Libs/Optimize/Optimize.h @@ -303,6 +303,7 @@ class Optimize { vnl_vector_fixed TransformPoint(int domain, vnl_vector_fixed input); void UpdateProgress(); + void SetShowProgress(bool show) { m_show_progress = show; } protected: //! Set the iteration callback. Derived classes should override to set their own callback @@ -465,6 +466,8 @@ class Optimize { std::chrono::system_clock::time_point m_start_time; std::chrono::system_clock::time_point m_last_update_time; + bool m_show_progress = false; + }; } // namespace shapeworks From c46eec934da6a7e37ec3434feb393ca70fc5010a Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Wed, 1 Feb 2023 21:47:50 -0700 Subject: [PATCH 04/23] Update SW_PROGRESS to take a message as well. --- Libs/Common/Logging.cpp | 6 +++--- Libs/Common/Logging.h | 14 ++++++-------- Libs/Optimize/Optimize.cpp | 9 ++++----- Studio/Interface/ShapeWorksStudioApp.cpp | 9 +++++++-- Studio/Interface/ShapeWorksStudioApp.h | 1 + Studio/Interface/StudioLogger.cpp | 5 +++-- Studio/Interface/StudioLogger.h | 4 ++-- 7 files changed, 26 insertions(+), 22 deletions(-) diff --git a/Libs/Common/Logging.cpp b/Libs/Common/Logging.cpp index 1e8c98b015..082c76c4f5 100644 --- a/Libs/Common/Logging.cpp +++ b/Libs/Common/Logging.cpp @@ -123,9 +123,9 @@ void Logging::log_warning(const std::string& message, const int line, const char } //----------------------------------------------------------------------------- -void Logging::show_progress(double value) { +void Logging::show_progress(double value, const std::string& message) { if (progress_callback_) { - progress_callback_(value); + progress_callback_(value, message); } } @@ -155,6 +155,6 @@ void Logging::set_debug_callback(const std::function& callbac void Logging::set_status_callback(const std::function& callback) { status_callback_ = callback; } //----------------------------------------------------------------------------- -void Logging::set_progress_callback(const std::function& callback) { progress_callback_ = callback; } +void Logging::set_progress_callback(const std::function& callback) { progress_callback_ = callback; } } // namespace shapeworks diff --git a/Libs/Common/Logging.h b/Libs/Common/Logging.h index d858dcdb1c..9d44f4e6af 100644 --- a/Libs/Common/Logging.h +++ b/Libs/Common/Logging.h @@ -2,9 +2,8 @@ #include -#include - #include +#include template <> struct fmt::formatter { @@ -103,7 +102,7 @@ class Logging { void show_status(const std::string& message, const int line, const char* file) const; //! Display progress (0-100) - void show_progress(double value); + void show_progress(double value, const std::string& message); //! Log a debug message, use SW_DEBUG macro void log_debug(const std::string& message, const int line, const char* file) const; @@ -130,7 +129,7 @@ class Logging { void set_status_callback(const std::function& callback); //! Set a progress callback function to be called whenever a progress update is posted - void set_progress_callback(const std::function& callback); + void set_progress_callback(const std::function& callback); private: //! Constructor @@ -149,8 +148,7 @@ class Logging { std::function status_callback_; - std::function progress_callback_; - + std::function progress_callback_; }; //! Log stack macro @@ -183,8 +181,8 @@ class Logging { #define SW_STATUS(message, ...) \ shapeworks::Logging::Instance().show_status(fmt::format(message, ##__VA_ARGS__), __LINE__, __FILE__) -#define SW_PROGRESS(value) \ - shapeworks::Logging::Instance().show_progress(value); +#define SW_PROGRESS(value, message, ...) \ + shapeworks::Logging::Instance().show_progress(value, fmt::format(message, ##__VA_ARGS__)); //! Close session macro #define SW_CLOSE_LOG() shapeworks::Logging::Instance().close_log(); diff --git a/Libs/Optimize/Optimize.cpp b/Libs/Optimize/Optimize.cpp index 3acdf52ba7..1627c55dd0 100644 --- a/Libs/Optimize/Optimize.cpp +++ b/Libs/Optimize/Optimize.cpp @@ -2224,22 +2224,21 @@ void Optimize::UpdateProgress() { std::string message; if (m_optimizing) { - message = "Optimizing: "; + message = "Optimizing"; } else { - message = "Initializing: "; + message = "Initializing"; } int stage_num_iterations = m_sampler->GetOptimizer()->GetNumberOfIterations(); int stage_total_iterations = m_sampler->GetOptimizer()->GetMaximumNumberOfIterations(); int num_particles = m_sampler->GetParticleSystem()->GetNumberOfParticles(0); - message = fmt::format("{}: Particles: {}, Iteration: {} / {}", message, num_particles, stage_num_iterations, + message = fmt::format("{} : Particles: {}, Iteration: {} / {}", message, num_particles, stage_num_iterations, stage_total_iterations); message = fmt::format("{} ({:02d}:{:02d}:{:02d} remaining)", message, hours, minutes, seconds); double progress = current_particle_iterations_ * 100 / total_particle_iterations_; - SW_STATUS(message); - SW_PROGRESS(progress); + SW_PROGRESS(progress, message); if (m_show_progress) { // show percentage complete diff --git a/Studio/Interface/ShapeWorksStudioApp.cpp b/Studio/Interface/ShapeWorksStudioApp.cpp index 514c276d9d..40a0665b9d 100644 --- a/Studio/Interface/ShapeWorksStudioApp.cpp +++ b/Studio/Interface/ShapeWorksStudioApp.cpp @@ -77,7 +77,7 @@ ShapeWorksStudioApp::ShapeWorksStudioApp() { connect(&logger_, &StudioLogger::warning, this, &ShapeWorksStudioApp::handle_warning); connect(&logger_, &StudioLogger::debug, this, &ShapeWorksStudioApp::handle_debug); connect(&logger_, &StudioLogger::status, this, &ShapeWorksStudioApp::handle_status); - connect(&logger_, &StudioLogger::progress, this, &ShapeWorksStudioApp::handle_progress); + connect(&logger_, &StudioLogger::progress, this, &ShapeWorksStudioApp::handle_progress_with_message); // default hide ui_->feature_widget->hide(); @@ -693,10 +693,15 @@ void ShapeWorksStudioApp::message_callback(std::string str) { // QMetaObject::invokeMethod(this,) } +//--------------------------------------------------------------------------- +void ShapeWorksStudioApp::handle_progress_with_message(int value, std::string str) { + handle_progress(value); + handle_status(str); +} + //--------------------------------------------------------------------------- void ShapeWorksStudioApp::handle_progress(int value) { status_bar_->set_progress(value); - handle_message(current_message_.toStdString()); } //--------------------------------------------------------------------------- diff --git a/Studio/Interface/ShapeWorksStudioApp.h b/Studio/Interface/ShapeWorksStudioApp.h index df8dcb83eb..16ecf0972e 100644 --- a/Studio/Interface/ShapeWorksStudioApp.h +++ b/Studio/Interface/ShapeWorksStudioApp.h @@ -130,6 +130,7 @@ class ShapeWorksStudioApp : public QMainWindow { void handle_warning(std::string str); void handle_debug(std::string str); void handle_status(std::string str); + void handle_progress_with_message(int amt, std::string str); void handle_progress(int amt); void message_callback(std::string str); diff --git a/Studio/Interface/StudioLogger.cpp b/Studio/Interface/StudioLogger.cpp index 7edbfb483a..5e2af2c108 100644 --- a/Studio/Interface/StudioLogger.cpp +++ b/Studio/Interface/StudioLogger.cpp @@ -16,7 +16,8 @@ void StudioLogger::register_callbacks() { Logging::Instance().set_debug_callback(debug_callback); auto status_callback = std::bind(&StudioLogger::handle_status, this, std::placeholders::_1); Logging::Instance().set_status_callback(status_callback); - auto progress_callback = std::bind(&StudioLogger::handle_progress, this, std::placeholders::_1); + auto progress_callback = + std::bind(&StudioLogger::handle_progress, this, std::placeholders::_1, std::placeholders::_2); Logging::Instance().set_progress_callback(progress_callback); } @@ -31,4 +32,4 @@ void StudioLogger::handle_debug(std::string str) { Q_EMIT debug(str); } //--------------------------------------------------------------------------- void StudioLogger::handle_status(std::string str) { Q_EMIT status(str); } //--------------------------------------------------------------------------- -void StudioLogger::handle_progress(double value) { Q_EMIT progress(static_cast(value)); } +void StudioLogger::handle_progress(double value, std::string str) { Q_EMIT progress(static_cast(value), str); } diff --git a/Studio/Interface/StudioLogger.h b/Studio/Interface/StudioLogger.h index 91fbbc619a..bc477204b4 100644 --- a/Studio/Interface/StudioLogger.h +++ b/Studio/Interface/StudioLogger.h @@ -18,7 +18,7 @@ class StudioLogger : public QObject { void handle_warning(std::string str); void handle_debug(std::string str); void handle_status(std::string str); - void handle_progress(double value); + void handle_progress(double value, std::string str); Q_SIGNALS: void message(std::string str); @@ -26,5 +26,5 @@ class StudioLogger : public QObject { void warning(std::string str); void debug(std::string str); void status(std::string str); - void progress(int value); + void progress(int value, std::string str); }; From ea12077cde5182f639208d2691074afab7d27792 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Thu, 2 Feb 2023 14:35:24 -0700 Subject: [PATCH 05/23] Progress and XML out updates. --- Applications/shapeworks/Commands.cpp | 41 ++++++++++++++++++++++++++-- Libs/Groom/Groom.cpp | 3 +- Libs/Groom/Groom.h | 2 -- Libs/Optimize/Optimize.cpp | 27 +++++++++--------- Studio/CMakeLists.txt | 2 -- Studio/Data/ShapeWorksWorker.cpp | 4 +-- Studio/Data/ShapeWorksWorker.h | 6 ++-- Studio/Groom/GroomTool.cpp | 5 ++-- Studio/Groom/GroomTool.h | 4 +-- 9 files changed, 64 insertions(+), 30 deletions(-) diff --git a/Applications/shapeworks/Commands.cpp b/Applications/shapeworks/Commands.cpp index 4ec0e5caec..48f5c1ecf1 100644 --- a/Applications/shapeworks/Commands.cpp +++ b/Applications/shapeworks/Commands.cpp @@ -41,6 +41,33 @@ bool Example::execute(const optparse::Values &options, SharedCommandData &shared } #endif +static void setup_callbacks(bool show_progress, bool xml_status) { + if (show_progress) { + auto progress_callback = [](double progress, std::string message) { + // show percentage complete + std::cout << fmt::format("{} ({:.1f}%) ", message, progress) << "\r"; + std::cout.flush(); + }; + Logging::Instance().set_progress_callback(progress_callback); + } + + if (xml_status) { + auto progress_callback = [](double progress, std::string message) { + // show percentage complete + std::cout << fmt::format("{}{:.1f}", message, progress) + << "\n"; + std::cout.flush(); + }; + Logging::Instance().set_progress_callback(progress_callback); + + auto error_callback = [](std::string message) { + std::cout << fmt::format("{}", message) << "\n"; + std::cout.flush(); + }; + Logging::Instance().set_error_callback(error_callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // Seed /////////////////////////////////////////////////////////////////////////////// @@ -76,6 +103,7 @@ void OptimizeCommand::buildParser() { parser.add_option("--name").action("store").type("string").set_default("").help("Path to project file."); parser.add_option("--progress").action("store_true").set_default(false).help("Show progress [default: false]."); + parser.add_option("--xmlconsole").action("store_true").set_default(false).help("XML console output [default: false]."); Command::buildParser(); } @@ -83,6 +111,7 @@ void OptimizeCommand::buildParser() { bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData& sharedData) { const std::string& projectFile(static_cast(options.get("name"))); bool show_progress = static_cast(options.get("progress")); + bool xml_status = static_cast(options.get("xmlconsole")); if (projectFile.length() == 0) { std::cerr << "Must specify project name with --name \n"; @@ -94,6 +123,8 @@ bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData Optimize app; app.SetShowProgress(show_progress); + setup_callbacks(show_progress, xml_status); + if (isProject) { try { // load spreadsheet project @@ -121,7 +152,7 @@ bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData return success; } catch (std::exception& e) { - std::cerr << "Error: " << e.what() << "\n"; + SW_ERROR(e.what()); return false; } } else { @@ -140,18 +171,24 @@ void GroomCommand::buildParser() { parser.prog(prog).description(desc); parser.add_option("--name").action("store").type("string").set_default("").help("Path to project file."); + parser.add_option("--progress").action("store_true").set_default(false).help("Show progress [default: false]."); + parser.add_option("--xmlconsole").action("store_true").set_default(false).help("XML console output [default: false]."); Command::buildParser(); } bool GroomCommand::execute(const optparse::Values& options, SharedCommandData& sharedData) { const std::string& projectFile(static_cast(options.get("name"))); + bool show_progress = static_cast(options.get("progress")); + bool xml_status = static_cast(options.get("xmlconsole")); if (projectFile.length() == 0) { std::cerr << "Must specify project name with --name \n"; return false; } + setup_callbacks(show_progress, xml_status); + try { ProjectHandle project = std::make_shared(); project->load(projectFile); @@ -172,7 +209,7 @@ bool GroomCommand::execute(const optparse::Values& options, SharedCommandData& s } return success; } catch (std::exception& e) { - std::cerr << "Error: " << e.what() << "\n"; + SW_ERROR(e.what()); return false; } } diff --git a/Libs/Groom/Groom.cpp b/Libs/Groom/Groom.cpp index 165124c8b7..42f95cf6f1 100644 --- a/Libs/Groom/Groom.cpp +++ b/Libs/Groom/Groom.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -470,7 +471,7 @@ void Groom::increment_progress(int amount) { std::scoped_lock lock(mutex); this->progress_counter_ += amount; this->progress_ = static_cast(this->progress_counter_) / static_cast(this->total_ops_) * 100.0; - this->update_progress(); + SW_PROGRESS(progress_, fmt::format("Grooming ({}/{} ops)", progress_counter_, total_ops_)); } //--------------------------------------------------------------------------- diff --git a/Libs/Groom/Groom.h b/Libs/Groom/Groom.h index 082dfddc2f..5451fbd24f 100644 --- a/Libs/Groom/Groom.h +++ b/Libs/Groom/Groom.h @@ -33,8 +33,6 @@ class Groom { vtkSmartPointer target); protected: - //! call to be overridden by subclasses - virtual void update_progress(){}; std::atomic progress_ = 0; std::atomic total_ops_ = 0; diff --git a/Libs/Optimize/Optimize.cpp b/Libs/Optimize/Optimize.cpp index 1627c55dd0..53b14dc98a 100644 --- a/Libs/Optimize/Optimize.cpp +++ b/Libs/Optimize/Optimize.cpp @@ -195,17 +195,16 @@ bool Optimize::Run() { //--------------------------------------------------------------------------- int Optimize::SetParameters() { - if (this->m_verbosity_level == 0) { - std::cout << "Verbosity 0: This will be the only output on your screen, " - "unless there are any errors. Increase the verbosity if needed.\n"; - } - // sanity check if (m_domains_per_shape != m_number_of_particles.size()) { SW_ERROR("Inconsistency in parameters... m_domains_per_shape != m_number_of_particles.size()"); return false; } + + SW_ERROR("Inconsistency in parameters... m_domains_per_shape != m_number_of_particles.size()"); + return false; + // ensure that use_shape_statistics_after doesn't increase the particle count over what was specified for (int i = 0; i < m_number_of_particles.size(); i++) { m_use_shape_statistics_after = std::min(m_use_shape_statistics_after, m_number_of_particles[i]); @@ -2237,15 +2236,17 @@ void Optimize::UpdateProgress() { stage_total_iterations); message = fmt::format("{} ({:02d}:{:02d}:{:02d} remaining)", message, hours, minutes, seconds); - double progress = current_particle_iterations_ * 100 / total_particle_iterations_; + double progress = + static_cast(current_particle_iterations_) * 100 / static_cast(total_particle_iterations_); SW_PROGRESS(progress, message); - - if (m_show_progress) { - // show percentage complete - std::cout << fmt::format("{} ({:.2f}%) ", message, progress) << "\r"; - // flush stdout - std::cout.flush(); - } + /* + if (m_show_progress) { + // show percentage complete + std::cout << fmt::format("{} ({:.2f}%) ", message, progress) << "\r"; + // flush stdout + std::cout.flush(); + } + */ } } } // namespace shapeworks diff --git a/Studio/CMakeLists.txt b/Studio/CMakeLists.txt index f586adb731..ea09d5e110 100644 --- a/Studio/CMakeLists.txt +++ b/Studio/CMakeLists.txt @@ -115,11 +115,9 @@ SET(STUDIO_JOB_MOC_HDRS SET(STUDIO_GROOM_SRCS Groom/GroomTool.cpp - Groom/QGroom.cpp ) SET(STUDIO_GROOM_MOC_HDRS Groom/GroomTool.h - Groom/QGroom.h ) SET(STUDIO_PYTHON_SRCS diff --git a/Studio/Data/ShapeWorksWorker.cpp b/Studio/Data/ShapeWorksWorker.cpp index 991d9118b6..8e02e6e415 100644 --- a/Studio/Data/ShapeWorksWorker.cpp +++ b/Studio/Data/ShapeWorksWorker.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -10,7 +10,7 @@ namespace shapeworks { //--------------------------------------------------------------------------- -ShapeworksWorker::ShapeworksWorker(ThreadType type, QSharedPointer groom, QSharedPointer optimize, +ShapeworksWorker::ShapeworksWorker(ThreadType type, QSharedPointer groom, QSharedPointer optimize, QSharedPointer optimize_parameters, QSharedPointer session, double maxAngle, float decimationPercent, int numClusters) diff --git a/Studio/Data/ShapeWorksWorker.h b/Studio/Data/ShapeWorksWorker.h index adf9caa19a..dbf170a583 100644 --- a/Studio/Data/ShapeWorksWorker.h +++ b/Studio/Data/ShapeWorksWorker.h @@ -6,7 +6,7 @@ namespace shapeworks { class Optimize; -class QGroom; +class Groom; class OptimizeParameters; class ShapeworksWorker : public QObject { @@ -18,7 +18,7 @@ Q_OBJECT }; ShapeworksWorker(ThreadType type, - QSharedPointer groom, + QSharedPointer groom, QSharedPointer optimize, QSharedPointer optimize_parameters, QSharedPointer session, @@ -37,7 +37,7 @@ public Q_SLOTS: private: - QSharedPointer groom_; + QSharedPointer groom_; QSharedPointer optimize_; QSharedPointer optimize_parameters_; QSharedPointer session_; diff --git a/Studio/Groom/GroomTool.cpp b/Studio/Groom/GroomTool.cpp index 95d445dc2f..411ce09f80 100644 --- a/Studio/Groom/GroomTool.cpp +++ b/Studio/Groom/GroomTool.cpp @@ -490,7 +490,7 @@ void GroomTool::on_run_groom_button_clicked() { SW_LOG("Please wait: running groom step..."); Q_EMIT progress(0); - groom_ = QSharedPointer(new QGroom(session_->get_project())); + groom_ = QSharedPointer(new Groom(session_->get_project())); enable_actions(); @@ -500,7 +500,6 @@ void GroomTool::on_run_groom_button_clicked() { worker->moveToThread(thread); connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, &ShapeworksWorker::finished, this, &GroomTool::handle_thread_complete); - connect(groom_.data(), &QGroom::progress, this, &GroomTool::handle_progress); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); thread->start(); @@ -561,7 +560,7 @@ void GroomTool::skip_grooming_toggled() { store_params(); if (ui_->skip_grooming->isChecked()) { - groom_ = QSharedPointer(new QGroom(session_->get_project())); + groom_ = QSharedPointer(new Groom(session_->get_project())); groom_->run(); SW_MESSAGE("Skipped Grooming"); Q_EMIT groom_complete(); diff --git a/Studio/Groom/GroomTool.h b/Studio/Groom/GroomTool.h index ee325da021..6dfc1512ac 100644 --- a/Studio/Groom/GroomTool.h +++ b/Studio/Groom/GroomTool.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -97,7 +97,7 @@ class GroomTool : public QWidget { Ui_GroomTool* ui_; QSharedPointer session_; - QSharedPointer groom_; + QSharedPointer groom_; QElapsedTimer timer_; From 5994fe0d019df596e69f63ffed4f2f0b33157c0e Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Thu, 2 Feb 2023 14:35:35 -0700 Subject: [PATCH 06/23] Remove unneeded QGroom class --- Studio/Groom/QGroom.cpp | 16 ---------------- Studio/Groom/QGroom.h | 32 -------------------------------- 2 files changed, 48 deletions(-) delete mode 100644 Studio/Groom/QGroom.cpp delete mode 100644 Studio/Groom/QGroom.h diff --git a/Studio/Groom/QGroom.cpp b/Studio/Groom/QGroom.cpp deleted file mode 100644 index c7ad229335..0000000000 --- a/Studio/Groom/QGroom.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "QGroom.h" - -namespace shapeworks { - -//--------------------------------------------------------------------------- -void QGroom::update_progress() -{ - Q_EMIT progress(static_cast(this->progress_)); -} - -//--------------------------------------------------------------------------- -QGroom::QGroom(ProjectHandle project) : Groom(project) -{ - -} -} \ No newline at end of file diff --git a/Studio/Groom/QGroom.h b/Studio/Groom/QGroom.h deleted file mode 100644 index 2105ad11de..0000000000 --- a/Studio/Groom/QGroom.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -#ifndef Q_MOC_RUN -#include -#endif - -namespace shapeworks { - -//! Qt Wrapper for Groom -/*! - * The QGroom class wraps the Groom class to provide a QObject with a progress signal - * - */ -class QGroom : public QObject, public Groom { - -Q_OBJECT; - -public: - - QGroom(ProjectHandle project); - -protected: - // override update_progress to emit q_signal - void update_progress(); - -Q_SIGNALS: - void progress(int); - -}; -} From 49ed309468f4e04c77cb1ca4e74116dee7725455 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Thu, 2 Feb 2023 17:32:50 -0700 Subject: [PATCH 07/23] Remove debugging --- Applications/shapeworks/Commands.cpp | 2 +- Libs/Optimize/Optimize.cpp | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Applications/shapeworks/Commands.cpp b/Applications/shapeworks/Commands.cpp index 48f5c1ecf1..03233dc791 100644 --- a/Applications/shapeworks/Commands.cpp +++ b/Applications/shapeworks/Commands.cpp @@ -54,7 +54,7 @@ static void setup_callbacks(bool show_progress, bool xml_status) { if (xml_status) { auto progress_callback = [](double progress, std::string message) { // show percentage complete - std::cout << fmt::format("{}{:.1f}", message, progress) + std::cout << fmt::format("{}{:.1f}", message, progress) << "\n"; std::cout.flush(); }; diff --git a/Libs/Optimize/Optimize.cpp b/Libs/Optimize/Optimize.cpp index 53b14dc98a..ab0fed6d06 100644 --- a/Libs/Optimize/Optimize.cpp +++ b/Libs/Optimize/Optimize.cpp @@ -201,10 +201,6 @@ int Optimize::SetParameters() { return false; } - - SW_ERROR("Inconsistency in parameters... m_domains_per_shape != m_number_of_particles.size()"); - return false; - // ensure that use_shape_statistics_after doesn't increase the particle count over what was specified for (int i = 0; i < m_number_of_particles.size(); i++) { m_use_shape_statistics_after = std::min(m_use_shape_statistics_after, m_number_of_particles[i]); From 987e4f8609e94bf320e042db2f2a5e937411713f Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Fri, 3 Feb 2023 11:39:25 -0700 Subject: [PATCH 08/23] Only show time remaining after 3 seconds. And now only update it once per second to reduce the up/down jitter --- Applications/shapeworks/Commands.cpp | 13 +++++-------- Libs/Optimize/Optimize.cpp | 24 +++++++++++------------- Libs/Optimize/Optimize.h | 4 ++-- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Applications/shapeworks/Commands.cpp b/Applications/shapeworks/Commands.cpp index 03233dc791..90a8e320cd 100644 --- a/Applications/shapeworks/Commands.cpp +++ b/Applications/shapeworks/Commands.cpp @@ -44,8 +44,8 @@ bool Example::execute(const optparse::Values &options, SharedCommandData &shared static void setup_callbacks(bool show_progress, bool xml_status) { if (show_progress) { auto progress_callback = [](double progress, std::string message) { - // show percentage complete - std::cout << fmt::format("{} ({:.1f}%) ", message, progress) << "\r"; + // show status message and percentage complete + std::cout << fmt::format("{} ({:.1f}%) \r", message, progress); std::cout.flush(); }; Logging::Instance().set_progress_callback(progress_callback); @@ -53,15 +53,14 @@ static void setup_callbacks(bool show_progress, bool xml_status) { if (xml_status) { auto progress_callback = [](double progress, std::string message) { - // show percentage complete - std::cout << fmt::format("{}{:.1f}", message, progress) - << "\n"; + // print status message and percentage complete + std::cout << fmt::format("{}{:.1f}\n", message, progress); std::cout.flush(); }; Logging::Instance().set_progress_callback(progress_callback); auto error_callback = [](std::string message) { - std::cout << fmt::format("{}", message) << "\n"; + std::cout << fmt::format("{}\n", message); std::cout.flush(); }; Logging::Instance().set_error_callback(error_callback); @@ -121,8 +120,6 @@ bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData bool isProject = StringUtils::hasSuffix(projectFile, "xlsx") || StringUtils::hasSuffix(projectFile, "swproj"); Optimize app; - app.SetShowProgress(show_progress); - setup_callbacks(show_progress, xml_status); if (isProject) { diff --git a/Libs/Optimize/Optimize.cpp b/Libs/Optimize/Optimize.cpp index ab0fed6d06..892099a7f5 100644 --- a/Libs/Optimize/Optimize.cpp +++ b/Libs/Optimize/Optimize.cpp @@ -2204,10 +2204,7 @@ void Optimize::ComputeTotalIterations() { void Optimize::UpdateProgress() { auto now = std::chrono::system_clock::now(); - // compute time since last update in milliseconds - auto time_since_last_update = std::chrono::duration_cast(now - m_last_update_time); - - if (time_since_last_update.count() > 100) { + if ((now - m_last_update_time) > std::chrono::milliseconds(100)) { m_last_update_time = now; std::chrono::duration elapsed_seconds = std::chrono::duration_cast(now - m_start_time); @@ -2230,19 +2227,20 @@ void Optimize::UpdateProgress() { message = fmt::format("{} : Particles: {}, Iteration: {} / {}", message, num_particles, stage_num_iterations, stage_total_iterations); - message = fmt::format("{} ({:02d}:{:02d}:{:02d} remaining)", message, hours, minutes, seconds); + + if ((now - m_last_remaining_update_time) > std::chrono::seconds(1)) { + m_remaining_time_message = fmt::format("({:02d}:{:02d}:{:02d} remaining)", hours, minutes, seconds); + m_last_remaining_update_time = now; + } + + // only show the time remaining if it's been more than 3 seconds + if (elapsed_seconds > std::chrono::seconds(3)) { + message = fmt::format("{} {}", message, m_remaining_time_message); + } double progress = static_cast(current_particle_iterations_) * 100 / static_cast(total_particle_iterations_); SW_PROGRESS(progress, message); - /* - if (m_show_progress) { - // show percentage complete - std::cout << fmt::format("{} ({:.2f}%) ", message, progress) << "\r"; - // flush stdout - std::cout.flush(); - } - */ } } } // namespace shapeworks diff --git a/Libs/Optimize/Optimize.h b/Libs/Optimize/Optimize.h index e45c174434..47976a97ba 100644 --- a/Libs/Optimize/Optimize.h +++ b/Libs/Optimize/Optimize.h @@ -303,7 +303,6 @@ class Optimize { vnl_vector_fixed TransformPoint(int domain, vnl_vector_fixed input); void UpdateProgress(); - void SetShowProgress(bool show) { m_show_progress = show; } protected: //! Set the iteration callback. Derived classes should override to set their own callback @@ -466,7 +465,8 @@ class Optimize { std::chrono::system_clock::time_point m_start_time; std::chrono::system_clock::time_point m_last_update_time; - bool m_show_progress = false; + std::chrono::system_clock::time_point m_last_remaining_update_time; + std::string m_remaining_time_message; }; From d006435d7353e3b3df75982a05bb32a8b3aa6359 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Mon, 9 Jan 2023 00:06:17 -0700 Subject: [PATCH 09/23] Return an empty mesh on warp mesh error instead of nullptr. (Fixes potential crash) --- Libs/Analyze/MeshGenerator.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Libs/Analyze/MeshGenerator.cpp b/Libs/Analyze/MeshGenerator.cpp index 1c78007b79..f28dd99174 100644 --- a/Libs/Analyze/MeshGenerator.cpp +++ b/Libs/Analyze/MeshGenerator.cpp @@ -84,6 +84,8 @@ MeshHandle MeshGenerator::build_mesh_from_points(const Eigen::VectorXd& shape, i if (!poly_data) { std::string message = "Unable to warp mesh"; SW_ERROR(message); + auto poly_data = vtkSmartPointer::New(); + mesh->set_poly_data(poly_data); mesh->set_error_message(message); return mesh; } From 8b10f40e26b1bcc4046796a16596dbc7b076bf1f Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Thu, 9 Feb 2023 00:02:00 -0700 Subject: [PATCH 10/23] Improve error reporting of mesh warp errors. --- Libs/Analyze/MeshGenerator.cpp | 8 ++++---- Libs/Mesh/MeshWarper.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Libs/Analyze/MeshGenerator.cpp b/Libs/Analyze/MeshGenerator.cpp index f28dd99174..fcc2cd5fd8 100644 --- a/Libs/Analyze/MeshGenerator.cpp +++ b/Libs/Analyze/MeshGenerator.cpp @@ -84,7 +84,7 @@ MeshHandle MeshGenerator::build_mesh_from_points(const Eigen::VectorXd& shape, i if (!poly_data) { std::string message = "Unable to warp mesh"; SW_ERROR(message); - auto poly_data = vtkSmartPointer::New(); + poly_data = vtkSmartPointer::New(); mesh->set_poly_data(poly_data); mesh->set_error_message(message); return mesh; @@ -124,9 +124,9 @@ MeshHandle MeshGenerator::build_mesh_from_image(ImageType::Pointer image, float mesh->set_poly_data(Mesh(marching->GetOutput()).clean().computeNormals().getVTKMesh()); } catch (itk::ExceptionObject& excep) { - std::cerr << "Exception caught!" << std::endl; - std::cerr << excep << std::endl; - mesh->set_error_message(std::string("Exception: ") + excep.what()); + auto message = std::string("Exception: ") + excep.what(); + SW_ERROR(message); + mesh->set_error_message(message); } return mesh; } diff --git a/Libs/Mesh/MeshWarper.cpp b/Libs/Mesh/MeshWarper.cpp index 1f39647d26..2abab322ea 100644 --- a/Libs/Mesh/MeshWarper.cpp +++ b/Libs/Mesh/MeshWarper.cpp @@ -45,7 +45,7 @@ vtkSmartPointer MeshWarper::build_mesh(const Eigen::MatrixXd& parti double* p = poly_data->GetPoint(i); if (std::isnan(p[0]) || std::isnan(p[1]) || std::isnan(p[2])) { this->warp_available_ = false; // failed - std::cerr << "Reconstruction Failed\n"; + SW_ERROR("Reconstruction failed. NaN detected in mesh."); return nullptr; } } @@ -438,7 +438,7 @@ bool MeshWarper::generate_warp() { Eigen::MatrixXd vertices = referenceMesh.points(); this->faces_ = referenceMesh.faces(); - // perform warp + // generate the warp if (!MeshWarper::generate_warp_matrix(vertices, this->faces_, this->vertices_, this->warp_)) { this->update_progress(1.0); this->warp_available_ = false; @@ -473,7 +473,7 @@ bool MeshWarper::generate_warp_matrix(Eigen::MatrixXd TV, Eigen::MatrixXi TF, co // faster and looks OK const int k = 2; if (!igl::biharmonic_coordinates(TV, TF, S, k, W)) { - std::cerr << "igl:biharmonic_coordinates failed\n"; + SW_ERROR("Mesh Warp Error: igl:biharmonic_coordinates failed"); return false; } // Throw away interior tet-vertices, keep weights and indices of boundary From 0efd74a5071681193224256f86926d345474e983 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Fri, 10 Feb 2023 12:57:57 -0700 Subject: [PATCH 11/23] Force conda reinstall. --- install_shapeworks.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install_shapeworks.sh b/install_shapeworks.sh index ac6011f315..62afcbea3a 100644 --- a/install_shapeworks.sh +++ b/install_shapeworks.sh @@ -2,6 +2,8 @@ # Installs conda environment for building ShapeWorks # +echo "Checking Conda" + SW_MAJOR_VERSION=6.4 echo "" From c6f50dfc64dbcf1613a8a69355f18b97d1735820 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Fri, 10 Feb 2023 15:04:28 -0700 Subject: [PATCH 12/23] Move scikit-learn to pip --- install_shapeworks.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install_shapeworks.sh b/install_shapeworks.sh index 62afcbea3a..98dfce72de 100644 --- a/install_shapeworks.sh +++ b/install_shapeworks.sh @@ -107,7 +107,6 @@ function install_conda() { CONDA_PACKAGES=(python=3.9.13 \ openblas=0.3.20 \ 'vtk=9.1.0=qt*' \ - scikit-learn=1.1.1 \ pip=22.1.2 ) @@ -187,6 +186,8 @@ function install_conda() { if ! pip install SimpleITK==2.1.1.2; then return 1; fi if ! pip install bokeh==2.4.3; then return 1; fi if ! pip install seaborn==0.11.2; then return 1; fi + if ! pip install scikit-learn==1.1.1; then return 1; fi + if ! pip install Python/DatasetUtilsPackage; then return 1; fi # install the local GirderConnector code as a package if ! pip install Python/DocumentationUtilsPackage; then return 1; fi # install shapeworks auto-documentation as a package if ! pip install Python/DataAugmentationUtilsPackage; then return 1; fi # install data augmentation code as a package From 58ca891039f53c13823577e5be33b6c42e075006 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Fri, 10 Feb 2023 22:07:20 -0700 Subject: [PATCH 13/23] Remove debug line --- install_shapeworks.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/install_shapeworks.sh b/install_shapeworks.sh index 98dfce72de..d25697bfaa 100644 --- a/install_shapeworks.sh +++ b/install_shapeworks.sh @@ -2,8 +2,6 @@ # Installs conda environment for building ShapeWorks # -echo "Checking Conda" - SW_MAJOR_VERSION=6.4 echo "" From 7a837197b324a3e6ffd7c87ef4b747eb43411ecf Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Mon, 13 Feb 2023 12:07:32 -0700 Subject: [PATCH 14/23] Fix #2003 - legacy reconstructor being used incorrectly. --- Studio/Analysis/AnalysisTool.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Studio/Analysis/AnalysisTool.cpp b/Studio/Analysis/AnalysisTool.cpp index 73b004a686..2cf773093a 100644 --- a/Studio/Analysis/AnalysisTool.cpp +++ b/Studio/Analysis/AnalysisTool.cpp @@ -920,17 +920,19 @@ void AnalysisTool::enable_actions(bool newly_enabled) { update_domain_alignment_box(); } - auto domain_types = session_->get_groomed_domain_types(); - bool image_domain = domain_types.size() > 0 && domain_types[0] == DomainType::Image; - ui_->distance_transfom_radio_button->setEnabled(session_->particles_present() && session_->get_groomed_present() && - image_domain); + if (session_->particles_present()) { + auto domain_types = session_->get_groomed_domain_types(); + bool image_domain = domain_types.size() > 0 && domain_types[0] == DomainType::Image; + ui_->distance_transfom_radio_button->setEnabled(session_->particles_present() && session_->get_groomed_present() && + image_domain); - ui_->mesh_warping_radio_button->setEnabled(session_->particles_present() && session_->get_groomed_present()); + ui_->mesh_warping_radio_button->setEnabled(session_->particles_present() && session_->get_groomed_present()); - if (!ui_->mesh_warping_radio_button->isEnabled()) { - ui_->legacy_radio_button->setChecked(true); + if (!ui_->mesh_warping_radio_button->isEnabled()) { + ui_->legacy_radio_button->setChecked(true); + } + reconstruction_method_changed(); } - reconstruction_method_changed(); update_group_boxes(); ui_->sampleSpinBox->setMaximum(session_->get_num_shapes() - 1); From 0acf96aced75f935adbf56d5738c9cc2cf21825f Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Mon, 13 Feb 2023 13:31:01 -0700 Subject: [PATCH 15/23] Add custom slider to fix #2002 --- Studio/CMakeLists.txt | 2 + Studio/Interface/CustomSlider.cpp | 68 +++++++++++++++++++++++++++++++ Studio/Interface/CustomSlider.h | 14 +++++++ 3 files changed, 84 insertions(+) create mode 100644 Studio/Interface/CustomSlider.cpp create mode 100644 Studio/Interface/CustomSlider.h diff --git a/Studio/CMakeLists.txt b/Studio/CMakeLists.txt index ea09d5e110..56b3c99924 100644 --- a/Studio/CMakeLists.txt +++ b/Studio/CMakeLists.txt @@ -169,6 +169,7 @@ SET(STUDIO_UTILS_MOC_HDRS SET(STUDIO_INTERFACE_SRCS Interface/CompareWidget.cpp + Interface/CustomSlider.cpp Interface/ExportImageDialog.cpp Interface/KeyboardShortcuts.cpp Interface/LogWindow.cpp @@ -182,6 +183,7 @@ SET(STUDIO_INTERFACE_SRCS ) SET(STUDIO_INTERFACE_MOC_HDRS Interface/CompareWidget.h + Interface/CustomSlider.h Interface/ExportImageDialog.h Interface/KeyboardShortcuts.h Interface/LogWindow.h diff --git a/Studio/Interface/CustomSlider.cpp b/Studio/Interface/CustomSlider.cpp new file mode 100644 index 0000000000..ae2ee9a6c8 --- /dev/null +++ b/Studio/Interface/CustomSlider.cpp @@ -0,0 +1,68 @@ + + +#include "CustomSlider.h" + +#include +#include +#include +#include +#include + +#include "math.h" + +CustomSlider::CustomSlider(QWidget* parent) : QSlider(parent) { + this->setStyleSheet( + "\ + QSlider::groove:horizontal {\ + height: 8px; /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ \ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B1B1B1, stop:1 #c4c4c4);\ + margin: 2px 0;\ + }\ + \ + QSlider::handle:horizontal {\ + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);\ + border: 1px solid #5c5c5c;\ + width: 12px;\ + height: 12px; \ + margin: -2px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ \ + border-radius: 3px;\ + }\ + "); +}; + +void CustomSlider::paintEvent(QPaintEvent* ev) { + QStylePainter p(this); + QStyleOptionSlider opt; + initStyleOption(&opt); + + QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + + // draw tick marks + // do this manually because they are very badly behaved with style sheets + int interval = tickInterval(); + if (interval == 0) { + interval = pageStep(); + } + + if (tickPosition() != NoTicks) { + for (int i = minimum(); i <= maximum(); i += interval) { + int x = + std::round((double)((double)((double)(i - this->minimum()) / (double)(this->maximum() - this->minimum())) * + (double)(this->width() - handle.width()) + + (double)(handle.width() / 2.0))) - + 1; + int h = 4; + p.setPen(QColor("#a5a294")); + if (tickPosition() == TicksBothSides || tickPosition() == TicksAbove) { + int y = this->rect().top(); + p.drawLine(x, y, x, y + h); + } + if (tickPosition() == TicksBothSides || tickPosition() == TicksBelow) { + int y = this->rect().bottom(); + p.drawLine(x, y, x, y - h); + } + } + } + + QSlider::paintEvent(ev); +} diff --git a/Studio/Interface/CustomSlider.h b/Studio/Interface/CustomSlider.h new file mode 100644 index 0000000000..2c76041cbe --- /dev/null +++ b/Studio/Interface/CustomSlider.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +// adapted from: +// https://stackoverflow.com/questions/69890284/qslider-in-qt-misbehaves-in-new-macos-monterey-v12-0-1-any-workaround/69890285#69890285 +class CustomSlider : public QSlider { + public: + explicit CustomSlider(Qt::Orientation orientation, QWidget* parent = nullptr) : QSlider(orientation, parent){}; + explicit CustomSlider(QWidget* parent = nullptr); + + protected: + virtual void paintEvent(QPaintEvent* ev); +}; \ No newline at end of file From a4a34ec1976a8dca0d491be601b7eb8a3787ae11 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Mon, 13 Feb 2023 14:38:48 -0700 Subject: [PATCH 16/23] Fix #2002 by using a custom slider --- Studio/Analysis/AnalysisTool.ui | 17 ++++++++--------- Studio/Interface/CustomSlider.cpp | 8 ++++---- Studio/Interface/ShapeWorksStudioApp.ui | 7 ++++++- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Studio/Analysis/AnalysisTool.ui b/Studio/Analysis/AnalysisTool.ui index 6976368958..7eaad8b4bd 100644 --- a/Studio/Analysis/AnalysisTool.ui +++ b/Studio/Analysis/AnalysisTool.ui @@ -535,7 +535,7 @@ QWidget#particles_panel { - 2 + 0 @@ -603,7 +603,7 @@ QWidget#particles_panel { - + Group slider @@ -1059,16 +1059,10 @@ QWidget#particles_panel { - + false - - - 100 - 0 - - Standard deviation slider @@ -2190,6 +2184,11 @@ Reference Domain
jkqtplotter/jkqtplotter.h
1 + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
diff --git a/Studio/Interface/CustomSlider.cpp b/Studio/Interface/CustomSlider.cpp index ae2ee9a6c8..0ea435658a 100644 --- a/Studio/Interface/CustomSlider.cpp +++ b/Studio/Interface/CustomSlider.cpp @@ -14,17 +14,17 @@ CustomSlider::CustomSlider(QWidget* parent) : QSlider(parent) { this->setStyleSheet( "\ QSlider::groove:horizontal {\ - height: 8px; /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ \ + border: 1px solid #262626;\ + height: 3px;\ background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B1B1B1, stop:1 #c4c4c4);\ - margin: 2px 0;\ + margin: 0 5px;\ }\ \ QSlider::handle:horizontal {\ background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);\ border: 1px solid #5c5c5c;\ width: 12px;\ - height: 12px; \ - margin: -2px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ \ + margin: -8px -6px;\ border-radius: 3px;\ }\ "); diff --git a/Studio/Interface/ShapeWorksStudioApp.ui b/Studio/Interface/ShapeWorksStudioApp.ui index bb411e0475..4d824ebc34 100644 --- a/Studio/Interface/ShapeWorksStudioApp.ui +++ b/Studio/Interface/ShapeWorksStudioApp.ui @@ -368,7 +368,7 @@ QToolBar QToolButton::checked {
- + 100 @@ -1215,6 +1215,11 @@ QToolBar QToolButton::checked { + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
QVTKOpenGLNativeWidget QWidget From b0b3d48107f82b9e99a2e8b9f910962c8478e4b4 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Mon, 13 Feb 2023 14:45:50 -0700 Subject: [PATCH 17/23] Swap out remaining QSliders for CustomSlider --- Studio/Data/DataTool.cpp | 2 +- Studio/Data/DataTool.ui | 22 ++++++++++++++++++---- Studio/Data/PreferencesWindow.cpp | 2 +- Studio/Data/PreferencesWindow.ui | 13 ++++++++++--- Studio/Groom/GroomTool.cpp | 4 ++-- Studio/Groom/GroomTool.ui | 11 +++++++++-- Studio/Interface/CompareWidget.cpp | 2 +- Studio/Interface/CompareWidget.ui | 11 +++++++++-- Studio/Interface/ShapeWorksStudioApp.cpp | 12 ++++++------ Studio/Interface/ShapeWorksStudioApp.h | 8 ++++---- 10 files changed, 61 insertions(+), 26 deletions(-) diff --git a/Studio/Data/DataTool.cpp b/Studio/Data/DataTool.cpp index 020e971c58..ed89cc1550 100644 --- a/Studio/Data/DataTool.cpp +++ b/Studio/Data/DataTool.cpp @@ -109,7 +109,7 @@ void DataTool::set_session(QSharedPointer session) { connect(session.data(), &Session::ffc_changed, this, &DataTool::update_ffc_table); landmark_table_model_->set_session(session); - connect(ui_->ffc_brush_size_, &QSlider::valueChanged, session.data(), &Session::set_ffc_paint_size); + connect(ui_->ffc_brush_size_, &CustomSlider::valueChanged, session.data(), &Session::set_ffc_paint_size); connect(ui_->ffc_included_mode_, &QRadioButton::toggled, session.data(), &Session::set_ffc_paint_mode_inclusive); connect(ui_->ffc_active_, &QCheckBox::toggled, session.data(), &Session::set_ffc_paint_active); diff --git a/Studio/Data/DataTool.ui b/Studio/Data/DataTool.ui index 3b20dd691a..eaed9088c7 100644 --- a/Studio/Data/DataTool.ui +++ b/Studio/Data/DataTool.ui @@ -381,8 +381,8 @@ QWidget#specificity_panel { 0 0 - 470 - 340 + 464 + 330 @@ -848,7 +848,7 @@ QWidget#specificity_panel {
- + 5 @@ -1053,7 +1053,14 @@ QWidget#specificity_panel { - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> @@ -1078,6 +1085,13 @@ QWidget#specificity_panel { + + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
+
diff --git a/Studio/Data/PreferencesWindow.cpp b/Studio/Data/PreferencesWindow.cpp index 3d49ea4c33..48be5ad709 100644 --- a/Studio/Data/PreferencesWindow.cpp +++ b/Studio/Data/PreferencesWindow.cpp @@ -46,7 +46,7 @@ PreferencesWindow::PreferencesWindow(QWidget* parent, Preferences& prefs) : pref &PreferencesWindow::save_to_preferences); connect(ui_->orientation_marker_corner, qOverload(&QComboBox::currentIndexChanged), this, &PreferencesWindow::save_to_preferences); - connect(ui_->geodesic_cache_multiplier, &QSlider::valueChanged, this, &PreferencesWindow::save_to_preferences); + connect(ui_->geodesic_cache_multiplier, &CustomSlider::valueChanged, this, &PreferencesWindow::save_to_preferences); connect(ui_->color_map, qOverload(&QComboBox::currentIndexChanged), this, &PreferencesWindow::save_to_preferences); connect(ui_->particle_colors, qOverload(&QComboBox::currentIndexChanged), this, diff --git a/Studio/Data/PreferencesWindow.ui b/Studio/Data/PreferencesWindow.ui index 99781038d7..16259c0e9c 100644 --- a/Studio/Data/PreferencesWindow.ui +++ b/Studio/Data/PreferencesWindow.ui @@ -107,7 +107,7 @@ - + 0 @@ -200,7 +200,7 @@ - + 100 @@ -240,7 +240,7 @@ - + 1 @@ -587,6 +587,13 @@ + + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
+
pca_range pca_steps diff --git a/Studio/Groom/GroomTool.cpp b/Studio/Groom/GroomTool.cpp index 411ce09f80..a4b51d8a6c 100644 --- a/Studio/Groom/GroomTool.cpp +++ b/Studio/Groom/GroomTool.cpp @@ -73,13 +73,13 @@ GroomTool::GroomTool(Preferences& prefs, Telemetry& telemetry) : preferences_(pr "Set the adaptivity of remeshing, higher will allocate more triangles around areas of high curvature."); // connect percent controls - connect(ui_->remesh_percent_slider, &QSlider::valueChanged, this, + connect(ui_->remesh_percent_slider, &CustomSlider::valueChanged, this, [this](int value) { ui_->remesh_percent_spinbox->setValue(value); }); connect(ui_->remesh_percent_spinbox, qOverload(&QDoubleSpinBox::valueChanged), this, [=](double value) { ui_->remesh_percent_slider->setValue(static_cast(value)); }); // connect gradation controls - connect(ui_->remesh_gradation_slider, &QSlider::valueChanged, this, + connect(ui_->remesh_gradation_slider, &CustomSlider::valueChanged, this, [=](int value) { ui_->remesh_gradation_spinbox->setValue(value / 50.0); }); connect(ui_->remesh_gradation_spinbox, qOverload(&QDoubleSpinBox::valueChanged), this, [=](double value) { ui_->remesh_gradation_slider->setValue(static_cast(value * 50.0)); }); diff --git a/Studio/Groom/GroomTool.ui b/Studio/Groom/GroomTool.ui index 747718b6f6..30880f04eb 100644 --- a/Studio/Groom/GroomTool.ui +++ b/Studio/Groom/GroomTool.ui @@ -1607,7 +1607,7 @@ QWidget#domain_panel { - + 100 @@ -1620,7 +1620,7 @@ QWidget#domain_panel { - + 100 @@ -2013,6 +2013,13 @@ QWidget#domain_panel { + + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
+
mesh_fill_holes mesh_smooth diff --git a/Studio/Interface/CompareWidget.cpp b/Studio/Interface/CompareWidget.cpp index a771dde412..f900d03f4c 100644 --- a/Studio/Interface/CompareWidget.cpp +++ b/Studio/Interface/CompareWidget.cpp @@ -12,7 +12,7 @@ CompareWidget::CompareWidget(QWidget *parent) : QWidget(parent), ui_(new Ui::Com connect(ui_->original, &QCheckBox::toggled, this, &CompareWidget::settings_changed); connect(ui_->groomed, &QCheckBox::toggled, this, &CompareWidget::settings_changed); connect(ui_->reconstructed, &QCheckBox::toggled, this, &CompareWidget::settings_changed); - connect(ui_->opacity, &QSlider::valueChanged, this, &CompareWidget::settings_changed); + connect(ui_->opacity, &CustomSlider::valueChanged, this, &CompareWidget::settings_changed); connect(ui_->mean_shape, &QCheckBox::toggled, this, &CompareWidget::settings_changed); } diff --git a/Studio/Interface/CompareWidget.ui b/Studio/Interface/CompareWidget.ui index ad60f1aa2d..e14b272c06 100644 --- a/Studio/Interface/CompareWidget.ui +++ b/Studio/Interface/CompareWidget.ui @@ -7,7 +7,7 @@ 0 0 470 - 159 + 172 @@ -15,7 +15,7 @@ - + 100 @@ -74,6 +74,13 @@ + + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
+
diff --git a/Studio/Interface/ShapeWorksStudioApp.cpp b/Studio/Interface/ShapeWorksStudioApp.cpp index 40a0665b9d..7336b5fb23 100644 --- a/Studio/Interface/ShapeWorksStudioApp.cpp +++ b/Studio/Interface/ShapeWorksStudioApp.cpp @@ -729,7 +729,7 @@ void ShapeWorksStudioApp::create_glyph_submenu() { layout->addWidget(glyph_size_label_, 0, 1, 1, 1); layout->addWidget(glyph_quality_label_, 1, 1, 1, 1); - glyph_size_slider_ = new QSlider(widget); + glyph_size_slider_ = new CustomSlider(widget); glyph_size_slider_->setOrientation(Qt::Horizontal); glyph_size_slider_->setMinimum(1); glyph_size_slider_->setMaximum(100); @@ -742,7 +742,7 @@ void ShapeWorksStudioApp::create_glyph_submenu() { glyph_arrow_scale_ = new QCheckBox("Scale arrows"); - glyph_quality_slider_ = new QSlider(widget); + glyph_quality_slider_ = new CustomSlider(widget); glyph_quality_slider_->setMinimum(1); glyph_quality_slider_->setMaximum(20); glyph_quality_slider_->setPageStep(3); @@ -766,8 +766,8 @@ void ShapeWorksStudioApp::create_glyph_submenu() { glyph_quality_label_->setText(QString::number(preferences_.get_glyph_quality())); glyph_size_label_->setText(QString::number(preferences_.get_glyph_size())); - connect(glyph_size_slider_, &QSlider::valueChanged, this, &ShapeWorksStudioApp::handle_glyph_changed); - connect(glyph_quality_slider_, &QSlider::valueChanged, this, &ShapeWorksStudioApp::handle_glyph_changed); + connect(glyph_size_slider_, &CustomSlider::valueChanged, this, &ShapeWorksStudioApp::handle_glyph_changed); + connect(glyph_quality_slider_, &CustomSlider::valueChanged, this, &ShapeWorksStudioApp::handle_glyph_changed); connect(glyph_auto_size_, &QCheckBox::clicked, this, &ShapeWorksStudioApp::handle_glyph_changed); connect(glyph_arrow_scale_, &QCheckBox::clicked, this, &ShapeWorksStudioApp::handle_glyph_changed); @@ -829,7 +829,7 @@ void ShapeWorksStudioApp::create_iso_submenu() { QLabel* size_label = new QLabel(text); layout->addWidget(size_label, row, 0, 1, 1); - QSlider* slider = new QSlider(widget); + CustomSlider* slider = new CustomSlider(widget); slider->setOrientation(Qt::Horizontal); slider->setMinimum(1); slider->setMaximum(100); @@ -838,7 +838,7 @@ void ShapeWorksStudioApp::create_iso_submenu() { slider->setTickInterval(10); slider->setValue(100); slider->setMinimumWidth(200); - connect(slider, &QSlider::valueChanged, this, &ShapeWorksStudioApp::handle_opacity_changed); + connect(slider, &CustomSlider::valueChanged, this, &ShapeWorksStudioApp::handle_opacity_changed); layout->addWidget(slider, row, 1, 1, 1); widget->setLayout(layout); diff --git a/Studio/Interface/ShapeWorksStudioApp.h b/Studio/Interface/ShapeWorksStudioApp.h index 16ecf0972e..07663bde63 100644 --- a/Studio/Interface/ShapeWorksStudioApp.h +++ b/Studio/Interface/ShapeWorksStudioApp.h @@ -20,9 +20,9 @@ #include #include #include -#include #include #include +#include // Forward Qt class declarations class Ui_ShapeWorksStudioApp; @@ -239,8 +239,8 @@ class ShapeWorksStudioApp : public QMainWindow { QSharedPointer wheel_event_forwarder_; // programmatic UI elements - QSlider* glyph_size_slider_; - QSlider* glyph_quality_slider_; + CustomSlider* glyph_size_slider_; + CustomSlider* glyph_quality_slider_; QLabel* glyph_size_label_; QLabel* glyph_quality_label_; QCheckBox* glyph_auto_size_; @@ -250,7 +250,7 @@ class ShapeWorksStudioApp : public QMainWindow { QPointer status_bar_; QSharedPointer splash_screen_; QErrorMessage error_message_dialog_; - std::vector iso_opacity_sliders_; + std::vector iso_opacity_sliders_; std::vector domain_particle_checkboxes_; QString current_message_; From b446d50ad091a75edb49905107738056c6ff730d Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Tue, 14 Feb 2023 13:13:41 -0700 Subject: [PATCH 18/23] Set a minimum size for slider so that it doesn't get cut off --- Studio/Interface/CustomSlider.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Studio/Interface/CustomSlider.cpp b/Studio/Interface/CustomSlider.cpp index 0ea435658a..49a00c0955 100644 --- a/Studio/Interface/CustomSlider.cpp +++ b/Studio/Interface/CustomSlider.cpp @@ -13,6 +13,9 @@ CustomSlider::CustomSlider(QWidget* parent) : QSlider(parent) { this->setStyleSheet( "\ + QSlider {\ + min-height: 24px\ + }\ QSlider::groove:horizontal {\ border: 1px solid #262626;\ height: 3px;\ From b307215bb8b7f2822ba6865c376d957198a96fe6 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Tue, 14 Feb 2023 13:39:55 -0700 Subject: [PATCH 19/23] Remove logging message --- Libs/Optimize/Constraints.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Libs/Optimize/Constraints.cpp b/Libs/Optimize/Constraints.cpp index d7348d910a..a1143523f0 100644 --- a/Libs/Optimize/Constraints.cpp +++ b/Libs/Optimize/Constraints.cpp @@ -777,7 +777,6 @@ bool Constraints::hasConstraints() { return true; } - SW_LOG("no constraints"); return false; } From cc4c123a288ceca09487f372b06afd54e3da339f Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Tue, 14 Feb 2023 13:52:18 -0700 Subject: [PATCH 20/23] Add SW_LOG_ONCE macro and use with telemetry --- Libs/Common/Logging.h | 9 +++++++++ Studio/Data/Telemetry.cpp | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Libs/Common/Logging.h b/Libs/Common/Logging.h index 9d44f4e6af..3b69f80f07 100644 --- a/Libs/Common/Logging.h +++ b/Libs/Common/Logging.h @@ -187,4 +187,13 @@ class Logging { //! Close session macro #define SW_CLOSE_LOG() shapeworks::Logging::Instance().close_log(); +//! Log once macro, will only log the message once +#define SW_LOG_ONCE(message, ...) \ +{ \ + static bool logged = false; \ + if (!logged) { \ + SW_LOG(message, ##__VA_ARGS__); \ + logged = true; \ + } \ +} } // namespace shapeworks diff --git a/Studio/Data/Telemetry.cpp b/Studio/Data/Telemetry.cpp index 21a931a8f6..081c48d83b 100644 --- a/Studio/Data/Telemetry.cpp +++ b/Studio/Data/Telemetry.cpp @@ -43,12 +43,12 @@ void Telemetry::record_event(const QString& name, const QVariantMap& params) { QString api_secret{GA_API_SECRET}; if (measurement_id.isEmpty() || api_secret.isEmpty()) { - SW_LOG("Telemetry disabled, no measurement id or api secret"); + SW_LOG_ONCE("Telemetry disabled, no measurement id or api secret"); return; } if (!prefs_.get_telemetry_enabled()) { - SW_LOG("Telemetry disabled by preferences"); + SW_LOG_ONCE("Telemetry disabled by preferences"); return; } From 43efba53e9f136394eefbe8a4da9d9a9b91b144e Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Tue, 14 Feb 2023 23:37:59 -0700 Subject: [PATCH 21/23] Fix #2005 by replacing slashes --- Libs/Project/ProjectUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libs/Project/ProjectUtils.cpp b/Libs/Project/ProjectUtils.cpp index 4a672d3295..a6beef55b9 100644 --- a/Libs/Project/ProjectUtils.cpp +++ b/Libs/Project/ProjectUtils.cpp @@ -140,7 +140,7 @@ StringList ProjectUtils::get_values(StringList prefixes, StringList domain_names for (auto& [key, value] : key_map) { for (const auto& prefix : prefixes) { if (key == prefix + domain) { - values.push_back(value); + values.push_back(StringUtils::replace_string(value, "\\", "/")); } } } From 67a42e48987b4708928aba9f98aee92d285c3264 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Tue, 14 Feb 2023 23:58:15 -0700 Subject: [PATCH 22/23] Fix #2006 by adding try/catch --- Studio/Interface/UpdateChecker.cpp | 87 ++++++++++++++++-------------- Studio/Interface/UpdateChecker.h | 6 +-- 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/Studio/Interface/UpdateChecker.cpp b/Studio/Interface/UpdateChecker.cpp index 84fc426038..f861029c6f 100644 --- a/Studio/Interface/UpdateChecker.cpp +++ b/Studio/Interface/UpdateChecker.cpp @@ -57,49 +57,56 @@ void UpdateChecker::run_update_check() { void UpdateChecker::handleNetworkReply(QNetworkReply* reply) { std::string response = QString(reply->readAll()).toStdString(); + response = ""; + // get the json response - auto j = json::parse(response); - - std::string platform = StudioUtils::get_platform_string().toStdString(); - - int major = j[platform]["major"].get(); - int minor = j[platform]["minor"].get(); - int patch = j[platform]["patch"].get(); - QString message = QString::fromStdString(j[platform]["message"].get()); - - bool update_available = false; - if (major > SHAPEWORKS_MAJOR_VERSION) { - update_available = true; - } else if (major == SHAPEWORKS_MAJOR_VERSION && minor > SHAPEWORKS_MINOR_VERSION) { - update_available = true; - } else if (major == SHAPEWORKS_MAJOR_VERSION && minor == SHAPEWORKS_MINOR_VERSION && - patch > SHAPEWORKS_PATCH_VERSION) { - update_available = true; - } + try { + auto j = json::parse(response); + std::string platform = StudioUtils::get_platform_string().toStdString(); + + int major = j[platform]["major"].get(); + int minor = j[platform]["minor"].get(); + int patch = j[platform]["patch"].get(); + QString message = QString::fromStdString(j[platform]["message"].get()); + + bool update_available = false; + if (major > SHAPEWORKS_MAJOR_VERSION) { + update_available = true; + } else if (major == SHAPEWORKS_MAJOR_VERSION && minor > SHAPEWORKS_MINOR_VERSION) { + update_available = true; + } else if (major == SHAPEWORKS_MAJOR_VERSION && minor == SHAPEWORKS_MINOR_VERSION && + patch > SHAPEWORKS_PATCH_VERSION) { + update_available = true; + } - if (update_available) { - auto url = QString("https://github.com/SCIInstitute/ShapeWorks/releases/latest"); - - auto title = QString("New version available"); - setWindowTitle(title); - ui_->label->setTextFormat(Qt::RichText); - ui_->label->setOpenExternalLinks(true); - ui_->label->setTextInteractionFlags(Qt::TextBrowserInteraction); - ui_->label->setText(QString("A new version of ShapeWorks is available.

" - "To download the latest version, please visit:

%1

" + - message) - .arg(url)); - - show(); - raise(); - - } else { - if (manual_trigger_) { - // show a messagebox saying there is no update - auto title = QString("No update available"); - auto info = QString("You are running the latest version of ShapeWorks."); - QMessageBox::information(nullptr, title, info, QMessageBox::Ok); + if (update_available) { + auto url = QString("https://github.com/SCIInstitute/ShapeWorks/releases/latest"); + + auto title = QString("New version available"); + setWindowTitle(title); + ui_->label->setTextFormat(Qt::RichText); + ui_->label->setOpenExternalLinks(true); + ui_->label->setTextInteractionFlags(Qt::TextBrowserInteraction); + ui_->label->setText(QString("A new version of ShapeWorks is available.

" + "To download the latest version, please visit:

%1

" + + message) + .arg(url)); + + show(); + raise(); + + } else { + if (manual_trigger_) { + // show a messagebox saying there is no update + auto title = QString("No update available"); + auto info = QString("You are running the latest version of ShapeWorks."); + QMessageBox::information(nullptr, title, info, QMessageBox::Ok); + } } + + } catch (json::exception& e) { + SW_LOG("Unable to check for updates: " + std::string(e.what())); + return; } } } // namespace shapeworks \ No newline at end of file diff --git a/Studio/Interface/UpdateChecker.h b/Studio/Interface/UpdateChecker.h index 2fd6042411..0393efe353 100644 --- a/Studio/Interface/UpdateChecker.h +++ b/Studio/Interface/UpdateChecker.h @@ -1,10 +1,10 @@ #pragma once +#include + #include #include -#include - namespace Ui { class UpdateChecker; } @@ -25,7 +25,6 @@ class UpdateChecker : public QDialog { void run_auto_update_check(); void run_manual_update_check(); - public Q_SLOTS: void handleNetworkReply(QNetworkReply* reply); @@ -38,7 +37,6 @@ class UpdateChecker : public QDialog { Ui::UpdateChecker* ui_; Preferences& prefs_; - }; } // namespace shapeworks \ No newline at end of file From edfaf5d5f8bfaeb60c67a809bf75d2966fcb6246 Mon Sep 17 00:00:00 2001 From: Alan Morris Date: Wed, 15 Feb 2023 09:33:37 -0700 Subject: [PATCH 23/23] Remove trigger --- Studio/Interface/UpdateChecker.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Studio/Interface/UpdateChecker.cpp b/Studio/Interface/UpdateChecker.cpp index f861029c6f..41d4e9008d 100644 --- a/Studio/Interface/UpdateChecker.cpp +++ b/Studio/Interface/UpdateChecker.cpp @@ -57,8 +57,6 @@ void UpdateChecker::run_update_check() { void UpdateChecker::handleNetworkReply(QNetworkReply* reply) { std::string response = QString(reply->readAll()).toStdString(); - response = ""; - // get the json response try { auto j = json::parse(response);