Skip to content
Snippets Groups Projects
Forked from ogs / ogs
11442 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
MeshView.cpp 13.10 KiB
/**
 * \file
 * \author Lars Bilke
 * \date   2009-09-24
 * \brief  Implementation of the MeshView class.
 *
 * \copyright
 * Copyright (c) 2012-2020, OpenGeoSys Community (http://www.opengeosys.org)
 *            Distributed under a Modified BSD License.
 *              See accompanying file LICENSE.txt or
 *              http://www.opengeosys.org/project/license
 *
 */

#include "MeshView.h"

#include <memory>

#include <QHeaderView>
#include <QContextMenuEvent>
#include <QFileDialog>
#include <QMenu>
#include <QObject>
#include <QSettings>

#include "SHPInterface.h"
#include "TetGenInterface.h"
#include "Applications/FileIO/AsciiRasterInterface.h"

#include "MeshLib/Mesh.h"
#include "MeshLib/Node.h"
#include "MeshLib/MeshSurfaceExtraction.h"
#include "MeshLib/MeshEditing/AddLayerToMesh.h"
#include "MeshLib/MeshEditing/RasterDataToMesh.h"

#include "OGSError.h"
#include "MeshMapping2DDialog.h"
#include "RasterDataToMeshDialog.h"
#include "MeshLayerEditDialog.h"
#include "MeshValueEditDialog.h"
#include "SurfaceExtractionDialog.h"
#include "AddLayerToMeshDialog.h"
#include "MeshItem.h"
#include "MeshModel.h"

#include "ImportFileTypes.h"
#include "LastSavedFileDirectory.h"
#include "SaveMeshDialog.h"


MeshView::MeshView( QWidget* parent /*= 0*/ )
    : QTreeView(parent)
{
    setUniformRowHeights(true);
    //resizeColumnsToContents();
    //resizeRowsToContents();
}

MeshView::~MeshView() = default;

void MeshView::updateView()
{
    setAlternatingRowColors(true);
    setColumnWidth(0,150);
    std::size_t nColumns = (this->model() != nullptr) ? this->model()->columnCount() : 0;
    for (std::size_t i = 1; i < nColumns; i++)
    {
        resizeColumnToContents(i);
    }
}

void MeshView::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
{
    Q_UNUSED(deselected);
    if (!selected.isEmpty())
    {
        emit removeSelectedMeshComponent();
        const QModelIndex idx = *(selected.indexes().begin());
        const TreeItem* tree_item = static_cast<TreeModel*>(this->model())->getItem(idx);

        const auto* list_item = dynamic_cast<const MeshItem*>(tree_item);
        if (list_item)
        {
            emit enableSaveButton(true);
            emit enableRemoveButton(true);
            emit meshSelected(*list_item->getMesh());
        }
        else
        {
            emit enableSaveButton(false);
            emit enableRemoveButton(false);
            emit elementSelected(dynamic_cast<const MeshItem*>(tree_item->parentItem())->vtkSource(), static_cast<unsigned>(tree_item->row()), true);
        }
    }
}

void MeshView::addMesh()
{
    emit openMeshFile(ImportFileType::OGS_MSH);
}

void MeshView::removeMesh()
{
    QModelIndex index (this->selectionModel()->currentIndex());
    if (!index.isValid())
    {
        OGSError::box("No mesh selected.");
    }
    else
    {
        emit requestMeshRemoval(index);
        emit enableSaveButton(false);
        emit enableRemoveButton(false);
    }
}

void MeshView::contextMenuEvent( QContextMenuEvent* event )
{
    QModelIndex const& index = this->selectionModel()->currentIndex();
    MeshItem const*const item = dynamic_cast<MeshItem*>(static_cast<TreeItem*>(index.internalPointer()));

    if (item == nullptr)
    {
        return;
    }

    unsigned const mesh_dim (item->getMesh()->getDimension());

    std::vector<MeshAction> actions;
    actions.push_back({new QAction("Map mesh...", this), 1, 2});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(openMap2dMeshDialog()));
    actions.push_back({new QAction("Assign raster data to mesh...", this), 1, 2});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(openRasterDataToMeshDialog()));
    actions.push_back({new QAction("Edit mesh...", this), 2, 3});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(openMeshEditDialog()));
    actions.push_back({new QAction("Add layer...", this), 1, 3});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(openAddLayerDialog()));
    actions.push_back({new QAction("Edit material groups...", this), 1, 3});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(openValuesEditDialog()));
    actions.push_back({new QAction("Extract surface...", this), 3, 3});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(extractSurfaceMesh()));
    actions.push_back({new QAction("Calculate element quality...", this), 2, 3});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(checkMeshQuality()));
    actions.push_back({new QAction("Convert to geometry", this), 1, 2});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(convertMeshToGeometry()));
    actions.push_back({new QAction("Export to Shapefile...", this), 2, 2});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(exportToShapefile()));
    actions.push_back({new QAction("Export to TetGen...", this), 3, 3});
    connect(actions.back().action, SIGNAL(triggered()), this, SLOT(exportToTetGen()));

    QMenu menu(this);
    for (MeshAction a : actions)
    {
        if (mesh_dim >= a.min_dim && mesh_dim <= a.max_dim)
        {
            menu.addAction(a.action);
        }
    }
    menu.exec(event->globalPos());
}

