Skip to content
Snippets Groups Projects
SampleEditorController.cpp 16.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • //  ************************************************************************************************
    //
    //  BornAgain: simulate and fit reflection and scattering
    //
    
    //! @file      GUI/View/SampleDesigner/SampleEditorController.cpp
    
    //! @brief     Implements class SampleEditorController
    //!
    //! @homepage  http://www.bornagainproject.org
    //! @license   GNU General Public License v3 or higher (see COPYING)
    //! @copyright Forschungszentrum Jülich GmbH 2021
    //! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
    //
    //  ************************************************************************************************
    
    
    #include "GUI/View/SampleDesigner/SampleEditorController.h"
    
    #include "Base/Util/Assert.h"
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include "GUI/Model/CatSample/FormFactorItemCatalog.h"
    #include "GUI/Model/Project/ProjectDocument.h"
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include "GUI/Model/Sample/CompoundItem.h"
    #include "GUI/Model/Sample/CoreAndShellItem.h"
    
    #include "GUI/Model/Sample/InterferenceItems.h"
    #include "GUI/Model/Sample/LayerItem.h"
    
    #include "GUI/Model/Sample/MaterialModel.h"
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include "GUI/Model/Sample/MesocrystalItem.h"
    
    #include "GUI/Model/Sample/ParticleItem.h"
    #include "GUI/Model/Sample/ParticleLayoutItem.h"
    
    #include "GUI/Model/Sample/SampleItem.h"
    
    #include "GUI/View/Numeric/DoubleSpinBox.h"
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include "GUI/View/SampleDesigner/CompoundForm.h"
    #include "GUI/View/SampleDesigner/CoreAndShellForm.h"
    
    #include "GUI/View/SampleDesigner/InterferenceForm.h"
    
    #include "GUI/View/SampleDesigner/LatticeTypeSelectionForm.h"
    
    #include "GUI/View/SampleDesigner/LayerForm.h"
    #include "GUI/View/SampleDesigner/MaterialInplaceForm.h"
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include "GUI/View/SampleDesigner/MesocrystalForm.h"
    
    #include "GUI/View/SampleDesigner/ParticleLayoutForm.h"
    #include "GUI/View/SampleDesigner/SampleEditorCommands.h"
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
    #include "GUI/View/SampleDesigner/SampleForm.h"
    
    #include "Sample/HardParticle/Cylinder.h"
    
    SampleEditorController::SampleEditorController(ProjectDocument* document, SampleItem* multi)
    
        : m_sampleItem(multi)
        , m_sampleForm(nullptr)
    
        , m_document(document)
    
    void SampleEditorController::setSampleForm(SampleForm* view)
    
        m_sampleForm = view;
    
    SampleForm* SampleEditorController::sampleForm() const
    
        return m_sampleForm;
    
    SampleItem* SampleEditorController::sampleItem() const
    
        return m_sampleItem;
    
    void SampleEditorController::addLayerItem(LayerItem* before)
    
        const int newIndex = (before != nullptr) ? m_sampleItem->layerItems().indexOf(before)
                                                 : m_sampleItem->layerItems().size();
    
        m_undoStack.push(new CommandAddLayer(this, newIndex));
    }
    
    
    void SampleEditorController::addLayerItemFromUndo(int atIndex)
    
        // -- find a color for the new layer
        QColor color;
    
        auto unusedColors = LayerEditorUtil::predefinedLayerColors();
    
        for (auto* l : m_sampleItem->layerItems())
    
            unusedColors.removeAll(l->color());
        if (!unusedColors.isEmpty())
            color = unusedColors.first();
        else {
            // search for a color which has been used the less, and which is not the same as in the
            // layers above and below
            QMap<QString, int> usage;
    
            for (auto* l : m_sampleItem->layerItems())
    
                usage[l->color().name()] += 1;
    
    
            auto sortedByUsage = LayerEditorUtil::predefinedLayerColors();
    
            std::stable_sort(
                sortedByUsage.begin(), sortedByUsage.end(),
                [&](const QColor& a, const QColor& b) { return usage[a.name()] < usage[b.name()]; });
    
    
    
            const QColor above =
    
                (atIndex > 0) ? m_sampleItem->layerItems()[atIndex - 1]->color() : QColor();
            const QColor below = (atIndex < m_sampleItem->layerItems().size())
                                     ? m_sampleItem->layerItems()[atIndex]->color()
    
                if (col != above && col != below) {
                    color = col;
                    break;
                }
        }
    
        // - create new layer
    
        LayerItem* layer = m_sampleItem->createLayerItemAt(atIndex);
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        layer->setMaterial(materialModel()->defaultMaterialItem());
    
        layer->setColor(color);
    
        ASSERT(m_sampleForm);
        m_sampleForm->onLayerAdded(layer);
        m_sampleForm->updateUnits();
    
    
        // expand the new layer's form for better workflow
    
        for (auto* c : m_sampleForm->findChildren<LayerForm*>())
    
            if (c->layerItem() == layer)
                c->expand();
    
    void SampleEditorController::addLayoutItem(LayerForm* layerItemWidget)
    
        auto* newLayoutItem = layerItemWidget->layerItem()->addLayoutItem();
    
        layerItemWidget->onLayoutAdded(newLayoutItem);
    
        m_sampleForm->updateUnits();
    
    
        for (auto* layoutForms : layerItemWidget->findChildren<ParticleLayoutForm*>())
            layoutForms->updateTitle(layerItemWidget->layerItem());
    
    void SampleEditorController::removeLayerItem(LayerItem* layerItem)
    
    {
        m_undoStack.push(new CommandRemoveLayer(this, layerItem));
    }
    
    
    void SampleEditorController::removeLayerItemFromUndo(int atIndex)
    
        auto* layer = m_sampleItem->layerItems()[atIndex];
    
        emit aboutToRemoveItem(layer);
    
        m_sampleForm->onAboutToRemoveLayer(layer);
        m_sampleItem->removeLayer(layer);
        m_sampleForm->updateRowVisibilities();
    
    void SampleEditorController::removeLayoutItem(LayerForm* layerItemWidget,
                                                  ParticleLayoutItem* layout)
    
        emit aboutToRemoveItem(layout);
    
        layerItemWidget->onAboutToRemoveLayout(layout);
    
        layerItemWidget->layerItem()->removeLayoutItem(layout);
    
    
        for (auto* layoutForms : layerItemWidget->findChildren<ParticleLayoutForm*>())
            layoutForms->updateTitle(layerItemWidget->layerItem());
    
    void SampleEditorController::addParticleLayoutItem(ParticleLayoutItem* layoutItem,
                                                       FormFactorItemCatalog::Type formFactorType)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newParticle = createAndInitItem(formFactorType);
    
        layoutItem->addItemWithParticleSelection(newParticle);
    
    
        emit modified();
    
        //  search for particle layout widget for notification
    
        ASSERT(m_sampleForm);
        for (auto* w : m_sampleForm->findChildren<ParticleLayoutForm*>())
    
            if (w->layoutItem() == layoutItem)
                w->onParticleAdded(newParticle);
    
        m_sampleForm->updateUnits();
    
    void SampleEditorController::addParticleLayoutItem(ParticleLayoutItem* layoutItem,
                                                       ItemWithParticlesCatalog::Type type)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newItem = createAndInitItem(type);
    
        layoutItem->addItemWithParticleSelection(newItem);
    
        //  search for particle layout widget for notification
    
        ASSERT(m_sampleForm);
        for (auto* w : m_sampleForm->findChildren<ParticleLayoutForm*>())
    
            if (w->layoutItem() == layoutItem)
                w->onParticleAdded(newItem);
    
        m_sampleForm->updateUnits();
    
    void SampleEditorController::addCompoundItem(CompoundItem* compositionItem,
                                                 ItemWithParticlesCatalog::Type type)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newItem = createAndInitItem(type);
    
        compositionItem->addItemWithParticleSelection(newItem);
    
        //  search for composition widget for notification
    
        ASSERT(m_sampleForm);
    
        for (auto* c : m_sampleForm->findChildren<CompoundForm*>())
    
            if (c->compositionItem() == compositionItem)
                c->onParticleAdded(newItem);
    
        m_sampleForm->updateUnits();
    
    void SampleEditorController::addCompoundItem(CompoundItem* compositionItem,
                                                 FormFactorItemCatalog::Type formFactorType)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newParticle = createAndInitItem(formFactorType);
    
        compositionItem->addItemWithParticleSelection(newParticle);
    
    
        emit modified();
    
        //  search for composition widget for notification
    
        ASSERT(m_sampleForm);
    
        for (auto* c : m_sampleForm->findChildren<CompoundForm*>())
    
            if (c->compositionItem() == compositionItem)
                c->onParticleAdded(newParticle);
    
        m_sampleForm->updateUnits();
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
    SampleEditorController::createAndInitItem(FormFactorItemCatalog::Type formFactorType) const
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newParticle = new ParticleItem(materialModel());
    
        newParticle->setFormFactor(FormFactorItemCatalog::create(formFactorType));
    
        newParticle->setMaterial(materialModel()->defaultParticleMaterialItem());
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
    SampleEditorController::createAndInitItem(ItemWithParticlesCatalog::Type itemType) const
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newItem = ItemWithParticlesCatalog::create(itemType, materialModel());
    
        if (auto* p = dynamic_cast<ItemWithMaterial*>(newItem))
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
            p->setMaterial(materialModel()->defaultMaterialItem());
    
        if (auto* cs = dynamic_cast<CoreAndShellItem*>(newItem)) {
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
            cs->createCoreItem(materialModel());
            cs->createShellItem(materialModel());
    
            cs->coreItem()->setFormFactor(new CylinderItem());
            cs->shellItem()->setFormFactor(new CylinderItem());
    
        if (auto* meso = dynamic_cast<MesocrystalItem*>(newItem); meso && meso->basisItem())
            if (auto* p = dynamic_cast<ItemWithMaterial*>(meso->basisItem()))
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
                p->setMaterial(materialModel()->defaultMaterialItem());
    
    void SampleEditorController::setCoreFormFactor(CoreAndShellForm* widget,
    
                                                   FormFactorItemCatalog::Type type)
    
    {
        auto* particleCoreShell = widget->coreShellItem();
    
    
        if (particleCoreShell->coreItem() == nullptr)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
            particleCoreShell->createCoreItem(materialModel());
    
        particleCoreShell->coreItem()->setFormFactor(FormFactorItemCatalog::create(type));
    
        widget->createCoreWidgets();
    
        m_sampleForm->updateUnits();
    
        emit modified();
    
    void SampleEditorController::setShellFormFactor(CoreAndShellForm* widget,
    
                                                    FormFactorItemCatalog::Type type)
    
    {
        auto* particleCoreShell = widget->coreShellItem();
    
    
        if (particleCoreShell->shellItem() == nullptr)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
            particleCoreShell->createShellItem(materialModel());
    
        particleCoreShell->shellItem()->setFormFactor(FormFactorItemCatalog::create(type));
    
        widget->createShellWidgets();
    
        m_sampleForm->updateUnits();
    
        emit modified();
    
    void SampleEditorController::removeParticle(ItemWithParticles* itemToRemove)
    
        ASSERT(m_sampleForm);
    
        for (auto* layoutForm : m_sampleForm->findChildren<ParticleLayoutForm*>())
    
            if (layoutForm->layoutItem()->itemsWithParticles().contains(itemToRemove)) {
    
                layoutForm->onAboutToRemoveParticle(itemToRemove);
    
                emit aboutToRemoveItem(itemToRemove);
    
                layoutForm->layoutItem()->removeItemWithParticle(itemToRemove);
    
        for (auto* c : m_sampleForm->findChildren<CompoundForm*>())
    
            if (c->compositionItem()->itemsWithParticles().contains(itemToRemove)) {
    
                c->onAboutToRemoveParticle(itemToRemove);
    
                emit aboutToRemoveItem(itemToRemove);
    
                c->compositionItem()->removeItemWithParticle(itemToRemove);
    
    void SampleEditorController::setDouble(double newValue, DoubleProperty& d)
    
        m_undoStack.push(new CommandChangeValue(d.label(), this, d.value(), newValue, d.uid()));
        d.setValue(newValue);
    
        emit modified();
    
    }
    
    void SampleEditorController::setDoubleFromUndo(double newValue, const QString& path)
    {
    
        ASSERT(m_sampleForm);
    
        for (auto* s : m_sampleForm->findChildren<DoubleSpinBox*>()) {
    
        m_sampleForm->ensureVisible(spinBox);
    
        QSignalBlocker b(spinBox);
        spinBox->setBaseValue(newValue);
        spinBox->setFocus();
        spinBox->selectAll();
    
    void SampleEditorController::setCurrentIndex(AbstractSelectionContainerForm* widget, int index,
    
    {
        d.setCurrentIndex(index);
    
        widget->createContent();
    
        m_sampleForm->updateUnits();
    
        emit modified();
    
    QUndoStack* SampleEditorController::undoStack()
    {
        return &m_undoStack;
    }
    
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
    MaterialModel* SampleEditorController::materialModel() const
    
        return &m_sampleItem->materialModel();
    
    }
    
    ProjectDocument* SampleEditorController::projectDocument() const
    {
        return m_document;
    
    void SampleEditorController::selectMaterial(ItemWithMaterial* item,
                                                const QString& newMaterialIdentifier)
    {
        item->setMaterial(newMaterialIdentifier);
    
        //  update Layer title
    
        ASSERT(m_sampleForm);
        for (auto* c : m_sampleForm->findChildren<LayerForm*>())
    
            if (c->layerItem() == item)
                c->updateTitle();
    
        // #baLayerEditor notify all material users (update link info)
    
        emit modified();
    
    void SampleEditorController::setMaterialValue(ItemWithMaterial* item, double newValue,
    
    
        // -- notify all other users of this material (update values in the UI)
    
        ASSERT(m_sampleForm);
        for (auto* c : m_sampleForm->findChildren<MaterialInplaceForm*>())
    
            if (c->itemWithMaterial() != item
                && c->itemWithMaterial()->materialIdentifier() == item->materialIdentifier())
                c->updateValues();
    
        emit modified();
    
    void SampleEditorController::setDensityRelatedValue(InterferenceItem* interferenceItem,
    
                                                        double newValue, DoubleProperty& d)
    
        // -- notify the containing particle layout UI about changed value
    
        ASSERT(m_sampleForm);
        for (auto* c : m_sampleForm->findChildren<ParticleLayoutForm*>())
    
            if (c->layoutItem()->interferenceSelection().currentItem() == interferenceItem) {
    
    void SampleEditorController::onStartingToMoveLayer()
    {
    
        ASSERT(m_sampleForm);
        m_sampleForm->showAddLayerButtons(false);
    
    }
    
    void SampleEditorController::onStoppedToMoveLayer(QWidget* widgetToMove,
                                                      QWidget* moveAboveThisWidget)
    {
    
        ASSERT(m_sampleForm);
        m_sampleForm->showAddLayerButtons(true);
    
        auto* itemToMove = dynamic_cast<LayerForm*>(widgetToMove)->layerItem();
    
    
        const auto* moveAboveThisLayerForm = m_sampleForm->findNextLayerForm(moveAboveThisWidget);
        auto* moveAboveThisItem =
    
            moveAboveThisLayerForm != nullptr ? moveAboveThisLayerForm->layerItem() : nullptr;
    
    
        m_sampleItem->moveLayer(itemToMove, moveAboveThisItem);
    
        m_sampleForm->onLayerMoved(itemToMove);
    
    void SampleEditorController::setSampleName(const QString& name)
    {
    
        m_sampleItem->setSampleName(name);
    
        emit modified();
    }
    
    void SampleEditorController::setSampleDescription(const QString& description)
    {
    
        m_sampleItem->setDescription(description);
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    void SampleEditorController::setMesocrystalBasis(MesocrystalForm* widget,
    
                                                     ItemWithParticlesCatalog::Type type)
    
        auto* meso = widget->mesocrystalItem();
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        meso->setBasisItem(createAndInitItem(type));
    
        widget->createBasisWidgets();
    
        m_sampleForm->updateUnits();
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    void SampleEditorController::setMesocrystalBasis(MesocrystalForm* widget,
    
                                                     FormFactorItemCatalog::Type type)
    {
    
        auto* meso = widget->mesocrystalItem();
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        meso->setBasisItem(createAndInitItem(type));
    
        widget->createBasisWidgets();
    
        m_sampleForm->updateUnits();
    
        emit modified();
    
    void SampleEditorController::selectInterference(InterferenceForm* widget, int newIndex)
    
        widget->layoutItem()->interferenceSelection().setCurrentIndex(newIndex);
    
        widget->onInterferenceTypeChanged();
    
        m_sampleForm->updateUnits();
    
    
        // Disable/enable total density property in the particle layout, depending on type of
        // interference function.
        QWidget* parent = widget->parentWidget();
        while (parent != nullptr && dynamic_cast<ParticleLayoutForm*>(parent) == nullptr)
            parent = parent->parentWidget();
    
    
        if (auto* particleLayoutForm = dynamic_cast<ParticleLayoutForm*>(parent)) {
    
            particleLayoutForm->updateDensityEnabling();
    
            particleLayoutForm->updateDensityValue();
    
    
    void SampleEditorController::setIntegrateOverXi(LatticeTypeSelectionForm* widget, bool newValue)
    {
        widget->interferenceItem()->setXiIntegration(newValue);
        widget->onIntegrateOverXiChanged();
    
        emit modified();