/**************************************************************************** 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 "actions/onf_actionaffiliatepointalignementsandfieldinventory.h" #include "ct_global/ct_context.h" #include "ct_iterator/ct_pointiterator.h" #include "ct_color.h" #include "ct_math/ct_mathpoint.h" #include "ct_pointcloudindex/ct_pointcloudindexvector.h" #include "ct_itemdrawable/ct_scene.h" #include #include #include #include #include #include #include #include "math.h" ONF_ActionAffiliatePointAlignementsAndFieldInventory_dataContainer::ONF_ActionAffiliatePointAlignementsAndFieldInventory_dataContainer() { _drawManager = NULL; } ONF_ActionAffiliatePointAlignementsAndFieldInventory::ONF_ActionAffiliatePointAlignementsAndFieldInventory(ONF_ActionAffiliatePointAlignementsAndFieldInventory_dataContainer *dataContainer) : CT_AbstractActionForGraphicsView() { _dataContainer = dataContainer; _selectedPos = NULL; _previousSelectedPos = NULL; _selectOption = NULL; _selectTool = NULL; _pointMode = false; _automaticColorList.append(QColor(255, 0, 255)); // Rose _automaticColorList.append(QColor(255,200,255)); // Magenta Clair _automaticColorList.append(QColor(200,255,255)); // Cyan Clair _automaticColorList.append(QColor(200,200,255)); // Mauve Clair _automaticColorList.append(QColor(255,200,200)); // Rouge Clair _automaticColorList.append(QColor(255,200,150)); // Orange clair _automaticColorList.append(QColor(150,200,255)); // Bleu Clair _automaticColorList.append(QColor(200,255,150)); // Vert-Jaune Clair _automaticColorList.append(QColor(150,255,200)); // Turquoise Clair _automaticColorList.append(QColor(255,150,200)); // Rose Clair _automaticColorList.append(QColor(200,150,255)); // Violet Clair _automaticColorList.append(QColor(0 ,0 ,255)); // Bleu _automaticColorList.append(QColor(100,100,255)); // Mauve _automaticColorList.append(QColor(255,150,0 )); // Orange _automaticColorList.append(QColor(0 ,255,150)); // Turquoise _automaticColorList.append(QColor(150,0 ,255)); // Violet _unmatchedColor = QColor(200,200,200); // Light Grey _selectedColor = QColor(255,255,0 ); _validatedColor = QColor(0 ,150,0 ); _validatedAndSelectedColor = QColor(0 ,255,0 ); } ONF_ActionAffiliatePointAlignementsAndFieldInventory::~ONF_ActionAffiliatePointAlignementsAndFieldInventory() { document()->removeAllItemDrawable(); if (_selectOption != NULL) { ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions *option = (ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions*)optionAt(0); if (option != NULL) { option->clearSelectionWidget(_selectOption); } delete _selectOption; } if (_selectTool != NULL) {delete _selectTool;} } QString ONF_ActionAffiliatePointAlignementsAndFieldInventory::uniqueName() const { return "ONF_ActionAffiliatePointAlignementsAndFieldInventory"; } QString ONF_ActionAffiliatePointAlignementsAndFieldInventory::title() const { return "Select scene for position"; } QString ONF_ActionAffiliatePointAlignementsAndFieldInventory::description() const { return "Select scene for position"; } QIcon ONF_ActionAffiliatePointAlignementsAndFieldInventory::icon() const { return QIcon(":/icons/select_rectangular.png"); } QString ONF_ActionAffiliatePointAlignementsAndFieldInventory::type() const { return CT_AbstractAction::TYPE_MODIFICATION; } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::init() { CT_AbstractActionForGraphicsView::init(); if(nOptions() == 0) { // create the option widget if it was not already created ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions *option = new ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions(this); // add the options to the graphics view graphicsView()->addActionOptions(option); connect(option, SIGNAL(parametersChanged()), this, SLOT(redrawOverlayAnd3D())); connect(option, SIGNAL(addCluster()), this, SLOT(addCluster())); connect(option, SIGNAL(removeCluster()), this, SLOT(removeCluster())); connect(option, SIGNAL(initColors()), this, SLOT(initColors())); connect(option, SIGNAL(validate()), this, SLOT(validatePosition())); connect(option, SIGNAL(rmqModified()), this, SLOT(modifyRmq())); // register the option to the superclass, so the hideOptions and showOptions // is managed automatically registerOption(option); _selectTool = new CT_ActionSelectTool(); _selectOption = new CT_ActionSelectItemDrawableGVOptions(NULL, _selectTool); _selectOption->setAvailableModes(false, true, false, false); _selectOption->setAvailableSelectionModes(true, true, true, true, true, true); _selectTool->init(graphicsView(), _selectOption); connect(_selectTool, SIGNAL(selectionModeChanged(GraphicsViewInterface::SelectionMode)), _selectOption, SLOT(setSelectionMode(GraphicsViewInterface::SelectionMode))); connect(_selectTool, SIGNAL(needRedrawOverlay()), this, SLOT(redrawOverlay())); option->setSelectionWidget(_selectOption); document()->removeAllItemDrawable(); GraphicsViewInterface* view = dynamic_cast(document()->views().first()); view->setSelectionMode(GraphicsViewInterface::SELECT_ONE); _dataContainer->_drawManager->setDataContainer(_dataContainer, false); _dataContainer->_newClusterDrawManager->setDataContainer(_dataContainer, true); _dataContainer->_sceneVisible = false; for (int i = 0 ; i < _dataContainer->_alignements.size() ; i++) { CT_AbstractItemDrawableWithPointCloud* alignement = (CT_AbstractItemDrawableWithPointCloud*) _dataContainer->_alignements.at(i); document()->addItemDrawable(*alignement); } _colorNum = 0; initColors(); view->camera()->fitCameraToVisibleItems(); view->camera()->setOrientation(0.2, 0, 0, 0.95); } } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::initColors() { _colorNum = std::rand() % _automaticColorList.size(); for (int i = 0 ; i < _dataContainer->_positions.size() ; i++) { ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* treePos = _dataContainer->_positions.at(i); treePos->_color = nextColor(); } redrawOverlayAnd3D(true); } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::validatePosition() { ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions *option = (ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions*)optionAt(0); if (_selectedPos != NULL) { _selectedPos->_validated = !_selectedPos->_validated; if (_selectedPos->_validated) {_selectedPos = NULL; option->setRmq("", false);} } redrawOverlayAnd3D(); } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::modifyRmq() { ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions *option = (ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions*)optionAt(0); if (_selectedPos != NULL) { _selectedPos->_rmq = option->rmq(); } } QColor ONF_ActionAffiliatePointAlignementsAndFieldInventory::nextColor() { int num = _colorNum++; if (_colorNum >= _automaticColorList.size()) {_colorNum = 0;} return _automaticColorList.at(num); } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::colorizeAlignements() { for (int i = 0 ; i < _dataContainer->_alignements.size() ; i++) { CT_AbstractItemDrawableWithPointCloud* alignement = (CT_AbstractItemDrawableWithPointCloud*) _dataContainer->_alignements.at(i); if (!_dataContainer->_matched[i]) { document()->setColor(alignement, _unmatchedColor); } } for (int i = 0 ; i < _dataContainer->_positions.size() ; i++) { ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* treePos = _dataContainer->_positions.at(i); QColor color = treePos->_color; if (treePos->_validated) { if (treePos == _selectedPos) { color = _validatedAndSelectedColor; } else { color = _validatedColor; } } else if (treePos == _selectedPos) { color = _selectedColor; } for (int j = 0 ; j < treePos->_alignementsIds.size() ; j++) { int alignementId = treePos->_alignementsIds.at(j); CT_AbstractItemDrawableWithPointCloud* alignement = (CT_AbstractItemDrawableWithPointCloud*) _dataContainer->_alignements.at(alignementId); if (_dataContainer->_matched[alignementId]) { document()->setColor(alignement, color); } else { document()->setColor(alignement, _unmatchedColor); } } } } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::addCluster() { if (_pointMode) { CT_CIR pcir = graphicsView()->getSelectedPoints(); CT_PointIterator it(pcir); if (it.hasNext() && _selectedPos != NULL) { while (it.hasNext()) { it.next(); size_t indexPt = it.currentGlobalIndex(); CT_PointCluster* clust = _dataContainer->_addedAlignements.value(indexPt, NULL); if (clust != NULL) // point déjà ajouté à un nouveau cluster { int index = _dataContainer->_alignements.indexOf(clust); if (!_selectedPos->_alignementsIds.contains(index)) { for (int i = 0 ; i < _dataContainer->_positions.size() ; i++) { ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* treePos = _dataContainer->_positions.at(i); if (treePos != _selectedPos && treePos->_alignementsIds.contains(index)) { treePos->_alignementsIds.removeOne(index); } } _selectedPos->_alignementsIds.append(index); _dataContainer->_matched[index] = true; } document()->setSelectAllItemDrawable(false); } else { if (!_dataContainer->_droppedPointsIds.contains(indexPt)) { _dataContainer->_droppedPointsIds.append(indexPt); CT_PointCluster* newCluster = new CT_PointCluster(NULL, _dataContainer->_result); newCluster->setAlternativeDrawManager(_dataContainer->_newClusterDrawManager); newCluster->addPoint(indexPt); _selectedPos->_alignementsIds.append(_dataContainer->_alignements.size()); _dataContainer->_alignements.append(newCluster); _dataContainer->_addedAlignements.insert(indexPt, newCluster); _dataContainer->_matched.push_back(true); _dataContainer->_newCluster.push_back(true); } } } graphicsView()->setAllPointsSelected(false); } else { PS_LOG->addMessage(LogInterface::error, LogInterface::action, "Ajout impossible : aucune position séléctionnée.\nLe point ne peut être affecté."); } } else { QList items = document()->getSelectedItemDrawable(); if (items.size() > 0 && _selectedPos != NULL) { int index = _dataContainer->_alignements.indexOf(items.first()); if (index >= 0 && !_selectedPos->_alignementsIds.contains(index)) { for (int i = 0 ; i < _dataContainer->_positions.size() ; i++) { ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* treePos = _dataContainer->_positions.at(i); if (treePos != _selectedPos && treePos->_alignementsIds.contains(index)) { treePos->_alignementsIds.removeOne(index); } } _selectedPos->_alignementsIds.append(index); _dataContainer->_matched[index] = true; } document()->setSelectAllItemDrawable(false); } else { PS_LOG->addMessage(LogInterface::error, LogInterface::action, "Ajout impossible : aucune position séléctionnée.\nL'alignement ne peut être affecté."); } } redrawOverlayAnd3D(true); } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::removeCluster() { if (_pointMode) { CT_CIR pcir = graphicsView()->getSelectedPoints(); CT_PointIterator it(pcir); if (it.hasNext()) { while (it.hasNext()) { it.next(); size_t index = it.currentGlobalIndex(); CT_PointCluster* clust = _dataContainer->_addedAlignements.value(index, NULL); if (clust != NULL) // point déjà ajouté à un nouveau cluster { int alignementIndex = _dataContainer->_alignements.indexOf(clust); if (alignementIndex >= 0) { _dataContainer->_matched[alignementIndex] = false; } } else { if (!_dataContainer->_droppedPointsIds.contains(index)) { _dataContainer->_droppedPointsIds.append(index); CT_PointCluster* newCluster = new CT_PointCluster(NULL, _dataContainer->_result); newCluster->setAlternativeDrawManager(_dataContainer->_newClusterDrawManager); newCluster->addPoint(index); _dataContainer->_alignements.append(newCluster); _dataContainer->_addedAlignements.insert(index, newCluster); _dataContainer->_matched.push_back(false); _dataContainer->_newCluster.push_back(true); } } } graphicsView()->setAllPointsSelected(false); } } else { QList items = document()->getSelectedItemDrawable(); if (items.size() > 0 && _selectedPos != NULL) { int index = _dataContainer->_alignements.indexOf(items.first()); if (_selectedPos->_alignementsIds.contains(index)) { _selectedPos->_alignementsIds.removeOne(index); _dataContainer->_matched[index] = false; } document()->setSelectAllItemDrawable(false); } else { PS_LOG->addMessage(LogInterface::error, LogInterface::action, "Retrait impossible : aucune position séléctionnée.\nL'alignement ne peut être retiré."); } } redrawOverlayAnd3D(true); } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::redrawOverlay() { document()->redrawGraphics(); } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::redrawOverlayAnd3D() { redrawOverlayAnd3D(false); } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::redrawOverlayAnd3D(bool forceRedraw) { ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions *option = (ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions*)optionAt(0); if (forceRedraw || _dataContainer->_drawManager->lineVisible() != option->lines() || _dataContainer->_sceneVisible != option->scenes() || _dataContainer->_sceneVisible2 != option->scenes2()) { _dataContainer->_drawManager->setLineVisible(option->lines()); _dataContainer->_sceneVisible = option->scenes(); _dataContainer->_sceneVisible2 = option->scenes2(); document()->removeAllItemDrawable(); for (int i = 0 ; i < _dataContainer->_alignements.size() ; i++) { CT_AbstractItemDrawableWithPointCloud* alignement = (CT_AbstractItemDrawableWithPointCloud*) _dataContainer->_alignements.at(i); document()->addItemDrawable(*alignement); } QMapIterator itNewClust(_dataContainer->_addedAlignements); while (itNewClust.hasNext()) { itNewClust.next(); document()->addItemDrawable(*(itNewClust.value())); } if (option->scenes()) { for (int i = 0 ; i < _dataContainer->_scenes.size() ; i++) { document()->addItemDrawable(*(_dataContainer->_scenes.at(i))); } } if (option->scenes2()) { for (int i = 0 ; i < _dataContainer->_scenes2.size() ; i++) { document()->addItemDrawable(*(_dataContainer->_scenes2.at(i))); } } } if (_pointMode != option->pointMode()) { _pointMode = option->pointMode(); GraphicsViewInterface* view = dynamic_cast(document()->views().first()); if (_pointMode) { document()->setSelectAllItemDrawable(false); view->setSelectionMode(GraphicsViewInterface::SELECT_ONE_POINT); } else { graphicsView()->setAllPointsSelected(false); view->setSelectionMode(GraphicsViewInterface::SELECT_ONE); } } colorizeAlignements(); setDrawing3DChanged(); document()->redrawGraphics(DocumentInterface::RO_WaitForConversionCompleted); } bool ONF_ActionAffiliatePointAlignementsAndFieldInventory::mousePressEvent(QMouseEvent *e) { _lastPos = e->pos(); _buttonsPressed = e->buttons(); return false; } bool ONF_ActionAffiliatePointAlignementsAndFieldInventory::mouseMoveEvent(QMouseEvent *e) { return false; } bool ONF_ActionAffiliatePointAlignementsAndFieldInventory::mouseReleaseEvent(QMouseEvent *e) { ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions *option = (ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions*)optionAt(0); GraphicsViewInterface *view = graphicsView(); int tolerance = option->tolerance(); QPoint dir = e->pos() - _lastPos; if (dir.manhattanLength() < 3) { if (_buttonsPressed == Qt::RightButton) { Eigen::Vector3d origin, direction; view->convertClickToLine(e->pos(), origin, direction); _selectedPos = getPositionFromDirection(origin, direction); if (_selectedPos != NULL) { option->setRmq(_selectedPos->_rmq, true); graphicsView()->camera()->setCX(_selectedPos->_base(0)); graphicsView()->camera()->setCY(_selectedPos->_base(1)); graphicsView()->camera()->setCZ(_selectedPos->_base(2)); } else { option->setRmq("", false); } redrawOverlayAnd3D(); } else if (_buttonsPressed == Qt::LeftButton) { #ifdef COMPUTREE_V5 QRect r(0, 0, tolerance, tolerance); r.moveCenter(e->pos()); view->select(r); #else view->setSelectRegionWidth(tolerance); view->setSelectRegionHeight(tolerance); view->select(e->pos()); #endif redrawOverlayAnd3D(); return false; } } return false; } bool ONF_ActionAffiliatePointAlignementsAndFieldInventory::wheelEvent(QWheelEvent *e) { return false; } bool ONF_ActionAffiliatePointAlignementsAndFieldInventory::keyPressEvent(QKeyEvent *e) { if((e->key() == Qt::Key_A) && !e->isAutoRepeat()) { addCluster(); } if((e->key() == Qt::Key_Z) && !e->isAutoRepeat()) { removeCluster(); } if((e->key() == Qt::Key_E) && !e->isAutoRepeat()) { validatePosition(); } return false; } bool ONF_ActionAffiliatePointAlignementsAndFieldInventory::keyReleaseEvent(QKeyEvent *e) { return false; } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::draw(GraphicsViewInterface &view, PainterInterface &painter) { for (int i = 0 ; i < _dataContainer->_positions.size() ; i++) { ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* treePos = _dataContainer->_positions.at(i); drawCylinder(view, painter, treePos); } } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::drawOverlay(GraphicsViewInterface &view, QPainter &painter) { painter.save(); int add = painter.fontMetrics().height()+2; int y = add; QString txt; bool showLog = _previousSelectedPos != _selectedPos; _previousSelectedPos = _selectedPos; QString logMessage = ""; if (showLog && _selectedPos == NULL) { logMessage.append("-------------------------------------
"); logMessage.append(tr("No selected Tree
")); PS_LOG->addMessage(LogInterface::info, LogInterface::action, logMessage); } int nbPosValidated = 0; for (int i = 0 ; i < _dataContainer->_positions.size() ; i++) { ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* treePos = _dataContainer->_positions.at(i); if (treePos->_validated) {nbPosValidated++;} } txt = QString("Validated: %1 / %2").arg(nbPosValidated).arg(_dataContainer->_positions.size()); painter.drawText(2, y, txt); y += add; y += add; if (_selectedPos != NULL) { if (showLog) { logMessage.append("-------------------------------------
"); logMessage.append("Selected Tree informations:
"); } painter.setPen(QColor(255,255,255)); txt = QString("Plot: %1 ; Tree: %2").arg(_selectedPos->_idPlot).arg(_selectedPos->_idTree); painter.drawText(2, y, txt); y += add; if (showLog) {logMessage.append(QString("%1
").arg(txt));} txt = QString("DBH: %1 cm").arg(_selectedPos->_dbh); painter.drawText(2, y, txt); y += add; if (showLog) {logMessage.append(QString("%1
").arg(txt));} txt = QString("H: %1 m").arg(_selectedPos->_height); painter.drawText(2, y, txt); y += add; if (showLog) {logMessage.append(QString("%1
").arg(txt));} txt = QString("Species: %1").arg(_selectedPos->_species); painter.drawText(2, y, txt); y += add; if (showLog) {logMessage.append(QString("%1
").arg(txt));} Eigen::Vector3d bary(0,0,0); int cptBary = 0; for (int i = 0 ; i < _selectedPos->_alignementsIds.size() ; i++) { int id = _selectedPos->_alignementsIds.at(i); if (id > 0) { CT_PointCluster* alignement = (CT_PointCluster*) _dataContainer->_alignements.at(id); bary(0) += alignement->getCenterX(); bary(1) += alignement->getCenterY(); cptBary++; } } double dist = 0; if (cptBary > 0) { bary(0) /= cptBary; bary(1) /= cptBary; dist = sqrt(pow(_selectedPos->_base(0) - bary(0), 2) + pow(_selectedPos->_base(1) - bary(1), 2)); } txt = QString("XY Distance: %1 m").arg(dist); painter.drawText(2, y, txt); y += add; if (showLog) {logMessage.append(QString("%1
").arg(txt));} txt = QString("NB. Alignements: %1").arg(cptBary); painter.drawText(2, y, txt); y += add; if (showLog) {logMessage.append(QString("%1
").arg(txt));} if (showLog) { PS_LOG->addMessage(LogInterface::info, LogInterface::action, logMessage); } } painter.restore(); } CT_AbstractAction* ONF_ActionAffiliatePointAlignementsAndFieldInventory:: copy() const { return new ONF_ActionAffiliatePointAlignementsAndFieldInventory(_dataContainer); } ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* ONF_ActionAffiliatePointAlignementsAndFieldInventory::getPositionFromDirection(Eigen::Vector3d &origin, Eigen::Vector3d &direction) { ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions *option = (ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions*)optionAt(0); double minDist = std::numeric_limits::max(); ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* pos = NULL; for (int i = 0 ; i < _dataContainer->_positions.size() ; i++) { ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* treePos = _dataContainer->_positions.at(i); Eigen::Vector3d treeDirection = Eigen::Vector3d(0, 0, 1); treeDirection.normalize(); double treeHeight = treePos->_height; if (treePos->_height == -1) {treeHeight = option->defaultHeight();} double treeLength = treeHeight; for (double l = 0 ; l < treeLength ; l += 0.1) { Eigen::Vector3d point = treePos->_base + treeDirection*l; double distance = CT_MathPoint::distancePointLine(point, direction, origin); if (distance < minDist) { minDist = distance; pos = treePos; } } } if (minDist > 5.0) {return NULL;} return pos; } bool ONF_ActionAffiliatePointAlignementsAndFieldInventory::getCoordsForMousePosition(QPoint p, double &x, double &y) { Eigen::Vector3d origin, direction; GraphicsViewInterface *view = graphicsView(); view->convertClickToLine(p, origin, direction); if (direction.z() == 0) {return false;} double coef = (0.0 - origin.z())/direction.z(); x = origin.x() + coef*direction.x(); y = origin.y() + coef*direction.y(); return true; } void ONF_ActionAffiliatePointAlignementsAndFieldInventory::drawCylinder(GraphicsViewInterface &view, PainterInterface &painter, ONF_ActionAffiliatePointAlignementsAndFieldInventory_treePosition* treePos) const { Q_UNUSED(view); ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions *option = (ONF_ActionAffiliatePointAlignementsAndFieldInventoryOptions*)optionAt(0); Eigen::Vector3d direction = Eigen::Vector3d(0, 0, 1); direction.normalize(); double treeLength = option->defaultHeight(); double spacing = option->spacing(); double radius = treePos->_dbh / 200.0; QColor color = painter.getColor(); QColor colorCyl = treePos->_color; if (treePos->_validated) { if (treePos == _selectedPos) { colorCyl = _validatedAndSelectedColor; } else { colorCyl = _validatedColor; } } else if (treePos == _selectedPos) { colorCyl = _selectedColor; } painter.setColor(colorCyl); if(option->circles()) { for (double l = 0 ; l <= treeLength ; l += spacing) { Eigen::Vector3d circleCenter = treePos->_base + direction * l; painter.drawCircle3D(circleCenter, direction, radius); } } else { Eigen::Vector3d center = treePos->_base + direction*treeLength/2.0; painter.drawCylinder3D(center, direction, radius, treeLength); } painter.setColor(color); }