/**************************************************************************** Copyright (C) 2012-2012 Universite de Sherbrooke, Quebec, CANADA All rights reserved. Contact : richard.fournier@usherbrooke.ca jean-francois.cote@nrcan-rncan.gc.ca joris.ravaglia@gmail.com Developers : Joris RAVAGLIA Adapted by Alexandre Piboule for Computree 2.0 This file is part of Computree version 2.0. Computree 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, either version 3 of the License, or (at your option) any later version. Computree 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. You should have received a copy of the GNU General Public License along with Computree. If not, see . *****************************************************************************/ #include "lvox2_stepcomputelvoxgrids.h" #include "ct_result/model/inModel/ct_inresultmodelgrouptocopy.h" #include "ct_result/model/outModel/tools/ct_outresultmodelgrouptocopypossibilities.h" // Inclusion of standard result class #include "ct_result/ct_resultgroup.h" // Inclusion of used ItemDrawable classes #include "ct_itemdrawable/ct_scene.h" #include "ct_itemdrawable/ct_scanner.h" #include "ct_itemdrawable/ct_grid3d.h" #include "ct_itemdrawable/ct_pointsattributesscalartemplated.h" #include "qvector3d.h" #include "ct_step/abstract/ct_abstractsteploadfile.h" #include "ct_view/ct_stepconfigurabledialog.h" #include "tools/lvox_computehitsthread.h" #include "tools/lvox_computetheoriticalsthread.h" #include "tools/lvox_computebeforethread.h" #include "tools/lvox_computedensitythread.h" #include "urfm/tools/lvox2_computeactualbeamthread.h" #include "urfm/tools/lvox2_computehitsthread.h" #include "ct_iterator/ct_pointiterator.h" #include #include #include #include #define DEF_SearchInResult "r" #define DEF_SearchInScene "sc" #define DEF_SearchInFlag "flag" #define DEF_SearchInScan "sca" #define DEF_SearchInGroup "gr" LVOX2_StepComputeLvoxGrids::LVOX2_StepComputeLvoxGrids(CT_StepInitializeData &dataInit) : CT_AbstractStep(dataInit) { //********************************************// // Attributes of LVox // //********************************************// _res = 0.1; //0.5; _effectiveRayThresh = 0; _computeDistances = false; _ntMode = 1; //0; // by default, Beam number is theoretical _gridMode = 3;//1 _xBase = 0;//-20.0; _yBase = 0;//-20.0; _zBase = 0;//-10.0; _xDim = 60;//80; _yDim = 60;//80; _zDim = 40;//80; _cylindricFilter = true; } QString LVOX2_StepComputeLvoxGrids::getStepDescription() const { // Gives the descrption to print in the GUI return tr("2- Calculer les grilles LVOX"); } CT_VirtualAbstractStep* LVOX2_StepComputeLvoxGrids::createNewInstance(CT_StepInitializeData &dataInit) { // Creates an instance of this step return new LVOX2_StepComputeLvoxGrids(dataInit); } void LVOX2_StepComputeLvoxGrids::createInResultModelListProtected() { CT_InResultModelGroupToCopy *resultModel = createNewInResultModelForCopy(DEF_SearchInResult, tr("Scène(s)")); resultModel->setZeroOrMoreRootGroup(); resultModel->addGroupModel("", DEF_SearchInGroup, CT_AbstractItemGroup::staticGetType(), tr("Scan")); resultModel->addItemModel(DEF_SearchInGroup, DEF_SearchInScene, CT_Scene::staticGetType(), tr("Scène")); resultModel->addItemModel(DEF_SearchInGroup, DEF_SearchInScan, CT_Scanner::staticGetType(), tr("Scanner")); resultModel->addItemModel(DEF_SearchInGroup, DEF_SearchInFlag, CT_PointsAttributesScalarTemplated::staticGetType(),tr("Flag"), "", CT_InAbstractModel::C_ChooseOneIfMultiple, CT_InAbstractModel::F_IsOptional); } void LVOX2_StepComputeLvoxGrids::createOutResultModelListProtected() { // create a new OUT result that is a copy of the IN result selected by the user CT_OutResultModelGroupToCopyPossibilities *res = createNewOutResultModelToCopy(DEF_SearchInResult); if (res != NULL) { // in this result we add a CT_AffiliationID to the group named DEF_SearchInGroup. The name of the model of // the CT_AffiliationID will be generated automatically by the _outAffiliationIDModelName object. res->addItemModel(DEF_SearchInGroup, _hits_ModelName, new CT_Grid3D(), tr("Hits")); res->addItemAttributeModel(_hits_ModelName, _NiFlag_ModelName, new CT_StdItemAttributeT("LVOX_GRD_NI"), tr("isNi")); if (_ntMode==0||_ntMode==2) // Nt is theoritical (initial formulation) { res->addItemModel(DEF_SearchInGroup, _theo_ModelName, new CT_Grid3D(), tr("Theoretical")); res->addItemAttributeModel(_theo_ModelName, _NtFlag_ModelName, new CT_StdItemAttributeT("LVOX_GRD_NT"), tr("isNt")); } if (_ntMode==1||_ntMode==2) {// Nt is actual res->addItemModel(DEF_SearchInGroup, _actu_ModelName, new CT_Grid3D(), tr("ActualTotal")); res->addItemAttributeModel(_actu_ModelName, _NtaFlag_ModelName, new CT_StdItemAttributeT("LVOX_GRD_NTA"), tr("isNta")); } res->addItemModel(DEF_SearchInGroup, _bef_ModelName, new CT_Grid3D(), tr("Before")); res->addItemAttributeModel(_bef_ModelName, _NbFlag_ModelName, new CT_StdItemAttributeT("LVOX_GRD_NB"), tr("isNb")); res->addItemModel(DEF_SearchInGroup, _density_ModelName, new CT_Grid3D(), tr("Density")); res->addItemAttributeModel(_density_ModelName, _DensityFlag_ModelName, new CT_StdItemAttributeT("LVOX_GRD_DENSITY"), tr("isDensity")); if (_computeDistances) { res->addItemModel(DEF_SearchInGroup, _deltain_ModelName, new CT_Grid3D(), tr("DeltaIn")); res->addItemModel(DEF_SearchInGroup, _deltaout_ModelName, new CT_Grid3D(), tr("DeltaOut")); if (_ntMode==0||_ntMode==2) // Nt is theoritical (initial formulation) { res->addItemModel(DEF_SearchInGroup, _deltatheo_ModelName, new CT_Grid3D(), tr("DeltaTheoretical")); } if (_ntMode==1||_ntMode==2) { // Nt is actual res->addItemModel(DEF_SearchInGroup, _deltaactu_ModelName, new CT_Grid3D(), tr("DeltaActualTotal")); } res->addItemModel(DEF_SearchInGroup, _deltabef_ModelName, new CT_Grid3D(), tr("DeltaBefore")); } } } void LVOX2_StepComputeLvoxGrids::createPostConfigurationDialog() { CT_StepConfigurableDialog *configDialog = newStandardPostConfigurationDialog(); //********************************************// // Attributes of LVox // //********************************************// configDialog->addDouble(tr("Resolution of the grids"),tr("meters"),0.0001,10000,2, _res ); configDialog->addInt(tr("Minimum number of effective ray in a voxel to take it into account"),tr(""),0,100000, _effectiveRayThresh ); configDialog->addBool(tr("Compute Distances"), tr(""), tr(""), _computeDistances); //configDialog->addBool(tr("Compute actual beams"), tr(""), tr(""), _computeActualBeams); configDialog->addEmpty(); configDialog->addText(tr("Computation of total beam number :"),"", ""); CT_ButtonGroup &bg_ntMode = configDialog->addButtonGroup(_ntMode); configDialog->addExcludeValue("", "", tr("Default mode : Theoretical number"), bg_ntMode, 0); configDialog->addExcludeValue("", "", tr("Actual beam number (a full point cloud is required)"), bg_ntMode, 1); configDialog->addExcludeValue("", "", tr("Both are computed"), bg_ntMode, 2); configDialog->addEmpty(); configDialog->addText(tr("Reference for (minX, minY, minZ) corner of the grid :"),"", ""); CT_ButtonGroup &bg_gridMode = configDialog->addButtonGroup(_gridMode); configDialog->addExcludeValue("", "", tr("Default mode : Bounding box of the scene"), bg_gridMode, 0); configDialog->addExcludeValue("", "", tr("Custom mode : Relative to following coordinates:"), bg_gridMode, 1); configDialog->addExcludeValue("", "", tr("Custom mode : Relative to following coordinates + custom dimensions:"), bg_gridMode, 2); configDialog->addExcludeValue("", "", tr("Custom mode : centered on following coordinates + custom dimensions:"), bg_gridMode, 3); configDialog->addDouble(tr("X coordinate:"), "", -std::numeric_limits::max(), std::numeric_limits::max(), 4, _xBase); configDialog->addDouble(tr("Y coordinate:"), "", -std::numeric_limits::max(), std::numeric_limits::max(), 4, _yBase); configDialog->addDouble(tr("Z coordinate:"), "", -std::numeric_limits::max(), std::numeric_limits::max(), 4, _zBase); configDialog->addInt(tr("X dimension:"), "", 1, 1000, _xDim); configDialog->addInt(tr("Y dimension:"), "", 1, 1000, _yDim); configDialog->addInt(tr("Z dimension:"), "", 1, 1000, _zDim); configDialog->addBool(tr("Placette cylindrique"),"", "", _cylindricFilter); } void LVOX2_StepComputeLvoxGrids::compute() { // Gets the out result CT_ResultGroup* outResult = getOutResultList().first(); QList baseThreads; QList densityThreads; QMap > pointsOfView; // Global limits of generated grids double xMin = std::numeric_limits::max(); double yMin = std::numeric_limits::max(); double zMin = std::numeric_limits::max(); double xMax = -std::numeric_limits::max(); double yMax = -std::numeric_limits::max(); double zMax = -std::numeric_limits::max(); // Global limit of the enlarge grid (necessary for computations) double xMin2 = 0.f; double yMin2 = 0.f; double zMin2 = 0.f; double xMax2 = 0.f; double yMax2 = 0.f; double zMax2 = 0.f; double xMinScene = std::numeric_limits::max(); double yMinScene = std::numeric_limits::max(); double zMinScene = std::numeric_limits::max(); double xMaxScene = -std::numeric_limits::max(); double yMaxScene = -std::numeric_limits::max(); double zMaxScene = -std::numeric_limits::max(); double xMinScanner = std::numeric_limits::max(); double yMinScanner = std::numeric_limits::max(); double zMinScanner = std::numeric_limits::max(); double xMaxScanner = -std::numeric_limits::max(); double yMaxScanner = -std::numeric_limits::max(); double zMaxScanner = -std::numeric_limits::max(); // on va rechercher tous les groupes contenant des nuages de points (qui ont été choisi par l'utilisateur) CT_ResultGroupIterator itGrp(outResult, this, DEF_SearchInGroup); while (itGrp.hasNext() && !isStopped()) { CT_AbstractItemGroup *group = (CT_AbstractItemGroup*) itGrp.next(); const CT_Scene* scene = (CT_Scene*)group->firstItemByINModelName(this, DEF_SearchInScene); const CT_Scanner* scanner = (CT_Scanner*)group->firstItemByINModelName(this, DEF_SearchInScan); const CT_PointsAttributesScalarTemplated* flag = (CT_PointsAttributesScalarTemplated*)group->firstItemByINModelName(this, DEF_SearchInFlag); if (flag == NULL) {qDebug() << "Pas de Flag";} if (scene!=NULL && scanner!=NULL) { pointsOfView.insert(group, QPair(scene, scanner)); Eigen::Vector3d min, max; scene->getBoundingBox(min, max); if (min.x() < xMinScene) {xMinScene = min.x();} if (min.y() < yMinScene) {yMinScene = min.y();} if (min.z() < zMinScene) {zMinScene = min.z();} if (max.x() > xMaxScene) {xMaxScene = max.x();} if (max.y() > yMaxScene) {yMaxScene = max.y();} if (max.z() > zMaxScene) {zMaxScene = max.z();} if (scanner->getCenterX() < xMinScanner) {xMinScanner = scanner->getCenterX();} if (scanner->getCenterY() < yMinScanner) {yMinScanner = scanner->getCenterY();} if (scanner->getCenterZ() < zMinScanner) {zMinScanner = scanner->getCenterZ();} if (scanner->getCenterX() > xMaxScanner) {xMaxScanner = scanner->getCenterX();} if (scanner->getCenterY() > yMaxScanner) {yMaxScanner = scanner->getCenterY();} if (scanner->getCenterZ() > zMaxScanner) {zMaxScanner = scanner->getCenterZ();} } } if (_gridMode == 0) { xMin = std::min(xMinScene, xMinScanner); yMin = std::min(yMinScene, yMinScanner); zMin = std::min(zMinScene, zMinScanner); xMax = std::max(xMaxScene, xMaxScanner); yMax = std::max(yMaxScene, yMaxScanner); zMax = std::max(zMaxScene, zMaxScanner); } else if (_gridMode == 1) { double xMinAdjusted = std::min(xMinScene, xMinScanner); double yMinAdjusted = std::min(yMinScene, yMinScanner); double zMinAdjusted = std::min(zMinScene, zMinScanner); double xMaxAdjusted = std::max(xMaxScene, xMaxScanner); double yMaxAdjusted = std::max(yMaxScene, yMaxScanner); double zMaxAdjusted = std::max(zMaxScene, zMaxScanner); xMin = _xBase; yMin = _yBase; zMin = _zBase; while (xMin < xMinAdjusted) {xMin += _res;} while (yMin < yMinAdjusted) {yMin += _res;} while (zMin < zMinAdjusted) {zMin += _res;} while (xMin > xMinAdjusted) {xMin -= _res;} while (yMin > yMinAdjusted) {yMin -= _res;} while (zMin > zMinAdjusted) {zMin -= _res;} xMax = xMin + _res; yMax = yMin + _res; zMax = zMin + _res; while (xMax < xMaxAdjusted) {xMax += _res;} while (yMax < yMaxAdjusted) {yMax += _res;} while (zMax < zMaxAdjusted) {zMax += _res;} } else if (_gridMode == 2) { xMin = _xBase; yMin = _yBase; zMin = _zBase; xMax = xMin + _res*_xDim; yMax = yMin + _res*_yDim; zMax = zMin + _res*_zDim; } else { xMin = _xBase - _res*_xDim; yMin = _yBase - _res*_yDim; zMin = _zBase - _res*_zDim; xMax = _xBase + _res*_xDim; yMax = _yBase + _res*_yDim; zMax = _zBase + _res*_zDim; } // enlarge the grid when necessary to include scan positions bool enlarged = false; xMin2 = xMin; xMax2 = xMax; yMin2 = yMin, yMax2 = yMax; zMin2 = zMin; zMax2 = zMax; while (xMin2 > xMinScanner) {xMin2 -= _res; enlarged = true;} while (yMin2 > yMinScanner) {yMin2 -= _res; enlarged = true;} while (zMin2 > zMinScanner) {zMin2 -= _res; enlarged = true;} while (xMax2 < xMaxScanner) {xMax2 += _res; enlarged = true;} while (yMax2 < yMaxScanner) {yMax2 += _res; enlarged = true;} while (zMax2 < zMaxScanner) {zMax2 += _res; enlarged = true;} if (enlarged) { PS_LOG->addMessage(LogInterface::warning, LogInterface::step, tr("Dimensions spécifiées ne contenant pas les positions de scans : la grille a du être élargie !")); } xMin2 -= _res; yMin2 -= _res; zMin2 -= _res; xMax2 += _res; yMax2 += _res; zMax2 += _res; QMapIterator > it(pointsOfView); while (it.hasNext() && !isStopped()) { it.next(); CT_AbstractItemGroup* group = it.key(); const CT_Scene* scene = it.value().first; const CT_Scanner* scanner =it.value().second; // check properties of the point cloud size_t zeros = 0; size_t fars = 0; const CT_AbstractPointCloudIndex *pointCloudIndex = scene->getPointCloudIndex(); size_t n_points = pointCloudIndex->size(); Eigen::Vector3d scanPos = scanner->getPosition(); CT_PointIterator itP(pointCloudIndex); while (itP.hasNext()) { const CT_Point &point = itP.next().currentPoint(); if ((abs(point(0)-scanPos(0))<=0.001)&&(abs(point(1)-scanPos(1))<=0.001)&&(abs(point(2)-scanPos(2))<=0.001)) { zeros++; if (zeros==1) { qDebug() << "point example" << point(0) << point(1) << point(2); } } if (pow(point(0)-scanPos(0),2.0)+pow(point(1)-scanPos(1),2.0)+pow(point(2)-scanPos(2),2.0)>400) { fars++; } } qDebug() << "scan: null beams="<* hitGrid = CT_Grid3D::createGrid3DFromXYZCoords(_hits_ModelName.completeName(), outResult, xMin2, yMin2, zMin2, xMax2, yMax2, zMax2, _res, -1, 0, true); hitGrid->addItemAttribute(new CT_StdItemAttributeT(_NiFlag_ModelName.completeName(), "LVOX_GRD_NI", outResult, true)); CT_Grid3D* theoriticalGrid = NULL; if (_ntMode==0||_ntMode==2) // Nt is theoritical (initial formulation) { theoriticalGrid = new CT_Grid3D(_theo_ModelName.completeName(), outResult, hitGrid->minX(), hitGrid->minY(), hitGrid->minZ(), hitGrid->xdim(), hitGrid->ydim(), hitGrid->zdim(), _res, -1, 0); theoriticalGrid->addItemAttribute(new CT_StdItemAttributeT(_NtFlag_ModelName.completeName(), "LVOX_GRD_NT", outResult, true)); } CT_Grid3D* actualGrid = NULL; if (_ntMode==1||_ntMode==2) // Nt is actual { actualGrid = new CT_Grid3D(_actu_ModelName.completeName(), outResult, hitGrid->minX(), hitGrid->minY(), hitGrid->minZ(), hitGrid->xdim(), hitGrid->ydim(), hitGrid->zdim(), _res, -1, 0); actualGrid->addItemAttribute(new CT_StdItemAttributeT(_NtaFlag_ModelName.completeName(), "LVOX_GRD_NTA", outResult, true)); } CT_Grid3D* beforeGrid = new CT_Grid3D(_bef_ModelName.completeName(), outResult, hitGrid->minX(), hitGrid->minY(), hitGrid->minZ(), hitGrid->xdim(), hitGrid->ydim(), hitGrid->zdim(), _res, -1, 0); beforeGrid->addItemAttribute(new CT_StdItemAttributeT(_NbFlag_ModelName.completeName(), "LVOX_GRD_NB", outResult, true)); CT_Grid3D* density = new CT_Grid3D(_density_ModelName.completeName(), outResult, hitGrid->minX(), hitGrid->minY(), hitGrid->minZ(), hitGrid->xdim(), hitGrid->ydim(), hitGrid->zdim(), _res, -1, 0); density->addItemAttribute(new CT_StdItemAttributeT(_DensityFlag_ModelName.completeName(), "LVOX_GRD_DENSITY", outResult, true)); CT_Grid3D* deltaInGrid = NULL; CT_Grid3D* deltaOutGrid = NULL; CT_Grid3D* deltaTheoritical = NULL; CT_Grid3D* deltaActual = NULL; CT_Grid3D* deltaBefore = NULL; // grids are added to results group->addItemDrawable(hitGrid); if (_ntMode==0||_ntMode==2) { // Nt is theoretical group->addItemDrawable(theoriticalGrid); } if (_ntMode==1||_ntMode==2) { // Nt is actual group->addItemDrawable(actualGrid); } group->addItemDrawable(beforeGrid); group->addItemDrawable(density); if (_computeDistances) { deltaInGrid = new CT_Grid3D(_deltain_ModelName.completeName(), outResult, hitGrid->minX(), hitGrid->minY(), hitGrid->minZ(), hitGrid->xdim(), hitGrid->ydim(), hitGrid->zdim(), _res, -1, 0); deltaOutGrid = new CT_Grid3D(_deltaout_ModelName.completeName(), outResult, hitGrid->minX(), hitGrid->minY(), hitGrid->minZ(), hitGrid->xdim(), hitGrid->ydim(), hitGrid->zdim(), _res, -1, 0); if (_ntMode==0||_ntMode==2) {// Nt is theoretical deltaTheoritical = new CT_Grid3D(_deltatheo_ModelName.completeName(), outResult, hitGrid->minX(), hitGrid->minY(), hitGrid->minZ(), hitGrid->xdim(), hitGrid->ydim(), hitGrid->zdim(), _res, -1, 0); } if (_ntMode==1||_ntMode==2) {// Nt is actual deltaActual = new CT_Grid3D(_deltaactu_ModelName.completeName(), outResult, hitGrid->minX(), hitGrid->minY(), hitGrid->minZ(), hitGrid->xdim(), hitGrid->ydim(), hitGrid->zdim(), _res, -1, 0); } deltaBefore = new CT_Grid3D(_deltabef_ModelName.completeName(), outResult, hitGrid->minX(), hitGrid->minY(), hitGrid->minZ(), hitGrid->xdim(), hitGrid->ydim(), hitGrid->zdim(), _res, -1, 0); group->addItemDrawable(deltaInGrid); group->addItemDrawable(deltaOutGrid); if (_ntMode==0||_ntMode==2) { // Nt is theoretical group->addItemDrawable(deltaTheoritical);} if (_ntMode==1||_ntMode==2) { // Nt is actual group->addItemDrawable(deltaActual);} group->addItemDrawable(deltaBefore); } LVOX2_ComputeHitsThread* hitsThread = new LVOX2_ComputeHitsThread(scanner, hitGrid, deltaInGrid, deltaOutGrid, scene, _computeDistances, _cylindricFilter, xMin, xMax, yMin, yMax, zMin,zMax); connect(hitsThread, SIGNAL(progressChanged()), this, SLOT(updateProgress())); _threadList.append(hitsThread); baseThreads.append(hitsThread); LVOX_ComputeBeforeThread* beforeThread = new LVOX_ComputeBeforeThread(scanner, beforeGrid, deltaBefore, scene, _computeDistances); connect(beforeThread, SIGNAL(progressChanged()), this, SLOT(updateProgress())); _threadList.append(beforeThread); baseThreads.append(beforeThread); if (_ntMode==0) // Nt is theoritical (initial formulation) { LVOX_ComputeTheoriticalsThread* theoreticalThread = new LVOX_ComputeTheoriticalsThread(scanner, theoriticalGrid, deltaTheoritical, _computeDistances); connect(theoreticalThread, SIGNAL(progressChanged()), this, SLOT(updateProgress())); _threadList.append(theoreticalThread); baseThreads.append(theoreticalThread); LVOX_ComputeDensityThread* densityThread = new LVOX_ComputeDensityThread(density, hitGrid, theoriticalGrid, beforeGrid, _effectiveRayThresh); connect(densityThread, SIGNAL(progressChanged()), this, SLOT(updateProgress())); _threadList.append(densityThread); densityThreads.append(densityThread); } else if (_ntMode>=1) { // Nt is really computed (new formulation that requires that the scene contains the whole point cloud) LVOX2_ComputeActualBeamThread* actualBeamThread = new LVOX2_ComputeActualBeamThread(scanner, actualGrid, deltaActual, scene, _computeDistances); connect(actualBeamThread, SIGNAL(progressChanged()), this, SLOT(updateProgress())); _threadList.append(actualBeamThread); baseThreads.append(actualBeamThread); LVOX_ComputeDensityThread* densityThread = new LVOX_ComputeDensityThread(density, hitGrid, actualGrid, beforeGrid, _effectiveRayThresh); connect(densityThread, SIGNAL(progressChanged()), this, SLOT(updateProgress())); _threadList.append(densityThread); densityThreads.append(densityThread); } if (_ntMode==2) // Nt is theoritical (initial formulation) { LVOX_ComputeTheoriticalsThread* theoreticalThread = new LVOX_ComputeTheoriticalsThread(scanner, theoriticalGrid, deltaTheoritical, _computeDistances); connect(theoreticalThread, SIGNAL(progressChanged()), this, SLOT(updateProgress())); _threadList.append(theoreticalThread); baseThreads.append(theoreticalThread); } } int size = baseThreads.size(); for (int i = 0 ; i < size ; ++i) { baseThreads.at(i)->start(); } for (int i = 0 ; i < size ; ++i) { baseThreads.at(i)->wait(); updateProgress(); } for (int i = 0 ; i < size ; ++i) { disconnect(baseThreads.at(i), SIGNAL(progressChanged()), this, SLOT(updateProgress())); } size = densityThreads.size(); for (int i = 0 ; i < size ; ++i) { densityThreads.at(i)->start(); } for (int i = 0 ; i < size ; ++i) { densityThreads.at(i)->wait(); updateProgress(); } for (int i = 0 ; i < size ; ++i) { disconnect(densityThreads.at(i), SIGNAL(progressChanged()), this, SLOT(updateProgress())); } qDeleteAll(_threadList); setProgress(100); } void LVOX2_StepComputeLvoxGrids::updateProgress() { int progress = 0; int size = _threadList.size(); for (int i = 0 ; i < size ; ++i) { progress += _threadList.at(i)->getProgress(); } progress /= size; setProgress(progress); }