From 3717bdb2e60e7f0742ed8489c08c7ef009670a2e Mon Sep 17 00:00:00 2001 From: Benjamin Bercovici Date: Tue, 29 Jan 2019 16:38:02 -0700 Subject: [PATCH] improved SBGATMassProperties and updated docs --- README.md | 15 +- SbgatCore/CMakeLists.txt | 5 +- .../include/SbgatCore/SBGATMassProperties.hpp | 158 ++++++----- .../include/SbgatCore/SBGATObsLightcurve.hpp | 2 +- SbgatCore/include/SbgatCore/SBGATObsRadar.hpp | 2 +- .../SbgatCore/SBGATPolyhedronGravityModel.hpp | 2 +- .../include/SbgatCore/SBGATSphericalHarmo.hpp | 2 +- SbgatCore/include/SbgatCore/SBGATSrpYorp.hpp | 5 +- SbgatCore/source/SBGATMassProperties.cpp | 138 ++++++++-- SbgatCore/source/SBGATObsLightcurve.cpp | 2 +- SbgatCore/source/SBGATObsRadar.cpp | 5 +- .../source/SBGATPolyhedronGravityModel.cpp | 5 +- SbgatCore/source/SBGATSphericalHarmo.cpp | 5 +- SbgatCore/source/SBGATSrpYorp.cpp | 5 +- SbgatGui/include/Mainwindow.hpp | 27 +- SbgatGui/source/Mainwindow.cpp | 255 ++++++++++-------- Tests/source/Tests.cpp | 6 +- 17 files changed, 400 insertions(+), 239 deletions(-) diff --git a/README.md b/README.md index 27587c7..6066d30 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ before reinstalling. ### Linux & Mac users -[Refer to the detailed installation instructions](https://github.com/bbercovici/SBGAT/wiki/2:-Compile-and-install-SBGAT-dependencies). +[Refer to the detailed installation instructions](https://github.com/bbercovici/SBGAT/wiki/2:-Compiling-and-installing-SBGAT-dependencies). ## Getting updates @@ -75,6 +75,19 @@ to apply the update (if any). ## Changelog +### [SBGAT 2.01.2](https://github.com/bbercovici/SBGAT/releases/tag/2.01.2) + +#### New + +- `SBGATMassProperties` now offers a method to save the computed mass properties to a JSON file (`SBGATMassProperties::SaveMassProperties`) +- A static method evaluating and saving the mass properties of the provided shape is now provided (`SBGATMassProperties::ComputeAndSaveMassProperties`) +- The `Measures` menu in `SbgatGUI` has been augmented with a `Save geometric measures` action + +#### Improvements +- **The inertia tensor normalization has changed.** When computing the mass properties of a given small body, the following normalization is now applied to the inertia tensor: `I_norm = I / (mass * r_avg ^ 2)` where `r_avg = cbrt(3/4 *Volume/pi)`. `r_avg` is now computed along with the other properties within `SBGATMassProperties`. +- The parallel axis theorem is no-longer applied to the small body. That is, the inertia will always be expressed about (0,0,0). +- Several GUI minors bug fixes + ### [SBGAT 2.01.1](https://github.com/bbercovici/SBGAT/releases/tag/2.01.1) ### New: diff --git a/SbgatCore/CMakeLists.txt b/SbgatCore/CMakeLists.txt index 9fa7efd..0ed9cc7 100644 --- a/SbgatCore/CMakeLists.txt +++ b/SbgatCore/CMakeLists.txt @@ -44,8 +44,8 @@ # # ################################################################################ - -cmake_minimum_required(VERSION 3.12.0) + +cmake_minimum_required(VERSION 3.13.3) # Building procedure get_filename_component(dirName ${CMAKE_CURRENT_SOURCE_DIR} NAME) @@ -93,6 +93,7 @@ endif() # Find VTK Package find_package(VTK REQUIRED) + include(${VTK_USE_FILE}) # Find YORPLib diff --git a/SbgatCore/include/SbgatCore/SBGATMassProperties.hpp b/SbgatCore/include/SbgatCore/SBGATMassProperties.hpp index ab06ffd..68c90ce 100644 --- a/SbgatCore/include/SbgatCore/SBGATMassProperties.hpp +++ b/SbgatCore/include/SbgatCore/SBGATMassProperties.hpp @@ -3,7 +3,7 @@ Program: Small Body Geophysical Analysis Module: SBGATMassProperties.hpp - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. @@ -48,7 +48,7 @@ class VTKFILTERSCORE_EXPORT SBGATMassProperties : public vtkPolyDataAlgorithm{ void PrintTrailer(std::ostream& os, vtkIndent indent) override; /** - * Compute and return the volume. + * Compute and return the volume (m or km^3) */ double GetVolume() {this->Update(); return this->Volume;} @@ -80,17 +80,20 @@ class VTKFILTERSCORE_EXPORT SBGATMassProperties : public vtkPolyDataAlgorithm{ /** * Compute and return the area. */ - double GetSurfaceArea() {this->Update(); return this->SurfaceArea;} + double GetSurfaceArea() {this->Update(); return this->SurfaceArea; + } /** * Compute and return the min cell area. */ - double GetMinCellArea() {this->Update(); return this->MinCellArea;} + double GetMinCellArea() {this->Update(); return this->MinCellArea; + } /** * Compute and return the max cell area. */ - double GetMaxCellArea() {this->Update(); return this->MaxCellArea;} + double GetMaxCellArea() {this->Update(); return this->MaxCellArea; + } /** @@ -105,7 +108,8 @@ class VTKFILTERSCORE_EXPORT SBGATMassProperties : public vtkPolyDataAlgorithm{ * is one. This number is always >= 1.0. */ double GetNormalizedShapeIndex() - {this->Update(); return this->NormalizedShapeIndex;} + {this->Update(); return this->NormalizedShapeIndex; + } /** @@ -113,84 +117,114 @@ class VTKFILTERSCORE_EXPORT SBGATMassProperties : public vtkPolyDataAlgorithm{ * evaluated in the frame of origin assuming a constant density distribution * across the shape */ - arma::vec::fixed<3> GetCenterOfMass(){ - this -> Update(); return this -> center_of_mass;} + const arma::vec::fixed<3> & GetCenterOfMass(){ + this -> Update(); return this -> center_of_mass; + } - /** + /** * Compute and return the coordinates of the center of mass * evaluated in the frame of origin assuming a constant density distribution * across the shape */ - void GetCenterOfMass(double * com){ - this -> Update(); - com[0] = this -> center_of_mass(0); - com[1] = this -> center_of_mass(1); - com[2] = this -> center_of_mass(2); - } - + void GetCenterOfMass(double * com){ + this -> Update(); + com[0] = this -> center_of_mass(0); + com[1] = this -> center_of_mass(1); + com[2] = this -> center_of_mass(2); + } /** - * Compute and return the inertia tensor + * Compute and return the dimensionless inertia tensor * at the barycenter, evaluated in the frame of origin assuming a constant density distribution - * across the shape + * across the shape. The normalization applied to the inertia tensor is I_norm = I / (mass * r_avg ^ 2) where r_avg = cbrt(3/4*Volume/pi) */ - arma::mat::fixed<3,3> GetInertiaTensor(){ - this -> Update(); return this -> inertia_tensor;} + arma::mat::fixed<3,3> GetInertiaTensor(){ + this -> Update(); return this -> inertia_tensor; + } /** * Compute and return the principal axes of the inertia tensor */ - arma::mat::fixed<3,3> GetPrincipalAxes(){ - this -> Update(); return this -> principal_axes;} + arma::mat::fixed<3,3> GetPrincipalAxes(){ + this -> Update(); return this -> principal_axes; + } /** - * Compute and return the inertia moments assuming uniform density distribution - * across the shape + * Compute and return the dimensionless inertia moments assuming uniform density distribution + * across the shape. The normalization applied to the inertia tensor is I_norm = I / (mass * r_avg ^ 2) where r_avg = cbrt(3/4*Volume/pi) */ - arma::vec::fixed<3> GetInertiaMoments(){ - this -> Update(); return arma::eig_sym(this -> inertia_tensor);} + arma::vec::fixed<3> GetInertiaMoments(){ + this -> Update(); return arma::eig_sym(this -> inertia_tensor); + } + + /** + Computes the average radius of the shape (that is, the radius of a sphere occupying the same volume) (m or km) + */ + + double GetAverageRadius(){ + this -> Update(); return this -> r_avg; + } /** * Compute and return the bounding box (xmin,xmax,ymin,ymax,zmin,zmax) */ - double * GetBoundingBox(){ - this -> Update(); return this -> bounds; - } - - protected: - SBGATMassProperties(); - ~SBGATMassProperties() override; - - int RequestData(vtkInformation* request, - vtkInformationVector** inputVector, - vtkInformationVector* outputVector) override; - - - arma::vec::fixed<3> center_of_mass; - arma::mat::fixed<3,3> inertia_tensor; - arma::mat::fixed<3,3> principal_axes; - - double SurfaceArea; - double MinCellArea; - double MaxCellArea; - double Volume; - double VolumeProjected; - double VolumeX; - double VolumeY; - double VolumeZ; - double Kx; - double Ky; - double Kz; - double NormalizedShapeIndex; - double bounds[6]; - bool IsClosed; - - private: - SBGATMassProperties(const SBGATMassProperties&) = delete; - void operator=(const SBGATMassProperties&) = delete; - }; + double * GetBoundingBox(){ + this -> Update(); return this -> bounds; + } + + + /** + Computes the mass properties of the provided shape and saves the results to a JSON file + @param shape point to considered shape + @param path savepath (ex: "mass_properties.json") + @param is_in_meters true if the shape coordinates are expressed in meters, false otherwise + */ + static void ComputeAndSaveMassProperties(vtkSmartPointer shape,std::string path,bool is_in_meters); + + + /** + Save the computed mass properties to a JSON file + @param path savepath (ex: "mass_properties.json") + @param is_in_meters true if the shape coordinates are expressed in meters, false otherwise + + */ + void SaveMassProperties(std::string path,bool is_in_meters) const ; + + +protected: + SBGATMassProperties(); + ~SBGATMassProperties() override; + + int RequestData(vtkInformation* request, + vtkInformationVector** inputVector, + vtkInformationVector* outputVector) override; + + arma::vec::fixed<3> center_of_mass; + arma::mat::fixed<3,3> inertia_tensor; + arma::mat::fixed<3,3> principal_axes; + + double SurfaceArea; + double MinCellArea; + double MaxCellArea; + double Volume; + double VolumeProjected; + double VolumeX; + double VolumeY; + double VolumeZ; + double Kx; + double Ky; + double Kz; + double NormalizedShapeIndex; + double bounds[6]; + double r_avg; + bool IsClosed; + +private: + SBGATMassProperties(const SBGATMassProperties&) = delete; + void operator=(const SBGATMassProperties&) = delete; +}; #endif diff --git a/SbgatCore/include/SbgatCore/SBGATObsLightcurve.hpp b/SbgatCore/include/SbgatCore/SBGATObsLightcurve.hpp index 2378b54..d713740 100644 --- a/SbgatCore/include/SbgatCore/SBGATObsLightcurve.hpp +++ b/SbgatCore/include/SbgatCore/SBGATObsLightcurve.hpp @@ -4,7 +4,7 @@ Program: Small Body Geophysical Analysis Module: SBGATObsLightcurve.hpp - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatCore/include/SbgatCore/SBGATObsRadar.hpp b/SbgatCore/include/SbgatCore/SBGATObsRadar.hpp index 4b0602a..180b795 100644 --- a/SbgatCore/include/SbgatCore/SBGATObsRadar.hpp +++ b/SbgatCore/include/SbgatCore/SBGATObsRadar.hpp @@ -4,7 +4,7 @@ Program: Small Body Geophysical Analysis Module: SBGATObsRadar.hpp - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatCore/include/SbgatCore/SBGATPolyhedronGravityModel.hpp b/SbgatCore/include/SbgatCore/SBGATPolyhedronGravityModel.hpp index 1772d1c..bfbe745 100644 --- a/SbgatCore/include/SbgatCore/SBGATPolyhedronGravityModel.hpp +++ b/SbgatCore/include/SbgatCore/SBGATPolyhedronGravityModel.hpp @@ -3,7 +3,7 @@ Program: Small Body Geophysical Analysis Module: SBGATPolyhedronGravityModel.hpp - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatCore/include/SbgatCore/SBGATSphericalHarmo.hpp b/SbgatCore/include/SbgatCore/SBGATSphericalHarmo.hpp index 20d9940..63b34eb 100644 --- a/SbgatCore/include/SbgatCore/SBGATSphericalHarmo.hpp +++ b/SbgatCore/include/SbgatCore/SBGATSphericalHarmo.hpp @@ -4,7 +4,7 @@ Program: Small Body Geophysical Analysis Module: SBGATSphericalHarmo.hpp - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatCore/include/SbgatCore/SBGATSrpYorp.hpp b/SbgatCore/include/SbgatCore/SBGATSrpYorp.hpp index 03125b0..d977600 100644 --- a/SbgatCore/include/SbgatCore/SBGATSrpYorp.hpp +++ b/SbgatCore/include/SbgatCore/SBGATSrpYorp.hpp @@ -1,10 +1,7 @@ /*========================================================================= - Program: Small Body Geophysical Analysis - Module: SBGATSrpYorp.hpp - - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatCore/source/SBGATMassProperties.cpp b/SbgatCore/source/SBGATMassProperties.cpp index 22794b6..1ded3ef 100644 --- a/SbgatCore/source/SBGATMassProperties.cpp +++ b/SbgatCore/source/SBGATMassProperties.cpp @@ -23,10 +23,7 @@ SOFTWARE. /*========================================================================= - Program: Visualization Toolkit - Module: SBGATMassProperties.cxx - - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. @@ -48,6 +45,7 @@ SOFTWARE. #include #include #include +#include vtkStandardNewMacro(SBGATMassProperties); //---------------------------------------------------------------------------- @@ -127,7 +125,6 @@ int SBGATMassProperties::RequestData( double P_xy; double P_xz; double P_yz; - double l; //normalizing length double average_surface; vtkIdType idx; @@ -151,7 +148,6 @@ int SBGATMassProperties::RequestData( input -> GetBounds(this -> bounds); arma::vec::fixed<3> bbox_min = {this -> bounds[0],this -> bounds[2],this -> bounds[4]}; arma::vec::fixed<3> bbox_max = {this -> bounds[1],this -> bounds[3],this -> bounds[5]}; - l = arma::norm(bbox_max - bbox_min); for ( idx = 0; idx < 3 ; idx++ ){ munc[idx] = 0.0; @@ -172,7 +168,7 @@ int SBGATMassProperties::RequestData( // Note that the coordinates are normalized! for (idx=0; idx < numIds; idx++){ input->GetPoint(ptIds->GetId(idx), p); - x[idx] = p[0] / l; y[idx] = p[1] / l; z[idx] = p[2] / l; + x[idx] = p[0] ; y[idx] = p[1] ; z[idx] = p[2] ; } // get i j k vectors ... @@ -256,8 +252,6 @@ int SBGATMassProperties::RequestData( average_surface += area / numCells; - - if( area < mincellarea ) { mincellarea = area; @@ -365,49 +359,45 @@ int SBGATMassProperties::RequestData( } // Surface Area ... - // - this->SurfaceArea = surfacearea* std::pow(l,2); - this->MinCellArea = mincellarea* std::pow(l,2); - this->MaxCellArea = maxcellarea* std::pow(l,2); + this->SurfaceArea = surfacearea; + this->MinCellArea = mincellarea; + this->MaxCellArea = maxcellarea; // Weighting factors in Discrete Divergence theorem for volume calculation. // kxyz[0] = (munc[0] + (wxyz/3.0) + ((wxy+wxz)/2.0)) /numCells; kxyz[1] = (munc[1] + (wxyz/3.0) + ((wxy+wyz)/2.0)) /numCells; kxyz[2] = (munc[2] + (wxyz/3.0) + ((wxz+wyz)/2.0)) /numCells; - this->VolumeX = vol[0] * std::pow(l,3) ; - this->VolumeY = vol[1] * std::pow(l,3) ; - this->VolumeZ = vol[2] * std::pow(l,3) ; + this->VolumeX = vol[0] ; + this->VolumeY = vol[1] ; + this->VolumeZ = vol[2] ; this->Kx = kxyz[0]; this->Ky = kxyz[1]; this->Kz = kxyz[2]; - this->Volume = (kxyz[0] * vol[0] + kxyz[1] * vol[1] + kxyz[2] * vol[2])* std::pow(l,3); + this->Volume = (kxyz[0] * vol[0] + kxyz[1] * vol[1] + kxyz[2] * vol[2]); this->Volume = fabs(this->Volume); - this->VolumeProjected = volumeproj * std::pow(l,3); - this->NormalizedShapeIndex = - (sqrt(surfacearea)/std::cbrt(this->Volume))/2.199085233; - + this->VolumeProjected = volumeproj ; + this->NormalizedShapeIndex =(sqrt(surfacearea)/std::cbrt(this->Volume))/2.199085233; // Center of mass arma::vec::fixed<3> com_ = {cx,cy,cz}; - this -> center_of_mass = l * com_ / this->Volume * std::pow(l,3); + this -> center_of_mass = com_ / this->Volume ; - // Inertia tensor that was non-dimensionalized by l, not cbrt(V) arma::mat I = { {P_yy + P_zz, -P_xy, -P_xz}, { -P_xy, P_xx + P_zz, -P_yz}, { -P_xz, -P_yz, P_xx + P_yy}}; - double L = std::cbrt(this -> Volume) ; - this -> inertia_tensor = std::pow(l / L,5) * I - RBK::tilde(this -> center_of_mass / L) * RBK::tilde(this -> center_of_mass / L).t(); + + this -> r_avg = std::cbrt( 3./4. * this -> Volume / arma::datum::pi ) ; + this -> inertia_tensor = I / (this -> Volume * this -> r_avg * this -> r_avg); // The principal axes are extracted arma::vec eig_val; arma::mat eig_vec; arma::eig_sym(eig_val,eig_vec,this -> inertia_tensor); - // At this stage, eig_vec is (+/- v0,+/- v1 , +/- v2) where |v0| = |v1| = |v2| = 1 // with their corresponding values sorted in ascending order // But it is not guaranteed that det(eig_vec) = +1 @@ -492,3 +482,99 @@ int SBGATMassProperties::RequestData( os << "\tNormalized Shape Index: " << this->GetNormalizedShapeIndex () << "\n"; } + + + + void SBGATMassProperties::ComputeAndSaveMassProperties(vtkSmartPointer shape,std::string path,bool is_in_meters){ + + vtkSmartPointer mass_properties = vtkSmartPointer::New(); + mass_properties -> SetInputData(shape); + mass_properties -> Update(); + + mass_properties -> SaveMassProperties(path,is_in_meters); + + } + + void SBGATMassProperties::SaveMassProperties(std::string path,bool is_in_meters) const { + + nlohmann::json mass_properties_json; + std::string length_unit,surface_unit,volume_unit; + + if(is_in_meters){ + + length_unit = "m"; + surface_unit = "m^2"; + volume_unit = "m^3"; + } + else{ + length_unit = "km"; + surface_unit = "km^2"; + volume_unit = "km^3"; + } + + nlohmann::json com_json = { + {"value",{this -> center_of_mass(0), this -> center_of_mass(1), this -> center_of_mass(2)}}, + {"unit",length_unit} + }; + + nlohmann::json volume_json = { + {"value", this -> Volume}, + {"unit",volume_unit} + }; + + + nlohmann::json surface_area_json = { + {"value", this -> SurfaceArea}, + {"unit",surface_unit} + }; + + nlohmann::json inertia_tensor_json = { + {"value", { + {this -> inertia_tensor(0,0),this -> inertia_tensor(0,1),this -> inertia_tensor(0,2)}, + {this -> inertia_tensor(1,0),this -> inertia_tensor(1,1),this -> inertia_tensor(1,2)}, + {this -> inertia_tensor(2,0),this -> inertia_tensor(2,1),this -> inertia_tensor(2,2)}} + }, + {"unit","none"} + }; + + nlohmann::json projected_volume_components_json = { + {"value",{this->VolumeX,this->VolumeY,this->VolumeZ}}, + {"unit",volume_unit} + }; + + nlohmann::json projection_coefs_json = { + {"value",{this->Kx,this->Ky,this->Kz}}, + {"unit","none"} + }; + + nlohmann::json volume_projected_json = { + {"value",this->VolumeProjected}, + {"unit",volume_unit} + }; + + nlohmann::json normalized_shape_index_json = { + {"value",this->NormalizedShapeIndex}, + {"unit","none"} + }; + + nlohmann::json is_open_json = { + {"value",this -> IsClosed ? "True" : "False"} + }; + + mass_properties_json["VOLUME"] = volume_json; + mass_properties_json["SURFACE_AREA"] = surface_area_json; + mass_properties_json["COM"] = com_json; + mass_properties_json["VOLUME_PROJECTED"] = volume_projected_json; + mass_properties_json["PROJECTED_VOLUME_VX_VY_VZ"] = projected_volume_components_json; + mass_properties_json["PROJECTED_VOLUME_COEFS_KX_KY_KZ"] = projection_coefs_json; + mass_properties_json["NORMALIZED_SHAPE_INDEX_JSON"] = normalized_shape_index_json; + mass_properties_json["IS_CLOSED"] = is_open_json; + mass_properties_json["NORMALIZED_INERTIA_TENSOR"] = inertia_tensor_json; + + std::ofstream o(path); + o << std::setw(4) << mass_properties_json << std::endl; + + + + } + diff --git a/SbgatCore/source/SBGATObsLightcurve.cpp b/SbgatCore/source/SBGATObsLightcurve.cpp index 7933a46..4805cf3 100644 --- a/SbgatCore/source/SBGATObsLightcurve.cpp +++ b/SbgatCore/source/SBGATObsLightcurve.cpp @@ -3,7 +3,7 @@ Program: Visualization Toolkit Module: SBGATObsLightcurve.cpp - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatCore/source/SBGATObsRadar.cpp b/SbgatCore/source/SBGATObsRadar.cpp index 3ba5904..24e989b 100644 --- a/SbgatCore/source/SBGATObsRadar.cpp +++ b/SbgatCore/source/SBGATObsRadar.cpp @@ -23,10 +23,7 @@ SOFTWARE. /*========================================================================= - Program: Visualization Toolkit - Module: SBGATObsRadar.cpp - - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatCore/source/SBGATPolyhedronGravityModel.cpp b/SbgatCore/source/SBGATPolyhedronGravityModel.cpp index d727263..8842379 100644 --- a/SbgatCore/source/SBGATPolyhedronGravityModel.cpp +++ b/SbgatCore/source/SBGATPolyhedronGravityModel.cpp @@ -23,10 +23,7 @@ SOFTWARE. /*========================================================================= - Program: Visualization Toolkit - Module: SBGATMassProperties.cxx - - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatCore/source/SBGATSphericalHarmo.cpp b/SbgatCore/source/SBGATSphericalHarmo.cpp index 7884920..8785e0a 100644 --- a/SbgatCore/source/SBGATSphericalHarmo.cpp +++ b/SbgatCore/source/SBGATSphericalHarmo.cpp @@ -23,10 +23,7 @@ SOFTWARE. /*========================================================================= - Program: Visualization Toolkit - Module: SBGATSphericalHarmo.cxx - - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatCore/source/SBGATSrpYorp.cpp b/SbgatCore/source/SBGATSrpYorp.cpp index 8d05a69..237724c 100644 --- a/SbgatCore/source/SBGATSrpYorp.cpp +++ b/SbgatCore/source/SBGATSrpYorp.cpp @@ -23,10 +23,7 @@ SOFTWARE. /*========================================================================= - Program: Visualization Toolkit - Module: SBGATSrpYorp.cxx - - Derived class from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici + Class derived from VTK's vtkPolyDataAlgorithm by Benjamin Bercovici Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. diff --git a/SbgatGui/include/Mainwindow.hpp b/SbgatGui/include/Mainwindow.hpp index 2b98853..2a7d208 100644 --- a/SbgatGui/include/Mainwindow.hpp +++ b/SbgatGui/include/Mainwindow.hpp @@ -180,7 +180,7 @@ through the user interface layer brought by Qt.} */ QAction * add_shape_action; - + /** When triggered, opens settings window. @@ -225,11 +225,17 @@ through the user interface layer brought by Qt.} /** - When triggered, prints geometry measures of the selected prop to the console + When triggered, prints geometry measures of the selected small body shape to the console */ QAction * compute_geometric_measures_action; + /** + When triggered, opens a window asking for a file where to save the mass properties of the selected shape model + */ + QAction * save_geometric_measures_action; + + /** When triggered, opens settings window. */ @@ -290,12 +296,12 @@ through the user interface layer brought by Qt.} signals: /** - Sends the signal that a prop has been added + Sends the signal that a small body shape has been added */ void prop_added_signal(); /** - Sends the signal that a prop has been removed + Sends the signal that a small body shape has been removed */ void prop_removed_signal(); @@ -373,12 +379,19 @@ through the user interface layer brought by Qt.} void align_shape(); /** - Computes and displays a number of geometry measures associated with the selected prop + Computes and displays a number of geometry measures associated with the selected small body shape */ void compute_geometric_measures(); + + /** - Shows/hides the selected prop from the lateral widget. + Saves to a JSON file a number of geometry measures associated with the selected small body shape + */ + void save_geometric_measures(); + + /** + Shows/hides the selected small body shape from the lateral widget. @param row row index of calling cell @param col col index of calling cell (should be 1 for the slot to proceed, otherwise the call is ignored) */ @@ -464,7 +477,7 @@ through the user interface layer brought by Qt.} */ void save_shape(); - + /** Clears the console. diff --git a/SbgatGui/source/Mainwindow.cpp b/SbgatGui/source/Mainwindow.cpp index 0b3bf98..dce2501 100644 --- a/SbgatGui/source/Mainwindow.cpp +++ b/SbgatGui/source/Mainwindow.cpp @@ -278,7 +278,6 @@ void Mainwindow::createActions() { this -> clear_console_action -> setStatusTip(tr("Clears the log console")); connect(this -> clear_console_action, &QAction::triggered, this, &Mainwindow::clear_console); - this -> open_compute_yorp_window_action = new QAction(tr("Compute YORP Fourier coefficients"), this); this -> open_compute_yorp_window_action -> setStatusTip(tr("Computes the Fourier decomposition of YORP force/torques")); connect(this -> open_compute_yorp_window_action, &QAction::triggered, this, &Mainwindow::open_compute_yorp_window); @@ -294,13 +293,11 @@ void Mainwindow::createActions() { connect(this -> open_radar_window_action, &QAction::triggered, this, &Mainwindow::open_radar_window); - this -> open_lightcurve_window_action = new QAction(tr("Generate simulated light curve"), this); this -> open_lightcurve_window_action -> setStatusTip(tr("Generates simulated light curve")); connect(this -> open_lightcurve_window_action, &QAction::triggered, this, &Mainwindow::open_lightcurve_window); - this -> align_shape_action = new QAction(tr("Align shape"), this); this -> align_shape_action -> setStatusTip(tr("Align selected shape model with barycenter/principal axis")); connect(this -> align_shape_action, &QAction::triggered, this, &Mainwindow::align_shape); @@ -311,12 +308,16 @@ void Mainwindow::createActions() { connect(this -> save_console_action, &QAction::triggered, this, &Mainwindow::save_console); - this -> compute_geometric_measures_action = new QAction(tr("Compute geometric measures"), this); this -> compute_geometric_measures_action -> setStatusTip(tr("Compute geometric measures of the selected prop to the console")); connect(this -> compute_geometric_measures_action, &QAction::triggered, this, &Mainwindow::compute_geometric_measures); + this -> save_geometric_measures_action = new QAction(tr("Save geometric measures"), this); + this -> save_geometric_measures_action -> setStatusTip(tr("Save geometric measures of the selected prop to a file")); + connect(this -> save_geometric_measures_action, &QAction::triggered, this, &Mainwindow::save_geometric_measures); + + this -> open_compute_surface_pgm_window_action = new QAction(tr("Compute/Load surface PGM"), this); this -> open_compute_surface_pgm_window_action -> setStatusTip(tr("Evaluate or loads a surface Polyhedron Gravity Model")); connect(this -> open_compute_surface_pgm_window_action, &QAction::triggered, this, &Mainwindow::open_compute_surface_pgm_window); @@ -347,15 +348,17 @@ void Mainwindow::open_select_mapper_window(){ void Mainwindow::update_actions_availability() { - if (this -> wrapped_shape_data.size() == 0){ this -> compute_geometric_measures_action -> setEnabled(false); + this -> save_geometric_measures_action -> setEnabled(false); + this -> align_shape_action -> setEnabled(false); this -> save_shape_action -> setEnabled(false); } else{ this -> compute_geometric_measures_action -> setEnabled(true); + this -> save_geometric_measures_action -> setEnabled(true); int selected_row_index = this -> prop_table -> selectionModel() -> currentIndex().row(); std::string name = this -> prop_table -> item(selected_row_index, 0)-> text() .toStdString(); @@ -365,9 +368,6 @@ void Mainwindow::update_actions_availability() { } - - - } @@ -396,7 +396,7 @@ void Mainwindow::clear_console() { void Mainwindow::save_console() { QString fileName = QFileDialog::getSaveFileName(this, tr("Save to file"), "", - tr("Text file (*.txt)")); + tr("Text file (*.txt)")); if (fileName != "") { QFile file(fileName); @@ -429,19 +429,19 @@ void Mainwindow::save_shape(){ QString fileName = QFileDialog::getSaveFileName(this,tr("Save shape"), QString::fromStdString(default_name), tr("Wavefront file (*.obj)")); if (fileName.isEmpty() == false) { - int selected_row_index = this -> prop_table -> selectionModel() -> currentIndex().row(); - std::string name = this -> prop_table -> item(selected_row_index, 0) -> text() .toStdString(); + int selected_row_index = this -> prop_table -> selectionModel() -> currentIndex().row(); + std::string name = this -> prop_table -> item(selected_row_index, 0) -> text() .toStdString(); - vtkSmartPointer writer = vtkSmartPointer::New(); + vtkSmartPointer writer = vtkSmartPointer::New(); - writer -> SetInputData( this -> wrapped_shape_data[name] -> get_polydata()); + writer -> SetInputData( this -> wrapped_shape_data[name] -> get_polydata()); - writer -> SetFileName(fileName.toStdString().c_str()); - writer -> Update(); - this -> prop_table ->setItem(selected_row_index, 1, new QTableWidgetItem("")); + writer -> SetFileName(fileName.toStdString().c_str()); + writer -> Update(); + this -> prop_table ->setItem(selected_row_index, 1, new QTableWidgetItem("")); - } + } } @@ -462,7 +462,7 @@ void Mainwindow::add_shape() { } QMessageBox::StandardButton enforce_centering_aligment = QMessageBox::question(this, "Shape Alignment", "Force centering on barycenter and principal axes alignment?", - QMessageBox::Yes|QMessageBox::No); + QMessageBox::No|QMessageBox::Yes); @@ -475,80 +475,80 @@ void Mainwindow::add_shape() { } else { - scaling_factor = 1000; - } + scaling_factor = 1000; + } - std::stringstream ss; - ss.str(std::string()); + std::stringstream ss; + ss.str(std::string()); - std::string opening_line = "### Loading shape ###"; - this -> log_console -> appendPlainText(QString::fromStdString(opening_line)); - this -> log_console -> appendPlainText(QString::fromStdString("- Loading shape from ") + fileName); + std::string opening_line = "### Loading shape ###"; + this -> log_console -> appendPlainText(QString::fromStdString(opening_line)); + this -> log_console -> appendPlainText(QString::fromStdString("- Loading shape from ") + fileName); - std::chrono::time_point start, end; + std::chrono::time_point start, end; - start = std::chrono::system_clock::now(); + start = std::chrono::system_clock::now(); // The name of the shape model is extracted from the path - int dot_index = fileName.lastIndexOf("."); - int slash_index = fileName.lastIndexOf("/"); - std::string name = (fileName.toStdString()).substr(slash_index + 1 , dot_index - slash_index - 1); - std::string basic_name = name; + int dot_index = fileName.lastIndexOf("."); + int slash_index = fileName.lastIndexOf("/"); + std::string name = (fileName.toStdString()).substr(slash_index + 1 , dot_index - slash_index - 1); + std::string basic_name = name; // A new ModelDataWrapper is created and stored under the name of the shape model - std::shared_ptr model_data = std::make_shared(); + std::shared_ptr model_data = std::make_shared(); // The camera is moved to be adjusted to the new shape - this -> renderer -> GetActiveCamera() -> SetPosition(0, 0, 1.5 * scaling_factor); + this -> renderer -> GetActiveCamera() -> SetPosition(0, 0, 1.5 * scaling_factor); // Reading - vtkNew reader; - reader -> SetFileName(fileName.toStdString().c_str()); - reader -> Update(); + vtkNew reader; + reader -> SetFileName(fileName.toStdString().c_str()); + reader -> Update(); // Scaling - vtkSmartPointer transform = - vtkSmartPointer::New(); - transform -> Scale(scaling_factor,scaling_factor,scaling_factor); + vtkSmartPointer transform = + vtkSmartPointer::New(); + transform -> Scale(scaling_factor,scaling_factor,scaling_factor); - vtkSmartPointer transformFilter = - vtkSmartPointer::New(); - transformFilter->SetInputConnection(reader -> GetOutputPort()); - transformFilter->SetTransform(transform); - transformFilter -> Update(); + vtkSmartPointer transformFilter = + vtkSmartPointer::New(); + transformFilter->SetInputConnection(reader -> GetOutputPort()); + transformFilter->SetTransform(transform); + transformFilter -> Update(); // Create a PolyData - vtkSmartPointer polygonPolyData = transformFilter -> GetOutput(); + vtkSmartPointer polygonPolyData = transformFilter -> GetOutput(); // Create a mapper and actor - vtkSmartPointer mapper = - vtkSmartPointer::New(); + vtkSmartPointer mapper = + vtkSmartPointer::New(); - mapper -> SetInputConnection(transformFilter -> GetOutputPort()); - mapper -> ScalarVisibilityOff(); + mapper -> SetInputConnection(transformFilter -> GetOutputPort()); + mapper -> ScalarVisibilityOff(); - vtkSmartPointer actor = - vtkSmartPointer::New(); - actor -> SetMapper(mapper); + vtkSmartPointer actor = + vtkSmartPointer::New(); + actor -> SetMapper(mapper); // Visualize - this -> renderer -> AddActor(actor); + this -> renderer -> AddActor(actor); // Render - this -> qvtkWidget -> GetRenderWindow() -> Render(); + this -> qvtkWidget -> GetRenderWindow() -> Render(); // Store - model_data -> set_polydata(polygonPolyData); - model_data -> set_actor(actor); - model_data -> set_mapper(mapper); - model_data -> set_scale_factor(scaling_factor); + model_data -> set_polydata(polygonPolyData); + model_data -> set_actor(actor); + model_data -> set_mapper(mapper); + model_data -> set_scale_factor(scaling_factor); // The ModelDataWrapper pointer is stored. // If the name is not already taken, nothing special - unsigned int count = this -> wrapped_shape_data.count(name); + unsigned int count = this -> wrapped_shape_data.count(name); - if(count == 0){ + if(count == 0){ this -> wrapped_shape_data[name] = model_data; } else{ @@ -578,7 +578,7 @@ void Mainwindow::add_shape() { // The log console displays the name and content of the loaded shape model this -> log_console -> appendPlainText(QString::fromStdString("- Loading completed in ") - + QString::number(elapsed_seconds.count()) + QString::fromStdString(" s")); + + QString::number(elapsed_seconds.count()) + QString::fromStdString(" s")); std::string closing_line(opening_line.length() - 1, '#'); closing_line.append("\n"); @@ -639,19 +639,22 @@ void Mainwindow::align_shape(){ this -> get_renderer() -> RemoveActor2D(this -> wrapped_shape_data[name] -> get_colorbar_actor()); - - this -> qvtkWidget -> GetRenderWindow() -> Render(); this -> prop_table -> setItem(selected_row_index, 1, new QTableWidgetItem("Modified")); + std::string displayed_line = "- Translated " + name + " barycenter to (0,0,0) and aligned principal axes with visualization frame"; + + std::string closing_line(displayed_line.length() - 1, '#'); + closing_line.append("\n"); + this -> log_console -> appendPlainText(QString::fromStdString(closing_line)); + this -> log_console -> appendPlainText(QString::fromStdString(displayed_line)); + this -> log_console -> appendPlainText(QString::fromStdString(closing_line)); + } - - - void Mainwindow::add_prop_to_table_widget(std::string name) { @@ -760,78 +763,102 @@ void Mainwindow::remove_prop() { } +void Mainwindow::save_geometric_measures(){ + + int selected_row_index = this -> prop_table -> selectionModel() -> currentIndex().row(); + std::string name = this -> prop_table -> item(selected_row_index, 0) -> text() .toStdString(); + + QString fileName = QFileDialog::getSaveFileName(this, tr("Save to JSON file"), QString::fromStdString("./" + name + "_measures.json"),tr("JSON file (*.json)")); + if (fileName != "") { + SBGATMassProperties::ComputeAndSaveMassProperties(this -> wrapped_shape_data[name] -> get_polydata(),fileName.toStdString(),true); + + std::string displayed_line = "- Saved geometric measures of " + name + " to " + fileName.toStdString(); + std::string closing_line(displayed_line.length() - 1, '#'); + closing_line.append("\n"); + + this -> log_console -> appendPlainText(QString::fromStdString(closing_line)); + this -> log_console -> appendPlainText(QString::fromStdString(displayed_line)); + this -> log_console -> appendPlainText(QString::fromStdString(closing_line)); + + } + +} void Mainwindow::compute_geometric_measures(){ - int selected_row_index = this -> prop_table -> selectionModel() -> currentIndex().row(); - std::string name = this -> prop_table -> item(selected_row_index, 0) -> text() .toStdString(); + int selected_row_index = this -> prop_table -> selectionModel() -> currentIndex().row(); + std::string name = this -> prop_table -> item(selected_row_index, 0) -> text() .toStdString(); + + std::stringstream ss; - std::stringstream ss; + ss.str(std::string()); + ss.precision(10); - ss.str(std::string()); - ss.precision(10); + std::string opening_line = "### Computing shape geometric measures ###"; + this -> log_console -> appendPlainText(QString::fromStdString(opening_line)); - std::string opening_line = "### Computing shape geometric measures ###"; - this -> log_console -> appendPlainText(QString::fromStdString(opening_line)); + std::chrono::time_point start, end; + start = std::chrono::system_clock::now(); - std::chrono::time_point start, end; - start = std::chrono::system_clock::now(); + vtkSmartPointer mass_properties_filter = vtkSmartPointer::New(); + mass_properties_filter -> SetInputData(this -> wrapped_shape_data[name] -> get_polydata()); + mass_properties_filter -> Update(); + end = std::chrono::system_clock::now(); + std::chrono::duration elapsed_seconds = end - start; - vtkSmartPointer mass_properties_filter = vtkSmartPointer::New(); - mass_properties_filter -> SetInputData(this -> wrapped_shape_data[name] -> get_polydata()); - mass_properties_filter -> Update(); - end = std::chrono::system_clock::now(); - std::chrono::duration elapsed_seconds = end - start; + this -> log_console -> appendPlainText(QString::fromStdString("\n- Surface of " + name + " (m^2) :")); + this -> log_console -> appendPlainText(" " + QString::number(mass_properties_filter -> GetSurfaceArea ())); - this -> log_console -> appendPlainText(QString::fromStdString("\n- Surface of " + name + " (m^2) :")); - this -> log_console -> appendPlainText(" " + QString::number(mass_properties_filter -> GetSurfaceArea ())); + this -> log_console -> appendPlainText(QString::fromStdString("\n- Volume of " + name + " (m^3) :")); + this -> log_console -> appendPlainText(" " + QString::number(mass_properties_filter -> GetVolume())); - this -> log_console -> appendPlainText(QString::fromStdString("\n- Volume of " + name + " (m^3) :")); - this -> log_console -> appendPlainText(" " + QString::number(mass_properties_filter -> GetVolume())); - this -> log_console -> appendPlainText(QString::fromStdString("\n- Bounding box of " + name + " (m) :")); + this -> log_console -> appendPlainText(QString::fromStdString("\n- Average radius of " + name + " (m) :")); + this -> log_console -> appendPlainText(" " + QString::number(mass_properties_filter -> GetAverageRadius())); - double * bbox = mass_properties_filter -> GetBoundingBox(); + this -> log_console -> appendPlainText(QString::fromStdString("\n- Bounding box of " + name + " (m) :")); - this -> log_console -> appendPlainText(QString::fromStdString("-- Min: " + std::to_string(bbox[0]) + " "+ std::to_string(bbox[2]) + " "+ std::to_string(bbox[4]))); - this -> log_console -> appendPlainText(QString::fromStdString("-- Max: " + std::to_string(bbox[1]) + " "+ std::to_string(bbox[3]) + " "+ std::to_string(bbox[5]))); + double * bbox = mass_properties_filter -> GetBoundingBox(); - ss.str(std::string()); - ss.precision(10); + this -> log_console -> appendPlainText(QString::fromStdString("-- Min: " + std::to_string(bbox[0]) + " "+ std::to_string(bbox[2]) + " "+ std::to_string(bbox[4]))); + this -> log_console -> appendPlainText(QString::fromStdString("-- Max: " + std::to_string(bbox[1]) + " "+ std::to_string(bbox[3]) + " "+ std::to_string(bbox[5]))); - this -> log_console -> appendPlainText(QString::fromStdString("\n- Center of mass of " + name + " (m) :")); - mass_properties_filter -> GetCenterOfMass().t().raw_print(ss); - this -> log_console -> appendPlainText(QString::fromStdString(ss.str())); + ss.str(std::string()); + ss.precision(10); - ss.str(std::string()); - ss.precision(10); + this -> log_console -> appendPlainText(QString::fromStdString("\n- Center of mass of " + name + " (m) :")); + mass_properties_filter -> GetCenterOfMass().t().raw_print(ss); + this -> log_console -> appendPlainText(QString::fromStdString(ss.str())); - this -> log_console -> appendPlainText(QString::fromStdString("- Dimensionless inertia tensor of " + name )); - mass_properties_filter -> GetInertiaTensor().raw_print(ss); - this -> log_console -> appendPlainText(QString::fromStdString(ss.str())); + ss.str(std::string()); + ss.precision(10); - ss.str(std::string()); - ss.precision(10); + this -> log_console -> appendPlainText(QString::fromStdString("\n- Dimensionless inertia tensor of " + name + " :")); + mass_properties_filter -> GetInertiaTensor().raw_print(ss); + this -> log_console -> appendPlainText(QString::fromStdString(ss.str())); - this -> log_console -> appendPlainText(QString::fromStdString("- Principal axes of " + name )); - mass_properties_filter -> GetPrincipalAxes().raw_print(ss); - this -> log_console -> appendPlainText(QString::fromStdString(ss.str())); + ss.str(std::string()); + ss.precision(10); - ss.str(std::string()); - ss.precision(10); + this -> log_console -> appendPlainText(QString::fromStdString("\b- Principal axes of " + name + " :")); + mass_properties_filter -> GetPrincipalAxes().raw_print(ss); + this -> log_console -> appendPlainText(QString::fromStdString(ss.str())); - this -> log_console -> appendPlainText(QString::fromStdString("- Dimensionless inertia moments of " + name )); - mass_properties_filter -> GetInertiaMoments().t().raw_print(ss); - this -> log_console -> appendPlainText(QString::fromStdString(ss.str())); + ss.str(std::string()); + ss.precision(10); + this -> log_console -> appendPlainText(QString::fromStdString("\n- Dimensionless inertia moments of " + name + " :")); + mass_properties_filter -> GetInertiaMoments().t().raw_print(ss); + this -> log_console -> appendPlainText(QString::fromStdString(ss.str())); - this -> log_console -> appendPlainText(QString::fromStdString("- Done computing in ") - + QString::number(elapsed_seconds.count()) + QString::fromStdString(" s")); - std::string closing_line(opening_line.length() - 1, '#'); - closing_line.append("\n"); - this -> log_console -> appendPlainText(QString::fromStdString(closing_line)); + this -> log_console -> appendPlainText(QString::fromStdString("\n- Done computing in ") + + QString::number(elapsed_seconds.count()) + QString::fromStdString(" s")); + + std::string closing_line(opening_line.length() - 1, '#'); + closing_line.append("\n"); + this -> log_console -> appendPlainText(QString::fromStdString(closing_line)); @@ -861,6 +888,8 @@ void Mainwindow::createMenus() { this -> MeasuresMenu = menuBar() -> addMenu(tr("&Measures")); this -> MeasuresMenu -> addAction(this -> compute_geometric_measures_action); + this -> MeasuresMenu -> addAction(this -> save_geometric_measures_action); + this -> ObservationsMenu = menuBar() -> addMenu(tr("&Observations")); diff --git a/Tests/source/Tests.cpp b/Tests/source/Tests.cpp index d4a21f4..823e171 100644 --- a/Tests/source/Tests.cpp +++ b/Tests/source/Tests.cpp @@ -128,16 +128,16 @@ void TestsSBCore::test_sbgat_mass_properties(){ vtkSmartPointer mass_filter = vtkSmartPointer::New(); mass_filter -> SetInputConnection(transformFilter_trans -> GetOutputPort()); mass_filter -> Update(); + auto com_sbgat = mass_filter -> GetCenterOfMass(); assert(mass_filter -> CheckClosed()); // the inertia moments are invariant by rotation/translation arma::vec inertia_moments = {1./6,1./6,1./6}; + double r_avg = std::cbrt(3./ (4 * arma::datum::pi)); + inertia_moments = inertia_moments / (r_avg * r_avg) + arma::eig_sym(RBK::tilde(translation_vector) * RBK::tilde(translation_vector).t())/ (r_avg * r_avg); auto inertia_moments_sbgat = mass_filter -> GetInertiaMoments(); - // The center of mass should be right where the translation put it - auto com_sbgat = mass_filter -> GetCenterOfMass(); - assert(arma::norm(inertia_moments - inertia_moments_sbgat)/arma::norm(inertia_moments_sbgat) < 1e-8); assert(arma::norm(translation_vector - com_sbgat)/arma::norm(com_sbgat) < 1e-8);