/**************************************************************************** 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_stepcomputecrownprojection.h" ONF_StepComputeCrownProjection::ONF_StepComputeCrownProjection() : SuperClass() { _computeSlices = true; _spacing = 0.5; _thickness = 2; _zmin = std::numeric_limits::max(); _zmax = -std::numeric_limits::max(); _computeDirs = false; _nbDir = 8; } QString ONF_StepComputeCrownProjection::description() const { return tr("Projections de houppier"); } QString ONF_StepComputeCrownProjection::detailledDescription() const { return tr("Pour chaque nuage de point, calcule l'enveloppe convex projetée au sol de l'ensemble du nuage." "Optionnellement, calcule également des enveloppes convexes de tranches succesives, éventuellement recouvrantes. "); } QString ONF_StepComputeCrownProjection::inputDescription() const { return SuperClass::inputDescription() + tr("

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

"); } QString ONF_StepComputeCrownProjection::detailsDescription() const { return tr(""); } QString ONF_StepComputeCrownProjection::URL() const { //return tr("STEP URL HERE"); return SuperClass::URL(); //by default URL of the plugin } CT_VirtualAbstractStep* ONF_StepComputeCrownProjection::createNewInstance() const { return new ONF_StepComputeCrownProjection(); } //////////////////// PROTECTED METHODS ////////////////// void ONF_StepComputeCrownProjection::declareInputModels(CT_StepInModelStructureManager& manager) { manager.addResult(_inResult, tr("Scene(s)")); manager.setZeroOrMoreRootGroup(_inResult, _inZeroOrMoreRootGroup); manager.addGroup(_inZeroOrMoreRootGroup, _inGroup); manager.addItem(_inGroup, _inScene, tr("Scene(s)")); } void ONF_StepComputeCrownProjection::declareOutputModels(CT_StepOutModelStructureManager& manager) { manager.addResultCopy(_inResult); manager.addItem(_inGroup, _outconvexHull, tr("Enveloppe Convexe au sol")); if (_computeSlices) { manager.addGroup(_inGroup, _outgrpSlice); manager.addItem(_outgrpSlice, _outscliceCvx, tr("Enveloppe Convexe d'une tranche")); } } void ONF_StepComputeCrownProjection::fillPreInputConfigurationDialog(CT_StepConfigurableDialog* preInputConfigDialog) { preInputConfigDialog->addBool(tr("Calculer des enveloppes convexes par tranches"), "", "", _computeSlices); preInputConfigDialog->addTitle(tr("(Si décoché, seule l'enveloppe convexe totale sera calculée)")); } void ONF_StepComputeCrownProjection::fillPostInputConfigurationDialog(CT_StepConfigurableDialog* postInputConfigDialog) { if (_computeSlices) { postInputConfigDialog->addDouble(tr("Espacement des tranches"), "m", 0, std::numeric_limits::max(), 2, _spacing, 1); postInputConfigDialog->addDouble(tr("Epaisseur des tranches"), "m", 0, std::numeric_limits::max(), 2, _thickness, 1); postInputConfigDialog->addTitle(tr("N.B. : si l'espacement est inférieur à l'épaisseur, il y aura recouvrement (fenêtre glissante)")); } } void ONF_StepComputeCrownProjection::compute() { // parcours des scènes pour calculer z min et max QList groups; for (CT_StandardItemGroup* grp : _inGroup.iterateOutputs(_inResult)) { for (const CT_AbstractItemDrawableWithPointCloud* scene : grp->singularItems(_inScene)) { if (isStopped()) {return;} Eigen::Vector3d min, max; scene->boundingBox(min, max); if (min(2) < _zmin) {_zmin = min(2);} if (max(2) > _zmax) {_zmax = max(2);} groups.append(grp); } } QListIterator itGrp(groups); while (itGrp.hasNext() && !isStopped()) { if (isStopped()) {return;} computeConvexHullForOneSceneGroup(itGrp.next()); } } void ONF_StepComputeCrownProjection::computeConvexHullForOneSceneGroup(CT_StandardItemGroup* group) const { const CT_AbstractItemDrawableWithPointCloud* scene = group->singularItem(_inScene); if (scene != nullptr) { // création des niveaux QList allPoints; QList pointsByLevel; for (double z = _zmin ; z < _zmax ; z += _spacing) { pointsByLevel.append(ONF_StepComputeCrownProjection::level(z - _thickness, z + _thickness, z)); } QMap zValues; // création de la liste complète des points CT_PointIterator itP(scene->pointCloudIndex()); while(itP.hasNext() && (!isStopped())) { const CT_Point &point = itP.next().currentPoint(); Eigen::Vector2d *point2D = new Eigen::Vector2d(point(0), point(1)); allPoints.append(point2D); zValues.insert(point2D, point(2)); } // tri par (X,Y) de la liste des points CT_Polygon2DData::orderPointsByXY(allPoints); if (_computeSlices) { // Création de la liste des points pour chaque tranche QListIterator itPoints(allPoints); while (itPoints.hasNext()) { Eigen::Vector2d *point = itPoints.next(); double z = zValues.value(point); QListIterator it(pointsByLevel); while (it.hasNext()) { ONF_StepComputeCrownProjection::level &levelInfo = const_cast(it.next()); if (z >= levelInfo._zmin && z < levelInfo._zmax) { levelInfo._pointList.append(point); } } } } CT_Polygon2DData *data = CT_Polygon2DData::createConvexHull(allPoints); if (data != nullptr) { CT_Polygon2D* convexHull = new CT_Polygon2D(data); group->addSingularItem(_outconvexHull, convexHull); // Calcul des angles limites QMap, double> dirMax; double angle = 2*M_PI / double(_nbDir); dirMax.insert (QPair(0, angle / 2.0), 0); double lastAngle = angle / 2.0; for (int i = 1 ; i < _nbDir ; i++) { double newAngle = lastAngle + angle; dirMax.insert (QPair(lastAngle, newAngle), 0); lastAngle = newAngle; } dirMax.insert (QPair(lastAngle, 2.0 * M_PI + 0.1), 0); if (_computeSlices) { QListIterator itPtBLev(pointsByLevel); while (itPtBLev.hasNext()) { ONF_StepComputeCrownProjection::level ¤tLevel = const_cast(itPtBLev.next()); CT_Polygon2DData *dataSlice = CT_Polygon2DData::createConvexHull(currentLevel._pointList); if (dataSlice != nullptr) { CT_StandardItemGroup* grpSlice= new CT_StandardItemGroup(); group->addGroup(_outgrpSlice, grpSlice); CT_Polygon2D* slice = new CT_Polygon2D(dataSlice); slice->setZValue(currentLevel._zlevel); grpSlice->addSingularItem(_outscliceCvx, slice); if (_computeDirs) { // init Distances QMutableMapIterator, double> itAng(dirMax); while (itAng.hasNext()) { itAng.next(); itAng.setValue(0); } // Calcul de l'enveloppe directionnelle const Eigen::Vector2d &massCenter = dataSlice->getCenter(); QListIterator itPts(currentLevel._pointList); while (itPts.hasNext()) { Eigen::Vector2d* pt = itPts.next(); Eigen::Vector2d dir = (*pt) - massCenter; double distance = dir.norm(); double asinx = asin(dir(0) / distance); double acosy = acos(dir(1) / distance); double azimut; if (asinx >= 0) { azimut = acosy; } else { azimut = 2*M_PI - acosy; } itAng.toFront(); while (itAng.hasNext()) { itAng.next(); const QPair &pair = itAng.key(); if ((azimut >= pair.first) && (azimut < pair.second) && (distance > itAng.value())) {itAng.setValue(distance);} } } double lastDistance = itAng.value(); itAng.remove(); itAng.toFront(); if (itAng.value() < lastDistance) {itAng.setValue(lastDistance);} QVector vertices; itAng.toFront(); while (itAng.hasNext()) { itAng.next(); } } } } } } qDeleteAll(allPoints); } }