void MeshView::openMap2dMeshDialog()
{
    MeshModel const*const model = static_cast<MeshModel*>(this->model());
    QModelIndex const index = this->selectionModel()->currentIndex();
    MeshLib::Mesh const*const mesh = model->getMesh(index);
    if (mesh == nullptr)
    {
        return;
    }

    MeshMapping2DDialog dlg;
    if (dlg.exec() != QDialog::Accepted)
    {
        return;
    }

    auto result = std::make_unique<MeshLib::Mesh>(*mesh);
    result->setName(dlg.getNewMeshName());
    if (dlg.useRasterMapping())
    {
        std::unique_ptr<GeoLib::Raster> raster{
            FileIO::AsciiRasterInterface::readRaster(dlg.getRasterPath())};
        if (!raster)
        {
            OGSError::box(QString::fromStdString(
                "Error mapping mesh. Could not read raster file " +
                dlg.getRasterPath()));
            return;
        }
        if (!MeshLib::MeshLayerMapper::layerMapping(*result, *raster,
                                                    dlg.getNoDataReplacement(),
                                                    dlg.getIgnoreNoData()))
        {
            OGSError::box("Error mapping mesh.");
            return;
        }
    }
    else
    {
        MeshLib::MeshLayerMapper::mapToStaticValue(*result,
                                                   dlg.getStaticValue());
    }
    static_cast<MeshModel*>(this->model())->addMesh(std::move(result));

}

void MeshView::openRasterDataToMeshDialog()
{
    MeshModel const* const model = static_cast<MeshModel*>(this->model());
    QModelIndex const index = this->selectionModel()->currentIndex();
    MeshLib::Mesh const* mesh = model->getMesh(index);
    if (mesh == nullptr)
    {
        return;
    }

    RasterDataToMeshDialog dlg(mesh->getName());
    if (dlg.exec() != QDialog::Accepted)
    {
        return;
    }

    auto result = std::make_unique<MeshLib::Mesh>(*mesh);
    result->setName(dlg.getMeshName());
    std::unique_ptr<GeoLib::Raster> raster{
        FileIO::AsciiRasterInterface::readRaster(dlg.getRasterPath())};
    if (!raster)
    {
        OGSError::box(QString::fromStdString("Could not read raster file " +
                                             dlg.getRasterPath()));
        return;
    }
    if (dlg.createNodeArray())
    {
        MeshLib::RasterDataToMesh::projectToNodes(
            *result, *raster, dlg.getNoDataReplacement(), dlg.getArrayName());
    }
    else
    {
        MeshLib::RasterDataToMesh::projectToElements(
            *result, *raster, dlg.getNoDataReplacement(), dlg.getArrayName());
    }
    static_cast<MeshModel*>(this->model())->addMesh(std::move(result));
}

void MeshView::openMeshEditDialog()
{
    MeshModel const*const model = static_cast<MeshModel*>(this->model());
    QModelIndex const index = this->selectionModel()->currentIndex();
    MeshLib::Mesh const*const mesh = model->getMesh(index);

    MeshLayerEditDialog meshLayerEdit(mesh);
    connect(&meshLayerEdit, SIGNAL(mshEditFinished(MeshLib::Mesh*)),
            model, SLOT(addMesh(MeshLib::Mesh*)));
    meshLayerEdit.exec();
}

void MeshView::openValuesEditDialog()
{
    MeshModel const*const model = static_cast<MeshModel*>(this->model());
    QModelIndex const index = this->selectionModel()->currentIndex();
    auto* mesh = const_cast<MeshLib::Mesh*>(model->getMesh(index));

    MeshValueEditDialog valueEdit(mesh);
    connect(&valueEdit, SIGNAL(valueEditFinished(MeshLib::Mesh*)),
            model, SLOT(updateMesh(MeshLib::Mesh*)));
    valueEdit.exec();
}

