| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839 |
- //##########################################################################
- //# #
- //# CLOUDCOMPARE #
- //# #
- //# This program is free software; you can redistribute it and/or modify #
- //# it under the terms of the GNU General Public License as published by #
- //# the Free Software Foundation; version 2 or later of the License. #
- //# #
- //# This program is distributed in the hope that it will be useful, #
- //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
- //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
- //# GNU General Public License for more details. #
- //# #
- //# COPYRIGHT: CloudCompare project #
- //# #
- //##########################################################################
- #include "ccLibAlgorithms.h"
- //CCCoreLib
- #include <ScalarFieldTools.h>
- //qCC_db
- #include <ccOctree.h>
- #include <ccPointCloud.h>
- #include <ccScalarField.h>
- //Local
- #include "ccCommon.h"
- #include "ccConsole.h"
- #include "ccProgressDialog.h"
- #include "ccRegistrationTools.h"
- #include "ccUtils.h"
- //Qt
- #include <QApplication>
- #include <QElapsedTimer>
- #include <QInputDialog>
- #include <QMessageBox>
- // This is included only for temporarily removing an object from the tree.
- // TODO figure out a cleaner way to do this without having to include all of mainwindow.h
- #include "mainwindow.h"
- namespace ccLibAlgorithms
- {
- static QString GetDensitySFName(CCCoreLib::GeometricalAnalysisTools::Density densityType, bool approx, double densityKernelSize = 0.0)
- {
- QString sfName;
-
- //update the name with the density type
- switch (densityType)
- {
- case CCCoreLib::GeometricalAnalysisTools::DENSITY_KNN:
- sfName = CC_LOCAL_KNN_DENSITY_FIELD_NAME;
- break;
- case CCCoreLib::GeometricalAnalysisTools::DENSITY_2D:
- sfName = CC_LOCAL_SURF_DENSITY_FIELD_NAME;
- break;
- case CCCoreLib::GeometricalAnalysisTools::DENSITY_3D:
- sfName = CC_LOCAL_VOL_DENSITY_FIELD_NAME;
- break;
- default:
- assert(false);
- break;
- }
-
- sfName += QString(" (r=%2)").arg(densityKernelSize);
-
- if (approx)
- sfName += " [approx]";
-
- return sfName;
- }
-
- double GetDefaultCloudKernelSize(ccGenericPointCloud* cloud, unsigned knn/*=12*/)
- {
- assert(cloud);
- if (cloud && cloud->size() != 0)
- {
- //we get 1% of the cloud bounding box
- //and we divide by the number of points / 10e6 (so that the kernel for a 20 M. points cloud is half the one of a 10 M. cloud)
- ccBBox box = cloud->getOwnBB();
-
- //old way
- //PointCoordinateType radius = box.getDiagNorm() * static_cast<PointCoordinateType>(0.01/std::max(1.0,1.0e-7*static_cast<double>(cloud->size())));
-
- //new way
- CCVector3 d = box.getDiagVec();
- double volume = (static_cast<double>(d[0]) * d[1]) * d[2];
- double surface = pow(volume, 2.0/3.0);
- double surfacePerPoint = surface / cloud->size();
- return sqrt(surfacePerPoint * knn);
- }
-
- return -CCCoreLib::PC_ONE;
- }
-
- double GetDefaultCloudKernelSize(const ccHObject::Container& entities, unsigned knn/*=12*/)
- {
- double sigma = std::numeric_limits<double>::max();
-
- for (ccHObject* entity : entities)
- {
- ccPointCloud* pc = ccHObjectCaster::ToPointCloud(entity);
- double sigmaCloud = GetDefaultCloudKernelSize(pc);
-
- //we keep the smallest value
- if (sigmaCloud < sigma)
- {
- sigma = sigmaCloud;
- }
- }
-
- return sigma;
- }
- bool ComputeGeomCharacteristics(const GeomCharacteristicSet& characteristics,
- PointCoordinateType radius,
- ccHObject::Container& entities,
- const CCVector3* roughnessUpDir/*=nullptr*/,
- QWidget* parent/*=nullptr*/)
- {
- //no feature case
- if (characteristics.empty())
- {
- //nothing to do
- assert(false);
- return true;
- }
-
- //single features case
- if (characteristics.size() == 1)
- {
- return ComputeGeomCharacteristic( characteristics.front().charac,
- characteristics.front().subOption,
- radius,
- entities,
- roughnessUpDir,
- parent);
- }
- //multiple features case
- QScopedPointer<ccProgressDialog> pDlg;
- if (parent)
- {
- pDlg.reset(new ccProgressDialog(true, parent));
- pDlg->setAutoClose(false);
- }
-
- for (const GeomCharacteristic& g : characteristics)
- {
- if (!ComputeGeomCharacteristic( g.charac,
- g.subOption,
- radius,
- entities,
- roughnessUpDir,
- parent,
- pDlg.data()))
- {
- return false;
- }
- }
- return true;
- }
- bool ComputeGeomCharacteristic( CCCoreLib::GeometricalAnalysisTools::GeomCharacteristic c,
- int subOption,
- PointCoordinateType radius,
- ccHObject::Container& entities,
- const CCVector3* roughnessUpDir/*=nullptr*/,
- QWidget* parent/*= nullptr*/,
- ccProgressDialog* progressDialog/*=nullptr*/)
- {
- size_t selNum = entities.size();
- if (selNum < 1)
- return false;
- //generate the right SF name
- QString sfName;
- switch (c)
- {
- case CCCoreLib::GeometricalAnalysisTools::Feature:
- {
- switch (subOption)
- {
- case CCCoreLib::Neighbourhood::EigenValuesSum:
- sfName = "Eigenvalues sum";
- break;
- case CCCoreLib::Neighbourhood::Omnivariance:
- sfName = "Omnivariance";
- break;
- case CCCoreLib::Neighbourhood::EigenEntropy:
- sfName = "Eigenentropy";
- break;
- case CCCoreLib::Neighbourhood::Anisotropy:
- sfName = "Anisotropy";
- break;
- case CCCoreLib::Neighbourhood::Planarity:
- sfName = "Planarity";
- break;
- case CCCoreLib::Neighbourhood::Linearity:
- sfName = "Linearity";
- break;
- case CCCoreLib::Neighbourhood::PCA1:
- sfName = "PCA1";
- break;
- case CCCoreLib::Neighbourhood::PCA2:
- sfName = "PCA2";
- break;
- case CCCoreLib::Neighbourhood::SurfaceVariation:
- sfName = "Surface variation";
- break;
- case CCCoreLib::Neighbourhood::Sphericity:
- sfName = "Sphericity";
- break;
- case CCCoreLib::Neighbourhood::Verticality:
- sfName = "Verticality";
- break;
- case CCCoreLib::Neighbourhood::EigenValue1:
- sfName = "1st eigenvalue";
- break;
- case CCCoreLib::Neighbourhood::EigenValue2:
- sfName = "2nd eigenvalue";
- break;
- case CCCoreLib::Neighbourhood::EigenValue3:
- sfName = "3rd eigenvalue";
- break;
- default:
- assert(false);
- ccLog::Error("Internal error: invalid sub option for Feature computation");
- return false;
- }
- sfName += QString(" (%1)").arg(radius);
- }
- break;
- case CCCoreLib::GeometricalAnalysisTools::Curvature:
- {
- switch (subOption)
- {
- case CCCoreLib::Neighbourhood::GAUSSIAN_CURV:
- sfName = CC_CURVATURE_GAUSSIAN_FIELD_NAME;
- break;
- case CCCoreLib::Neighbourhood::MEAN_CURV:
- sfName = CC_CURVATURE_MEAN_FIELD_NAME;
- break;
- case CCCoreLib::Neighbourhood::NORMAL_CHANGE_RATE:
- sfName = CC_CURVATURE_NORM_CHANGE_RATE_FIELD_NAME;
- break;
- default:
- assert(false);
- ccLog::Error("Internal error: invalid sub option for Curvature computation");
- return false;
- }
- sfName += QString(" (%1)").arg(radius);
- }
- break;
- case CCCoreLib::GeometricalAnalysisTools::LocalDensity:
- sfName = GetDensitySFName(static_cast<CCCoreLib::GeometricalAnalysisTools::Density>(subOption), false, radius);
- break;
- case CCCoreLib::GeometricalAnalysisTools::ApproxLocalDensity:
- sfName = GetDensitySFName(static_cast<CCCoreLib::GeometricalAnalysisTools::Density>(subOption), true);
- break;
- case CCCoreLib::GeometricalAnalysisTools::Roughness:
- sfName = CC_ROUGHNESS_FIELD_NAME + QString(" (%1)").arg(radius);
- break;
- case CCCoreLib::GeometricalAnalysisTools::MomentOrder1:
- sfName = CC_MOMENT_ORDER1_FIELD_NAME + QString(" (%1)").arg(radius);
- break;
- default:
- assert(false);
- return false;
- }
- ccProgressDialog* pDlg = progressDialog;
- if (!pDlg && parent)
- {
- pDlg = new ccProgressDialog(true, parent);
- pDlg->setAutoClose(false);
- }
- for (size_t i = 0; i < selNum; ++i)
- {
- //is the ith selected data is eligible for processing?
- if (entities[i]->isKindOf(CC_TYPES::POINT_CLOUD))
- {
- ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(entities[i]);
- ccPointCloud* pc = nullptr;
- int sfIdx = -1;
- if (cloud->isA(CC_TYPES::POINT_CLOUD))
- {
- pc = static_cast<ccPointCloud*>(cloud);
- sfIdx = pc->getScalarFieldIndexByName(sfName.toStdString());
- if (sfIdx < 0)
- sfIdx = pc->addScalarField(sfName.toStdString());
- if (sfIdx >= 0)
- pc->setCurrentScalarField(sfIdx);
- else
- {
- ccConsole::Error(QString("Failed to create scalar field on cloud '%1' (not enough memory?)").arg(pc->getName()));
- continue;
- }
- }
- ccOctree::Shared octree = cloud->getOctree();
- if (!octree)
- {
- if (pDlg)
- {
- pDlg->show();
- }
- octree = cloud->computeOctree(pDlg);
- if (!octree)
- {
- ccConsole::Error(QString("Couldn't compute octree for cloud '%1'!").arg(cloud->getName()));
- break;
- }
- }
- CCCoreLib::GeometricalAnalysisTools::ErrorCode result = CCCoreLib::GeometricalAnalysisTools::ComputeCharactersitic( c,
- subOption,
- cloud,
- radius,
- roughnessUpDir,
- pDlg,
- octree.data());
- if (result == CCCoreLib::GeometricalAnalysisTools::NoError)
- {
- if (pc && sfIdx >= 0)
- {
- pc->setCurrentDisplayedScalarField(sfIdx);
- pc->showSF(sfIdx >= 0);
- pc->getCurrentInScalarField()->computeMinAndMax();
- if (c == CCCoreLib::GeometricalAnalysisTools::Roughness && roughnessUpDir != nullptr)
- {
- // signed roughness should be displayed with a symmetrical color scale
- ccScalarField* sf = dynamic_cast<ccScalarField*>(pc->getCurrentInScalarField());
- if (sf)
- {
- sf->setSymmetricalScale(true);
- }
- else
- {
- assert(false);
- }
- }
- }
- cloud->prepareDisplayForRefresh();
- }
- else
- {
- QString errorMessage;
- switch (result)
- {
- case CCCoreLib::GeometricalAnalysisTools::InvalidInput:
- errorMessage = "Internal error (invalid input)";
- break;
- case CCCoreLib::GeometricalAnalysisTools::NotEnoughPoints:
- errorMessage = "Not enough points";
- break;
- case CCCoreLib::GeometricalAnalysisTools::OctreeComputationFailed:
- errorMessage = "Failed to compute octree (not enough memory?)";
- break;
- case CCCoreLib::GeometricalAnalysisTools::ProcessFailed:
- errorMessage = "Process failed";
- break;
- case CCCoreLib::GeometricalAnalysisTools::UnhandledCharacteristic:
- errorMessage = "Internal error (unhandled characteristic)";
- break;
- case CCCoreLib::GeometricalAnalysisTools::NotEnoughMemory:
- errorMessage = "Not enough memory";
- break;
- case CCCoreLib::GeometricalAnalysisTools::ProcessCancelledByUser:
- errorMessage = "Process cancelled by user";
- break;
- default:
- assert(false);
- errorMessage = "Unknown error";
- break;
- }
-
- ccConsole::Warning(QString("Failed to apply processing to cloud '%1'").arg(cloud->getName()));
- ccConsole::Warning(errorMessage);
-
- if (pc && sfIdx >= 0)
- {
- pc->deleteScalarField(sfIdx);
- sfIdx = -1;
- }
- if (pDlg != progressDialog)
- {
- delete pDlg;
- pDlg = nullptr;
- }
- return false;
- }
- }
- }
- if (pDlg != progressDialog)
- {
- delete pDlg;
- pDlg = nullptr;
- }
- return true;
- }
- bool ApplyCCLibAlgorithm(CC_LIB_ALGORITHM algo, ccHObject::Container& entities, QWidget* parent/*=nullptr*/, void** additionalParameters/*=nullptr*/)
- {
- size_t selNum = entities.size();
- if (selNum < 1)
- return false;
-
- //generic parameters
- QString sfName;
- //computeScalarFieldGradient parameters
- bool euclidean = false;
-
- switch (algo)
- {
- case CCLIB_ALGO_SF_GRADIENT:
- {
- sfName = CC_GRADIENT_NORMS_FIELD_NAME;
- //parameters already provided?
- if (additionalParameters)
- {
- euclidean = *static_cast<bool*>(additionalParameters[0]);
- }
- else //ask the user!
- {
- euclidean = ( QMessageBox::question(parent,
- "Gradient",
- "Is the scalar field composed of (euclidean) distances?",
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::No ) == QMessageBox::Yes );
- }
- }
- break;
-
- default:
- assert(false);
- return false;
- }
-
- for (size_t i = 0; i < selNum; ++i)
- {
- //is the ith selected data is eligible for processing?
- ccGenericPointCloud* cloud = nullptr;
- switch (algo)
- {
- case CCLIB_ALGO_SF_GRADIENT:
- //for scalar field gradient, we can apply it directly on meshes
- bool lockedVertices;
- cloud = ccHObjectCaster::ToGenericPointCloud(entities[i], &lockedVertices);
- if (lockedVertices)
- {
- ccUtils::DisplayLockedVerticesWarning(entities[i]->getName(), selNum == 1);
- cloud = nullptr;
- }
- if (cloud)
- {
- //but we need an already displayed SF!
- if (cloud->isA(CC_TYPES::POINT_CLOUD))
- {
- ccPointCloud* pc = static_cast<ccPointCloud*>(cloud);
- int outSfIdx = pc->getCurrentDisplayedScalarFieldIndex();
- if (outSfIdx < 0)
- {
- cloud = nullptr;
- }
- else
- {
- //we set as 'output' SF the currently displayed scalar field
- pc->setCurrentOutScalarField(outSfIdx);
- sfName = QString("%1(%2)").arg(CC_GRADIENT_NORMS_FIELD_NAME, QString::fromStdString(pc->getScalarFieldName(outSfIdx)));
- }
- }
- else //if (!cloud->hasDisplayedScalarField()) //TODO: displayed but not necessarily set as OUTPUT!
- {
- cloud = nullptr;
- }
- }
- break;
-
- //by default, we apply processings on clouds only
- default:
- if (entities[i]->isKindOf(CC_TYPES::POINT_CLOUD))
- cloud = ccHObjectCaster::ToGenericPointCloud(entities[i]);
- break;
- }
-
- if (cloud)
- {
- ccPointCloud* pc = nullptr;
- int sfIdx = -1;
- if (cloud->isA(CC_TYPES::POINT_CLOUD))
- {
- pc = static_cast<ccPointCloud*>(cloud);
-
- sfIdx = pc->getScalarFieldIndexByName(sfName.toStdString());
- if (sfIdx < 0)
- sfIdx = pc->addScalarField(sfName.toStdString());
- if (sfIdx >= 0)
- pc->setCurrentInScalarField(sfIdx);
- else
- {
- ccConsole::Error(QString("Failed to create scalar field on cloud '%1' (not enough memory?)").arg(pc->getName()));
- continue;
- }
- }
-
- QScopedPointer<ccProgressDialog> pDlg;
- if (parent)
- {
- pDlg.reset(new ccProgressDialog(true, parent));
- }
-
- ccOctree::Shared octree = cloud->getOctree();
- if (!octree)
- {
- if (pDlg)
- {
- pDlg->show();
- }
- octree = cloud->computeOctree(pDlg.data());
- if (!octree)
- {
- ccConsole::Error(QString("Couldn't compute octree for cloud '%1'!").arg(cloud->getName()));
- break;
- }
- }
-
- int result = 0;
- QElapsedTimer eTimer;
- eTimer.start();
- switch(algo)
- {
- case CCLIB_ALGO_SF_GRADIENT:
- result = CCCoreLib::ScalarFieldTools::computeScalarFieldGradient(cloud,
- 0, //auto --> FIXME: should be properly set by the user!
- euclidean,
- false,
- pDlg.data(),
- octree.data());
- break;
- default:
- //missed something?
- assert(false);
- }
- qint64 elapsedTime_ms = eTimer.elapsed();
-
- if (result == 0)
- {
- if (pc && sfIdx >= 0)
- {
- pc->setCurrentDisplayedScalarField(sfIdx);
- pc->showSF(sfIdx >= 0);
- pc->getCurrentInScalarField()->computeMinAndMax();
- }
- cloud->prepareDisplayForRefresh();
- ccConsole::Print("[Algorithm] Timing: %3.2f s.", static_cast<double>(elapsedTime_ms) / 1000.0);
- }
- else
- {
- ccConsole::Warning(QString("Failed to apply processing to cloud '%1'").arg(cloud->getName()));
- if (pc && sfIdx >= 0)
- {
- pc->deleteScalarField(sfIdx);
- sfIdx = -1;
- }
- }
- }
- }
-
- return true;
- }
- bool ApplyScaleMatchingAlgorithm( ScaleMatchingAlgorithm algo,
- ccHObject::Container& entities,
- double icpRmsDiff,
- int icpFinalOverlap,
- unsigned refEntityIndex/*=0*/,
- QWidget* parent/*=nullptr*/)
- {
- if ( entities.size() < 2
- || refEntityIndex >= entities.size())
- {
- ccLog::Error("[ApplyScaleMatchingAlgorithm] Invalid input parameter(s)");
- return false;
- }
-
- std::vector<double> scales;
- try
- {
- scales.resize(entities.size(), -1.0);
- }
- catch (const std::bad_alloc&)
- {
- ccLog::Error("Not enough memory!");
- return false;
- }
-
- //check the reference entity
- ccHObject* refEntity = entities[refEntityIndex];
- if ( !refEntity->isKindOf(CC_TYPES::POINT_CLOUD)
- && !refEntity->isKindOf(CC_TYPES::MESH))
- {
- ccLog::Warning("[Scale Matching] The reference entity must be a cloud or a mesh!");
- return false;
- }
-
- unsigned count = static_cast<unsigned>(entities.size());
-
- //now compute the scales
- ccProgressDialog pDlg(true,parent);
- pDlg.setMethodTitle(QObject::tr("Computing entities scales"));
- pDlg.setInfo(QObject::tr("Entities: %1").arg(count));
- CCCoreLib::NormalizedProgress nProgress(&pDlg, 2 * count - 1);
- pDlg.start();
- QApplication::processEvents();
-
- for (unsigned i = 0; i < count; ++i)
- {
- ccHObject* ent = entities[i];
- //try to get the underlying cloud (or the vertices set for a mesh)
- bool lockedVertices;
- ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(ent,&lockedVertices);
- if (cloud && !lockedVertices)
- {
- switch (algo)
- {
- case BB_MAX_DIM:
- case BB_VOLUME:
- {
- ccBBox box = ent->getOwnBB();
- if (box.isValid())
- scales[i] = algo == BB_MAX_DIM ? box.getMaxBoxDim() : box.computeVolume();
- else
- ccLog::Warning(QString("[Scale Matching] Entity '%1' has an invalid bounding-box!").arg(ent->getName()));
- }
- break;
-
- case PCA_MAX_DIM:
- {
- CCCoreLib::Neighbourhood Yk(cloud);
- if (!Yk.getLSPlane())
- {
- ccLog::Warning(QString("[Scale Matching] Failed to perform PCA on entity '%1'!").arg(ent->getName()));
- break;
- }
- //deduce the scale
- {
- const CCVector3* X = Yk.getLSPlaneX();
- const CCVector3* O = Yk.getGravityCenter();
- double minX = 0;
- double maxX = 0;
- for (unsigned j = 0; j < cloud->size(); ++j)
- {
- double x = (*cloud->getPoint(j) - *O).dot(*X);
- if (j != 0)
- {
- minX = std::min(x,minX);
- maxX = std::max(x,maxX);
- }
- else
- {
- minX = maxX = x;
- }
- }
- scales[i] = maxX-minX;
- }
- }
- break;
-
- case ICP_SCALE:
- {
- ccGLMatrix transMat;
- double finalError = 0.0;
- double finalScale = 1.0;
- unsigned finalPointCount = 0;
-
- CCCoreLib::ICPRegistrationTools::Parameters parameters;
- {
- parameters.convType = CCCoreLib::ICPRegistrationTools::MAX_ERROR_CONVERGENCE;
- parameters.minRMSDecrease = icpRmsDiff;
- parameters.nbMaxIterations = 0;
- parameters.adjustScale = true;
- parameters.filterOutFarthestPoints = false;
- parameters.samplingLimit = 50000;
- parameters.finalOverlapRatio = icpFinalOverlap / 100.0;
- parameters.transformationFilters = CCCoreLib::RegistrationTools::SKIP_NONE;
- parameters.maxThreadCount = 0;
- parameters.useC2MSignedDistances = false;
- parameters.robustC2MSignedDistances = true;
- parameters.normalsMatching = CCCoreLib::ICPRegistrationTools::NO_NORMAL;
- }
- if (ccRegistrationTools::ICP(
- ent,
- refEntity,
- transMat,
- finalScale,
- finalError,
- finalPointCount,
- parameters,
- false,
- false,
- parent))
- {
- scales[i] = finalScale;
- }
- else
- {
- ccLog::Warning(QString("[Scale Matching] Failed to register entity '%1'!").arg(ent->getName()));
- }
-
- }
- break;
-
- default:
- assert(false);
- break;
- }
- }
- else if (cloud && lockedVertices)
- {
- //locked entities
- ccUtils::DisplayLockedVerticesWarning(ent->getName(),false);
- }
- else
- {
- //we need a cloud or a mesh
- ccLog::Warning(QString("[Scale Matching] Entity '%1' can't be rescaled this way!").arg(ent->getName()));
- }
-
- //if the reference entity is invalid!
- if (scales[i] <= 0 && i == refEntityIndex)
- {
- ccLog::Error("Reference entity has an invalid scale! Can't proceed.");
- return false;
- }
-
- if (!nProgress.oneStep())
- {
- //process cancelled by user
- return false;
- }
- }
-
- ccLog::Print(QString("[Scale Matching] Reference entity scale: %1").arg(scales[refEntityIndex]));
-
- //now we can rescale
- pDlg.setMethodTitle(QObject::tr("Rescaling entities"));
- {
- for (unsigned i=0; i<count; ++i)
- {
- if (i == refEntityIndex)
- continue;
- if (scales[i] < 0)
- continue;
-
- ccLog::Print(QString("[Scale Matching] Entity '%1' scale: %2").arg(entities[i]->getName()).arg(scales[i]));
- if (scales[i] <= CCCoreLib::ZERO_TOLERANCE_D)
- {
- ccLog::Warning("[Scale Matching] Entity scale is too small!");
- continue;
- }
-
- ccHObject* ent = entities[i];
-
- bool lockedVertices;
- ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(ent,&lockedVertices);
- if (!cloud || lockedVertices)
- continue;
-
- double scaled = 1.0;
- if (algo == ICP_SCALE)
- scaled = scales[i];
- else
- scaled = scales[refEntityIndex] / scales[i];
-
- PointCoordinateType scale_pc = static_cast<PointCoordinateType>(scaled);
-
- //we temporarily detach entity, as it may undergo
- //"severe" modifications (octree deletion, etc.) --> see ccPointCloud::scale
- MainWindow* instance = dynamic_cast<MainWindow*>(parent);
- MainWindow::ccHObjectContext objContext;
- if (instance)
- {
- objContext = instance->removeObjectTemporarilyFromDBTree(cloud);
- }
-
- CCVector3 C = cloud->getOwnBB().getCenter();
-
- cloud->scale( scale_pc,
- scale_pc,
- scale_pc,
- C );
-
- if (instance)
- instance->putObjectBackIntoDBTree(cloud,objContext);
- cloud->prepareDisplayForRefresh_recursive();
-
- //don't forget the 'global shift'!
- const CCVector3d& shift = cloud->getGlobalShift();
- cloud->setGlobalShift(shift*scaled);
- //DGM: nope! Not the global scale!
- }
-
- if (!nProgress.oneStep())
- {
- //process cancelled by user
- return false;
- }
- }
-
- return true;
- }
- }
|