void ccVolumeCalcTool::setDisplayedNumberPrecision(int precision)
{
	//update window
	if (m_glWindow)
	{
		ccGui::ParamStruct params = m_glWindow->getDisplayParameters();
		params.displayedNumPrecision = precision;
		m_glWindow->setDisplayParameters(params, true);
		m_glWindow->redraw(true, false);
	}

	//update report
	if (clipboardPushButton->isEnabled())
	{
		outputReport(m_lastReport);
	}
}
bool ccVolumeCalcTool::updateGrid()
{
	if (!m_cloud2)
	{
		assert(false);
		return false;
	}

	//cloud bounding-box --> grid size
	ccBBox box = getCustomBBox();
	if (!box.isValid())
	{
		return false;
	}

	unsigned gridWidth = 0, gridHeight = 0;
	if (!getGridSize(gridWidth, gridHeight))
	{
		return false;
	}

	//grid step
	double gridStep = getGridStep();
	assert(gridStep != 0);

	//ground
	ccGenericPointCloud* groundCloud = 0;
	double groundHeight = 0;
	switch (groundComboBox->currentIndex())
	{
	case 0:
		groundHeight = groundEmptyValueDoubleSpinBox->value();
		break;
	case 1:
		groundCloud = m_cloud1 ? m_cloud1 : m_cloud2;
		break;
	case 2:
		groundCloud = m_cloud2;
		break;
	default:
		assert(false);
		return false;
	}

	//ceil
	ccGenericPointCloud* ceilCloud = 0;
	double ceilHeight = 0;
	switch (ceilComboBox->currentIndex())
	{
	case 0:
		ceilHeight = ceilEmptyValueDoubleSpinBox->value();
		break;
	case 1:
		ceilCloud = m_cloud1 ? m_cloud1 : m_cloud2;
		break;
	case 2:
		ceilCloud = m_cloud2;
		break;
	default:
		assert(false);
		return false;
	}

	ccVolumeCalcTool::ReportInfo reportInfo;

	if (ComputeVolume(	m_grid,
						groundCloud,
						ceilCloud,
						box,
						getProjectionDimension(),
						gridStep,
						gridWidth,
						gridHeight,
						getTypeOfProjection(),
						getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox),
						reportInfo,
						groundHeight,
						ceilHeight,
						this))
	{	
		outputReport(reportInfo);
		return true;
	}
	else
	{
		return false;
	}
}
bool ccVolumeCalcTool::updateGrid()
{
    if (!m_cloud2)
    {
        assert(false);
        return false;
    }

    //cloud bounding-box --> grid size
    ccBBox box = getCustomBBox();
    if (!box.isValid())
    {
        return false;
    }

    unsigned gridWidth = 0, gridHeight = 0;
    if (!getGridSize(gridWidth, gridHeight))
    {
        return false;
    }

    //grid size
    unsigned gridTotalSize = gridWidth * gridHeight;
    if (gridTotalSize == 1)
    {
        if (QMessageBox::question(this, "Unexpected grid size", "The generated grid will only have 1 cell! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
            return false;
    }
    else if (gridTotalSize > 10000000)
    {
        if (QMessageBox::question(this, "Big grid size", "The generated grid will have more than 10.000.000 cells! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
            return false;
    }

    //grid step
    double gridStep = getGridStep();
    assert(gridStep != 0);

    //memory allocation
    CCVector3d minCorner = CCVector3d::fromArray(box.minCorner().u);
    if (!m_grid.init(gridWidth, gridHeight, gridStep, minCorner))
    {
        //not enough memory
        ccLog::Error("Not enough memory");
        return false;
    }

    //ground
    ccGenericPointCloud* groundCloud = 0;
    double groundHeight = 0;
    switch (groundComboBox->currentIndex())
    {
    case 0:
        groundHeight =	groundEmptyValueDoubleSpinBox->value();
        break;
    case 1:
        groundCloud = m_cloud1 ? m_cloud1 : m_cloud2;
        break;
    case 2:
        groundCloud = m_cloud2;
        break;
    default:
        assert(false);
        return false;
    }

    //vertical dimension
    const unsigned char Z = getProjectionDimension();
    assert(Z >= 0 && Z <= 2);
    //per-cell Z computation
    ccRasterGrid::ProjectionType projectionType = getTypeOfProjection();

    ccProgressDialog pDlg(true, this);

    ccRasterGrid groundRaster;
    if (groundCloud)
    {
        if (!groundRaster.init(gridWidth, gridHeight, gridStep, minCorner))
        {
            //not enough memory
            ccLog::Error("Not enough memory");
            return false;
        }

        if (groundRaster.fillWith(	groundCloud,
                                    Z,
                                    projectionType,
                                    getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox) == ccRasterGrid::INTERPOLATE,
                                    ccRasterGrid::INVALID_PROJECTION_TYPE,
                                    &pDlg))
        {
            groundRaster.fillEmptyCells(getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox), groundEmptyValueDoubleSpinBox->value());
            ccLog::Print(QString("[Volume] Ground raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(m_grid.width).arg(m_grid.height).arg(m_grid.minHeight).arg(m_grid.maxHeight));
        }
        else
        {
            return false;
        }
    }

    //ceil
    ccGenericPointCloud* ceilCloud = 0;
    double ceilHeight = 0;
    switch (ceilComboBox->currentIndex())
    {
    case 0:
        ceilHeight = ceilEmptyValueDoubleSpinBox->value();
        break;
    case 1:
        ceilCloud = m_cloud1 ? m_cloud1 : m_cloud2;
        break;
    case 2:
        ceilCloud = m_cloud2;
        break;
    default:
        assert(false);
        return false;
    }

    ccRasterGrid ceilRaster;
    if (ceilCloud)
    {
        if (!ceilRaster.init(gridWidth, gridHeight, gridStep, minCorner))
        {
            //not enough memory
            ccLog::Error("Not enough memory");
            return false;
        }

        if (ceilRaster.fillWith(ceilCloud,
                                Z,
                                projectionType,
                                getFillEmptyCellsStrategy(fillCeilEmptyCellsComboBox) == ccRasterGrid::INTERPOLATE,
                                ccRasterGrid::INVALID_PROJECTION_TYPE,
                                &pDlg))
        {
            ceilRaster.fillEmptyCells(getFillEmptyCellsStrategy(fillCeilEmptyCellsComboBox), ceilEmptyValueDoubleSpinBox->value());
            ccLog::Print(QString("[Volume] Ceil raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(m_grid.width).arg(m_grid.height).arg(m_grid.minHeight).arg(m_grid.maxHeight));
        }
        else
        {
            return false;
        }
    }

    //update grid and compute volume
    {
        pDlg.setMethodTitle(tr("Volume computation"));
        pDlg.setInfo(tr("Cells: %1 x %2").arg(m_grid.width).arg(m_grid.height));
        pDlg.start();
        pDlg.show();
        QCoreApplication::processEvents();
        CCLib::NormalizedProgress nProgress(&pDlg, m_grid.width*m_grid.height);

        ReportInfo repotInfo;
        size_t ceilNonMatchingCount = 0;
        size_t groundNonMatchingCount = 0;
        size_t cellCount = 0;

        //at least one of the grid is based on a cloud
        m_grid.nonEmptyCellCount = 0;
        for (unsigned i = 0; i < m_grid.height; ++i)
        {
            for (unsigned j = 0; j < m_grid.width; ++j)
            {
                ccRasterCell& cell = m_grid.rows[i][j];

                bool validGround = true;
                cell.minHeight = groundHeight;
                if (groundCloud)
                {
                    cell.minHeight = groundRaster.rows[i][j].h;
                    validGround = std::isfinite(cell.minHeight);
                }

                bool validCeil = true;
                cell.maxHeight = ceilHeight;
                if (ceilCloud)
                {
                    cell.maxHeight = ceilRaster.rows[i][j].h;
                    validCeil = std::isfinite(cell.maxHeight);
                }

                if (validGround && validCeil)
                {
                    cell.h = cell.maxHeight - cell.minHeight;
                    cell.nbPoints = 1;

                    repotInfo.volume += cell.h;
                    if (cell.h < 0)
                    {
                        repotInfo.removedVolume -= cell.h;
                    }
                    else if (cell.h > 0)
                    {
                        repotInfo.addedVolume += cell.h;
                    }
                    repotInfo.surface += 1.0;
                    ++m_grid.nonEmptyCellCount; //= matching count
                    ++cellCount;
                }
                else
                {
                    if (validGround)
                    {
                        ++cellCount;
                        ++groundNonMatchingCount;
                    }
                    else if (validCeil)
                    {
                        ++cellCount;
                        ++ceilNonMatchingCount;
                    }
                    cell.h = std::numeric_limits<double>::quiet_NaN();
                    cell.nbPoints = 0;
                }

                cell.avgHeight = (groundHeight + ceilHeight) / 2;
                cell.stdDevHeight = 0;

                if (!nProgress.oneStep())
                {
                    ccLog::Warning("[Volume] Process cancelled by the user");
                    return false;
                }
            }
        }
        m_grid.validCellCount = m_grid.nonEmptyCellCount;

        //count the average number of valid neighbors
        {
            size_t validNeighborsCount = 0;
            size_t count = 0;
            for (unsigned i = 1; i < m_grid.height - 1; ++i)
            {
                for (unsigned j = 1; j < m_grid.width - 1; ++j)
                {
                    ccRasterCell& cell = m_grid.rows[i][j];
                    if (cell.h == cell.h)
                    {
                        for (unsigned k = i - 1; k <= i + 1; ++k)
                        {
                            for (unsigned l = j - 1; l <= j + 1; ++l)
                            {
                                if (k != i || l != j)
                                {
                                    ccRasterCell& otherCell = m_grid.rows[k][l];
                                    if (std::isfinite(otherCell.h))
                                    {
                                        ++validNeighborsCount;
                                    }
                                }
                            }
                        }

                        ++count;
                    }
                }
            }

            if (count)
            {
                repotInfo.averageNeighborsPerCell = static_cast<double>(validNeighborsCount) / count;
            }
        }

        repotInfo.matchingPrecent = static_cast<float>(m_grid.validCellCount * 100) / cellCount;
        repotInfo.groundNonMatchingPercent = static_cast<float>(groundNonMatchingCount * 100) / cellCount;
        repotInfo.ceilNonMatchingPercent = static_cast<float>(ceilNonMatchingCount * 100) / cellCount;
        float cellArea = static_cast<float>(m_grid.gridStep * m_grid.gridStep);
        repotInfo.volume *= cellArea;
        repotInfo.addedVolume *= cellArea;
        repotInfo.removedVolume *= cellArea;
        repotInfo.surface *= cellArea;

        outputReport(repotInfo);
    }

    m_grid.setValid(true);

    return true;
}