void MeshView::openAddLayerDialog()
{
    QModelIndex const index = this->selectionModel()->currentIndex();
    if (!index.isValid())
    {
        return;
    }

    MeshLib::Mesh const*const mesh = static_cast<MeshModel*>(this->model())->getMesh(index);
    if (mesh == nullptr)
    {
        return;
    }

    AddLayerToMeshDialog dlg;
    if (dlg.exec() != QDialog::Accepted)
    {
        return;
    }

    bool const copy_material_ids = false;
    double const thickness(dlg.getThickness());
    std::unique_ptr<MeshLib::Mesh> result(
        MeshLib::addLayerToMesh(*mesh, thickness, dlg.getName(),
                                dlg.isTopLayer(), copy_material_ids));

    if (result)
    {
        static_cast<MeshModel*>(model())->addMesh(std::move(result));
    }
    else
    {
        OGSError::box("Error adding layer to mesh.");
    }
}

void MeshView::extractSurfaceMesh()
{
    QModelIndex const index = this->selectionModel()->currentIndex();
    if (!index.isValid())
    {
        return;
    }

    MeshLib::Mesh const*const mesh = static_cast<MeshModel*>(this->model())->getMesh(index);
    SurfaceExtractionDialog dlg;
    if (dlg.exec() != QDialog::Accepted)
    {
        return;
    }

    MathLib::Vector3 const& dir (dlg.getNormal());
    int const tolerance (dlg.getTolerance());
    std::unique_ptr<MeshLib::Mesh> sfc_mesh(
        MeshLib::MeshSurfaceExtraction::getMeshSurface(*mesh, dir, tolerance));
    if (sfc_mesh)
    {
        static_cast<MeshModel*>(model())->addMesh(std::move(sfc_mesh));
    }
    else
    {
        OGSError::box(
            " No surfaces found to extract\n using the specified parameters.");
    }
}

void MeshView::convertMeshToGeometry()
{
    QModelIndex const index = this->selectionModel()->currentIndex();
    MeshLib::Mesh const*const mesh = static_cast<MeshModel*>(this->model())->getMesh(index);
    emit requestMeshToGeometryConversion(mesh);
}

void MeshView::exportToShapefile() const
{
    QModelIndex const index = this->selectionModel()->currentIndex();
    if (!index.isValid())
    {
        return;
    }

    QSettings const settings;
    QFileInfo const fi (settings.value("lastOpenedMeshFileDirectory").toString());
    MeshLib::Mesh const*const mesh = static_cast<MeshModel*>(this->model())->getMesh(index);
    QString const fileName = QFileDialog::getSaveFileName(nullptr, "Convert mesh to shapefile...",
        LastSavedFileDirectory::getDir() + QString::fromStdString(mesh->getName()),
        "ESRI Shapefile (*.shp)");
    if (!fileName.isEmpty())
    {
        LastSavedFileDirectory::setDir(fileName);
        if (!FileIO::SHPInterface::write2dMeshToSHP(fileName.toStdString(),
                                                    *mesh))
        {
            OGSError::box("Error exporting mesh\n to shapefile");
        }
    }
}

void MeshView::exportToTetGen()
{
    QModelIndex const index = this->selectionModel()->currentIndex();
    if (!index.isValid())
    {
        return;
    }

    MeshLib::Mesh const*const mesh = static_cast<MeshModel*>(this->model())->getMesh(index);
    QSettings const settings;
    QString const filename = QFileDialog::getSaveFileName(this, "Write TetGen input file to",
        settings.value("lastOpenedTetgenFileDirectory").toString(),
        "TetGen Geometry (*.smesh)");
    if (!filename.isEmpty())
    {
        FileIO::TetGenInterface tg;
        std::vector<MeshLib::Node> attr;
        tg.writeTetGenSmesh(filename.toStdString(), *mesh, attr);
    }
}

void MeshView::writeToFile() const
{
    QModelIndex const index = this->selectionModel()->currentIndex();
    if (!index.isValid())
    {
        OGSError::box("No mesh selected.");
        return;
    }

    MeshLib::Mesh const*const mesh = static_cast<MeshModel*>(this->model())->getMesh(index);
    if (mesh == nullptr)
    {
        OGSError::box("No mesh selected.");
        return;
    }

    SaveMeshDialog dlg(*mesh);
    dlg.exec();
}

void MeshView::addDIRECTSourceTerms()
{
    //QModelIndex const index = this->selectionModel()->currentIndex();
    //MeshLib::Mesh const*const grid = static_cast<MeshModel*>(this->model())->getMesh(index);
}

void MeshView::loadDIRECTSourceTerms()
{
    QModelIndex const index = this->selectionModel()->currentIndex();
    emit loadFEMCondFileRequested(index.data(0).toString().toStdString());
}

void MeshView::checkMeshQuality ()
{
    QModelIndex const index = this->selectionModel()->currentIndex();
    MeshItem const*const item = static_cast<MeshItem*>(static_cast<MeshModel*>(this->model())->getItem(index));
    emit qualityCheckRequested(item->vtkSource());
}