/**************************************************************************** Copyright (C) 2010-2012 the Office National des Forêts (ONF), France All rights reserved. Contact : alexandre.piboule@onf.fr Developers : Alexandre PIBOULE (ONF) This file is part of PluginONF library. PluginONF is free library: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. PluginONF 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 Lesser General Public License along with PluginONF. If not, see . *****************************************************************************/ #include "onf_stepfitandfiltercylindersinsections.h" #include "tools/onf_citations.h" #include "ct_math/ct_sphericalline3d.h" ONF_StepFitAndFilterCylindersInSections::ONF_StepFitAndFilterCylindersInSections() : SuperClass() { _max_error = 0.04; _max_relative_error = 0.30; _min_radius = 0.02; _max_radius = 0.80; _phi_max = 30; _activeFiltering = true; _activeFilteringRelative = true; _activeFilteringVerticality = true; } QString ONF_StepFitAndFilterCylindersInSections::description() const { return tr("Ajuster des cylindres par clusters/billons"); } QString ONF_StepFitAndFilterCylindersInSections::detailledDescription() const { return tr(""); } QString ONF_StepFitAndFilterCylindersInSections::inputDescription() const { return SuperClass::inputDescription() + tr("

"); } QString ONF_StepFitAndFilterCylindersInSections::outputDescription() const { return SuperClass::outputDescription() + tr("

"); } QString ONF_StepFitAndFilterCylindersInSections::detailsDescription() const { return tr(""); } QStringList ONF_StepFitAndFilterCylindersInSections::getStepRISCitations() const { return QStringList() << ONF_citations::citation()._citationOthmaniEtAl2001; } CT_VirtualAbstractStep* ONF_StepFitAndFilterCylindersInSections::createNewInstance() const { // cree une copie de cette etape return new ONF_StepFitAndFilterCylindersInSections(); } //////////////////// PROTECTED ////////////////// void ONF_StepFitAndFilterCylindersInSections::declareInputModels(CT_StepInModelStructureManager& manager) { manager.addResult(_inResult, tr("Billons")); manager.setZeroOrMoreRootGroup(_inResult, _inZeroOrMoreRootGroup); manager.addGroup(_inZeroOrMoreRootGroup, _inSectionGroup, tr("Billons")); manager.addGroup(_inSectionGroup, _inClusterGroup, tr("Clusters")); manager.addItem(_inClusterGroup, _inCluster, tr("Points")); manager.addItem(_inClusterGroup, _inRefPoint, tr("Points de référence")); } void ONF_StepFitAndFilterCylindersInSections::fillPostInputConfigurationDialog(CT_StepConfigurableDialog* postInputConfigDialog) { postInputConfigDialog->addDouble(tr("Rayon minimum :"), "cm", 0, 1000, 2, _min_radius, 100); postInputConfigDialog->addDouble(tr("Rayon maximum :"), "cm", 0, 1000, 2, _max_radius, 100); postInputConfigDialog->addBool("", "", tr("Filtrer les cylindres sur la RMSE"), _activeFiltering); postInputConfigDialog->addDouble(tr("Erreur maximale :"), "cm", 0, 1000, 2, _max_error, 100); postInputConfigDialog->addBool("", "", tr("Filtrer les cylindres sur la RMSE relative"), _activeFilteringRelative); postInputConfigDialog->addDouble(tr("Erreur maximale relative au diamètre :"), "%", 0, 100, 2, _max_relative_error, 100); postInputConfigDialog->addBool("", "", tr("Filtrer les directions sur la RMSE"), _activeLinesFiltering); postInputConfigDialog->addDouble(tr("Erreur maximale :"), "cm", 0, 1000, 2, _max_line_error, 100); postInputConfigDialog->addBool("", "", tr("Filtrer les cylindres sur leur verticalité"), _activeFilteringVerticality); postInputConfigDialog->addDouble(tr("Angle maximum à la verticale (depuis de zénith) :"), "°", 0, 180, 2, _phi_max); } void ONF_StepFitAndFilterCylindersInSections::declareOutputModels(CT_StepOutModelStructureManager& manager) { manager.addResultCopy(_inResult); manager.addItem(_inClusterGroup, _outCylinder, tr("Cylindre")); } void ONF_StepFitAndFilterCylindersInSections::compute() { double phi_rad = M_PI*_phi_max/180; for (CT_StandardItemGroup* section : _inSectionGroup.iterateOutputs(_inResult)) { QList refPoints; QMap clusters; QMap groups; for (const CT_StandardItemGroup* group : section->groups(_inClusterGroup)) { for (const CT_ReferencePoint* refPoint : group->singularItems(_inRefPoint)) { if (isStopped()) {return;} const CT_PointCluster* pointCluster = group->singularItem(_inCluster); if (pointCluster != nullptr && pointCluster->pointCloudIndex() != nullptr) { refPoints.append(refPoint); clusters.insert(refPoint, pointCluster); groups.insert(refPoint, const_cast(group)); } } } int size = refPoints.size(); int i = 0; // parcours des points de référence de la section while((i < size) && !isStopped()) { // liste contenant le point d'avant, le point et le point d'après QList refPointsForDirection; if (i > 0) { refPointsForDirection.append(refPoints.at(i-1)); } else { if (i < (size-2)) { refPointsForDirection.append(refPoints.at(i+2)); } } refPointsForDirection.append(refPoints.at(i)); if (i < (size-1)) { refPointsForDirection.append(refPoints.at(i+1)); } else { if (i > 1) { refPointsForDirection.append(refPoints.at(i-2)); } } // ajustement de la ligne de direction CT_LineData *direction = CT_LineData::staticCreateLineDataFromItemCenters(refPointsForDirection); // on récupère le groupe de point const CT_PointCluster *item = clusters.value(refPoints.at(i), nullptr); if (direction != nullptr && item != nullptr) { // ajustement du cylindre CT_CircleData circleData; CT_CylinderData *cData = CT_CylinderData::staticCreate3DCylinderDataFromPointCloudAndDirection(*item->pointCloudIndex(), Eigen::Vector3d(item->getBarycenter().x(), item->getBarycenter().y(), item->getBarycenter().z()), *direction, &circleData); // et on ajoute un CT_cylinder si le cylindre ajusté existe if(cData != nullptr) { double phi = 0; double theta = 0; double length = 0; const Eigen::Vector3d &cylDirection = cData->getDirection(); CT_SphericalLine3D::convertToSphericalCoordinates(cylDirection(0), cylDirection(1), cylDirection(2), phi, theta, length); if ((cData->getRadius() > _min_radius) && (cData->getRadius() < _max_radius) && ((!_activeFiltering) || (cData->getCircleError() < _max_error)) && ((!_activeFilteringRelative) || (cData->getCircleError()/(2*cData->getRadius()) < _max_relative_error)) && ((!_activeFilteringVerticality) || (phi < phi_rad)) && ((!_activeLinesFiltering) || (cData->getLineError() < _max_line_error))) { CT_Cylinder *cyl = new CT_Cylinder(cData); CT_StandardItemGroup* grp = groups.value(refPoints.at(i)); grp->addSingularItem(_outCylinder, cyl); } else { delete cData; } } delete direction; } ++i; } } }