diff --git a/CMakeLists.txt b/CMakeLists.txt index 647ff5ee57918209b4fb681f18dca50ea110207b..3a79bd57b7a3041fcd8c4a96db2f1ace248a371f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ cmake_minimum_required( VERSION 3.1 FATAL_ERROR ) # visimpl project and version -project( visimpl VERSION 1.6.0 ) +project( visimpl VERSION 1.6.1 ) set( visimpl_VERSION_ABI 6 ) SET( VISIMPL_LICENSE "GPL") diff --git a/visimpl/MainWindow.cpp b/visimpl/MainWindow.cpp index d114bbc1feac056ab85365c4bd84d9162326561b..774c800c991c5635b566ad77995652b810c13150 100644 --- a/visimpl/MainWindow.cpp +++ b/visimpl/MainWindow.cpp @@ -2413,10 +2413,10 @@ void MainWindow::clearGroups( void ) const QFileInfo currentFile{_lastOpenedNetworkFileName}; const QString jsonGroupsFile = jsonObj.value("filename").toString(); - if(jsonGroupsFile.compare(currentFile.fileName(), Qt::CaseInsensitive) != 0) + if(!jsonGroupsFile.isEmpty() && jsonGroupsFile.compare(currentFile.fileName(), Qt::CaseInsensitive) != 0) { - const auto message = tr("This groups definitions are from file %1. Current file" - " is %2. Do you want to continue?").arg(jsonGroupsFile).arg(currentFile.fileName()); + const auto message = tr("This groups definitions are from file '%1'. Current file" + " is '%2'. Do you want to continue?").arg(jsonGroupsFile).arg(currentFile.fileName()); QMessageBox msgbox{this}; msgbox.setWindowTitle(title); @@ -2733,7 +2733,7 @@ void MainWindow::clearGroups( void ) const auto numActions = actions.size(); if(numActions > 0) { - const auto warnText = tr("Loading new camera positions will erase" + const auto warnText = tr("Loading new camera positions will remove" " %1 existing position%2. Are you sure?").arg(numActions).arg(numActions > 1 ? "s":""); if(QMessageBox::Ok != QMessageBox::warning(this, title, warnText, QMessageBox::Cancel|QMessageBox::Ok)) return; @@ -2807,6 +2807,25 @@ void MainWindow::clearGroups( void ) return; } + const QFileInfo currentFile{_lastOpenedNetworkFileName}; + const QString jsonPositionsFile = jsonObj.value("filename").toString(); + if(!jsonPositionsFile.isEmpty() && jsonPositionsFile.compare(currentFile.fileName(), Qt::CaseInsensitive) != 0) + { + const auto message = tr("This positions are from file '%1'. Current file" + " is '%2'. Do you want to continue?").arg(jsonPositionsFile).arg(currentFile.fileName()); + + QMessageBox msgbox{this}; + msgbox.setWindowTitle(title); + msgbox.setIcon(QMessageBox::Icon::Question); + msgbox.setText(message); + msgbox.setWindowIcon(QIcon(":/visimpl.png")); + msgbox.setStandardButtons(QMessageBox::Cancel|QMessageBox::Ok); + msgbox.setDefaultButton(QMessageBox::Ok); + + if(QMessageBox::Ok != msgbox.exec()) + return; + } + // Clear existing actions before entering new ones. for(auto action: actions) { @@ -2844,19 +2863,29 @@ void MainWindow::clearGroups( void ) { const QString nameFilter = "Camera positions (*.json)"; QDir directory; + QString filename; if(_lastOpenedNetworkFileName.isEmpty()) + { directory = QDir::home(); + filename = "positions.json"; + } else - directory = QFileInfo(_lastOpenedNetworkFileName).dir(); + { + QFileInfo fi(_lastOpenedNetworkFileName); + directory = fi.dir(); + filename = QString("%1_positions.json").arg(fi.baseName()); + } QFileDialog fDialog(this); fDialog.setWindowIcon(QIcon(":/visimpl.png")); fDialog.setWindowTitle("Save camera positions"); fDialog.setAcceptMode(QFileDialog::AcceptMode::AcceptSave); fDialog.setDefaultSuffix("json"); + fDialog.selectFile(filename); fDialog.setDirectory(directory); fDialog.setOption(QFileDialog::Option::DontUseNativeDialog, true); + fDialog.setOption(QFileDialog::Option::DontConfirmOverwrite, false); fDialog.setFileMode(QFileDialog::FileMode::AnyFile); fDialog.setNameFilters(QStringList{nameFilter}); fDialog.setNameFilter(nameFilter); @@ -2866,7 +2895,7 @@ void MainWindow::clearGroups( void ) if(fDialog.selectedFiles().empty()) return; - const auto filename = fDialog.selectedFiles().first(); + filename = fDialog.selectedFiles().first(); QFile wFile{filename}; if(!wFile.open(QIODevice::WriteOnly|QIODevice::Text|QIODevice::Truncate)) @@ -2910,6 +2939,7 @@ void MainWindow::clearGroups( void ) std::for_each(actions.cbegin(), actions.cend(), insertPosition); QJsonObject obj; + obj.insert("filename", QFileInfo{_lastOpenedNetworkFileName}.fileName()); obj.insert("positions", positionsObjs); QJsonDocument doc{obj}; diff --git a/visimpl/OpenGLWidget.cpp b/visimpl/OpenGLWidget.cpp index b7bc98789ceae3f98b33cf1235a0450d737342b6..ec033811bac9d7569a7f9cb84eaa4cda35e6011a 100644 --- a/visimpl/OpenGLWidget.cpp +++ b/visimpl/OpenGLWidget.cpp @@ -1356,6 +1356,7 @@ void main() _gidPositions.clear( ); _gidPositions.reserve( positions.size( )); + vec3 bbmin, bbmax; auto gidit = _player->gids( ).cbegin( ); auto insertElement = [ & ]( const vmml::Vector3f& v ) { @@ -1364,9 +1365,28 @@ void main() v.z( ) * _scaleFactor.z ); _gidPositions.insert( std::make_pair( *gidit , position )); + if(gidit == _player->gids().cbegin()) + { + bbmin = bbmax = position; + } + else + { + auto x = std::min(bbmin.x, position.x); + auto y = std::min(bbmin.y, position.y); + auto z = std::min(bbmin.z, position.z); + bbmin = vec3{x,y,z}; + + x = std::max(bbmax.x, position.x); + y = std::max(bbmax.y, position.y); + z = std::max(bbmax.z, position.z); + bbmax = vec3{x,y,z}; + } ++gidit; }; std::for_each( positions.cbegin( ) , positions.cend( ) , insertElement ); + + _boundingBoxHome = tBoundingBox{ bbmin, bbmax }; + return true; } diff --git a/visimpl/OpenGLWidget.h b/visimpl/OpenGLWidget.h index acfe5f8c17852e7453101cd383b51660586411ef..482f1382ed6534862a3b18fa28c04cd785288234 100644 --- a/visimpl/OpenGLWidget.h +++ b/visimpl/OpenGLWidget.h @@ -37,6 +37,8 @@ #include <chrono> #include <unordered_set> #include <queue> +#include <locale> +#include <iostream> #define VISIMPL_SKIP_GLEW_INCLUDE 1 @@ -65,6 +67,11 @@ class QLabel; class LoadingDialog; class LoaderThread; +struct streamDotSeparator: std::numpunct<char> +{ + char do_decimal_point() const { return '.'; } +}; + namespace visimpl { /** \class CameraPosition @@ -93,19 +100,29 @@ namespace visimpl */ CameraPosition(const QString &data) { - const auto parts = data.split(";"); + const auto separator = std::use_facet<std::numpunct<char>>(std::cout.getloc()).decimal_point(); + const bool needSubst = (separator == ','); + + auto parts = data.split(";"); Q_ASSERT(parts.size() == 3); const auto posData = parts.first(); const auto rotData = parts.last(); - radius = parts.at(1).toFloat(); + auto radiusData = parts.at(1); - const auto posParts = posData.split(","); + auto posParts = posData.split(","); Q_ASSERT(posParts.size() == 3); - const auto rotParts = rotData.split(","); + auto rotParts = rotData.split(","); Q_ASSERT(rotParts.size() == 9); + if(needSubst) + { + for(auto &part: posParts) part.replace('.', ','); + for(auto &part: rotParts) part.replace('.', ','); + radiusData.replace('.', ','); + } + position = Eigen::Vector3f(posParts[0].toFloat(), posParts[1].toFloat(), posParts[2].toFloat()); - radius = parts.at(1).toFloat(); + radius = radiusData.toFloat(); rotation.block<1,3>(0,0) = Eigen::Vector3f{rotParts[0].toFloat(), rotParts[1].toFloat(), rotParts[2].toFloat()}; rotation.block<1,3>(1,0) = Eigen::Vector3f{rotParts[3].toFloat(), rotParts[4].toFloat(), rotParts[5].toFloat()}; rotation.block<1,3>(2,0) = Eigen::Vector3f{rotParts[6].toFloat(), rotParts[7].toFloat(), rotParts[8].toFloat()}; @@ -117,6 +134,7 @@ namespace visimpl QString toString() const { std::stringstream stream; + stream.imbue(std::locale(stream.getloc(), new streamDotSeparator())); stream << position << ";" << radius << ";" << rotation(0,0) << "," << rotation(0,1) << "," << rotation(0,2) << "," << rotation(1,0) << "," << rotation(1,1) << "," << rotation(1,2) << ","