Skip to content
Snippets Groups Projects
SampleEditorController.cpp 17.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • //  ************************************************************************************************
    //
    //  BornAgain: simulate and fit reflection and scattering
    //
    
    //! @file      GUI/View/Sample/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/Sample/SampleEditorController.h"
    
    #include "Base/Util/Assert.h"
    
    #include "Base/Util/Vec.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/LayerStackItem.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/Model/Util/Backup.h"
    
    #include "GUI/View/Sample/CompoundForm.h"
    #include "GUI/View/Sample/CoreAndShellForm.h"
    #include "GUI/View/Sample/InterferenceForm.h"
    #include "GUI/View/Sample/LatticeTypeSelectionForm.h"
    #include "GUI/View/Sample/LayerForm.h"
    
    #include "GUI/View/Sample/LayerStackForm.h"
    
    #include "GUI/View/Sample/MaterialInplaceForm.h"
    #include "GUI/View/Sample/MesocrystalForm.h"
    #include "GUI/View/Sample/ParticleLayoutForm.h"
    #include "GUI/View/Sample/SampleForm.h"
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include <QLabel>
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    SampleEditorController::SampleEditorController(SampleItem* multi)
    
        : m_sample_item(multi)
        , m_sample_form(nullptr)
    
    void SampleEditorController::setSampleForm(SampleForm* view)
    
        m_sample_form = view;
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    
    
    void SampleEditorController::addLayerItem(LayerStackForm& parentStackForm,
                                              const ItemWithLayers* before)
    
        LayerStackItem& parentStack = parentStackForm.stackItem();
        const int at_index = parentStack.indexOfComponent(before) + 1;
        QColor color = findColor(at_index); // before adding layer
        LayerItem* new_layer = m_sample_item->createLayerItemAt(parentStack, at_index);
        new_layer->setMaterial(materialModel()->defaultMaterialItem());
        new_layer->setColor(color);
        onComponentAdded(new_layer);
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    }
    
    void SampleEditorController::addLayerStackItem(LayerStackForm& parentStackForm,
                                                   const ItemWithLayers* before)
    
        LayerStackItem& parentStack = parentStackForm.stackItem();
        const int at_index = parentStack.indexOfComponent(before) + 1;
        LayerStackItem* new_stack = m_sample_item->createLayerStackItemAt(parentStack, at_index);
        onComponentAdded(new_stack);
    }
    
    void SampleEditorController::duplicateItemWithLayers(const ItemWithLayers* component)
    {
        LayerStackItem* parentStack = m_sample_item->parentOfComponent(component);
    
        // case of the outer stack
        if (!parentStack)
            return;
    
        LayerStackForm* parentStackForm = m_sample_form->formOfStackItem(parentStack);
        ASSERT(parentStackForm);
    
        const int at_index = parentStack->indexOfComponent(component) + 1;
    
        if (const auto* layer = dynamic_cast<const LayerItem*>(component)) {
            QColor color = findColor(at_index); // before adding layer
            LayerItem* new_layer = m_sample_item->createLayerItemAt(*parentStack, at_index);
            new_layer->setMaterial(materialModel()->defaultMaterialItem());
            GUI::Util::copyContents(layer, new_layer);
            new_layer->setColor(color);
            onComponentAdded(new_layer);
    
        } else if (const auto* stack = dynamic_cast<const LayerStackItem*>(component)) {
            LayerStackItem* new_stack = m_sample_item->createLayerStackItemAt(*parentStack, at_index);
            GUI::Util::copyContents(stack, new_stack);
            onComponentAdded(new_stack);
    
        } else
            ASSERT_NEVER;
    }
    
    void SampleEditorController::onComponentAdded(ItemWithLayers* component)
    {
        LayerStackItem* parentStack = m_sample_item->parentOfComponent(component);
        ASSERT(parentStack);
    
        LayerStackForm* parentStackForm = m_sample_form->formOfStackItem(parentStack);
        ASSERT(parentStackForm);
    
        parentStackForm->addComponentForm(component);
    
        m_sample_form->updateRowVisibilities();
    
        emit gDoc->sampleChanged();
    
    QColor SampleEditorController::findColor(size_t atIndex)
    
        std::vector<LayerItem*> uniqueLayerItems = m_sample_item->uniqueLayerItems();
    
    
        QColor result;
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        auto unusedColors = GUI::Util::Layer::predefinedLayerColors();
    
        for (auto* l : uniqueLayerItems)
    
            unusedColors.removeAll(l->color());
        if (!unusedColors.isEmpty())
    
            result = 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 : uniqueLayerItems)
    
                usage[l->color().name()] += 1;
    
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
            auto sortedByUsage = GUI::Util::Layer::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) ? uniqueLayerItems[atIndex - 1]->color() : QColor();
            const QColor below =
                (atIndex < uniqueLayerItems.size()) ? uniqueLayerItems[atIndex]->color() : QColor();
    
                if (col != above && col != below) {
    
                    result = col;
    
        return result;
    }
    
    void SampleEditorController::removeItemWithLayers(ItemWithLayers* component)
    
        LayerStackItem* parentStack = m_sample_item->parentOfComponent(component);
    
        // case of the outer stack
        if (!parentStack)
            return;
    
        LayerStackForm* parentStackForm = m_sample_form->formOfStackItem(parentStack);
        ASSERT(parentStackForm);
    
    
        emit aboutToRemoveItem(component);
    
        parentStackForm->removeComponentForm(component);
    
        m_sample_item->removeComponent(component);
    
        m_sample_form->updateRowVisibilities();
    
        emit gDoc->sampleChanged();
    
    void SampleEditorController::onLayoutAdded(LayerForm* layerForm, ParticleLayoutItem* layout)
    
        layerForm->onLayoutAdded(layout);
    
    
        for (auto* layoutForms : layerForm->findChildren<ParticleLayoutForm*>())
            layoutForms->updateTitle(layerForm->layerItem());
    
    
        emit gDoc->sampleChanged();
    
    void SampleEditorController::addLayoutItem(LayerForm* layerForm)
    {
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newLayout = layerForm->layerItem()->addLayoutItem();
        onLayoutAdded(layerForm, newLayout);
    
    }
    
    void SampleEditorController::duplicateLayoutItem(LayerForm* layerForm, ParticleLayoutItem* layout)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newLayout = layerForm->layerItem()->addLayoutItem();
        GUI::Util::copyContents(layout, newLayout);
        onLayoutAdded(layerForm, newLayout);
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
    void SampleEditorController::removeLayoutItem(LayerForm* layerForm, ParticleLayoutItem* layout)
    
        emit aboutToRemoveItem(layout);
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        layerForm->onAboutToRemoveLayout(layout);
        layerForm->layerItem()->removeLayoutItem(layout);
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        for (auto* layoutForm : layerForm->findChildren<ParticleLayoutForm*>())
            layoutForm->updateTitle(layerForm->layerItem());
    
        emit gDoc->sampleChanged();
    
    void SampleEditorController::onParticleLayoutAdded(ParticleLayoutItem* layout,
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
                                                       ItemWithParticles* newItem)
    
        emit gDoc->sampleChanged();
    
    
        //  search for particle layout widget for notification
    
        ASSERT(m_sample_form);
        for (auto* w : m_sample_form->findChildren<ParticleLayoutForm*>())
    
            if (w->layoutItem() == layout)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
                w->onParticleAdded(newItem);
    
    void SampleEditorController::addParticleLayoutItem(ParticleLayoutItem* layout,
    
    {
        auto* newParticle = createAndInitItem(formFactorType);
    
        layout->addItemWithParticleSelection(newParticle);
        onParticleLayoutAdded(layout, newParticle);
    
    void SampleEditorController::addParticleLayoutItem(ParticleLayoutItem* layout,
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newItem = createAndInitItem(type);
    
        layout->addItemWithParticleSelection(newItem);
        onParticleLayoutAdded(layout, newItem);
    
    }
    
    void SampleEditorController::duplicateItemWithParticles(ItemWithParticles* item)
    {
    
        auto* newItem = createAndInitItem(type);
        GUI::Util::copyContents(item, newItem);
    
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        if (ParticleLayoutItem* parent_layout = parentLayoutItem(item)) {
    
            parent_layout->addItemWithParticleSelection(newItem);
            onParticleLayoutAdded(parent_layout, newItem);
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        } else if (CompoundItem* parent_compound = parentCompoundItem(item)) {
    
            parent_compound->addItemWithParticleSelection(newItem);
            onParticleCompoundAdded(parent_compound, newItem);
        } else
    
            ASSERT_NEVER;
    
    void SampleEditorController::onParticleCompoundAdded(CompoundItem* composition,
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
                                                         ItemWithParticles* newItem)
    
        emit gDoc->sampleChanged();
    
        //  search for composition widget for notification
    
        ASSERT(m_sample_form);
        for (auto* c : m_sample_form->findChildren<CompoundForm*>())
    
            if (c->compositionItem() == composition)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
                c->onParticleAdded(newItem);
    
    void SampleEditorController::addCompoundItem(CompoundItem* compositionItem,
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newItem = createAndInitItem(type);
    
        compositionItem->addItemWithParticleSelection(newItem);
    
        onParticleCompoundAdded(compositionItem, newItem);
    
    void SampleEditorController::addCompoundItem(CompoundItem* compositionItem,
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newParticle = createAndInitItem(formFactorType);
    
        compositionItem->addItemWithParticleSelection(newParticle);
    
        onParticleCompoundAdded(compositionItem, newParticle);
    
    SampleEditorController::createAndInitItem(FormfactorCatalog::Type formFactorType) const
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* newParticle = new ParticleItem(materialModel());
    
        newParticle->setFormfactor(FormfactorCatalog::create(formFactorType));
    
        newParticle->setMaterial(materialModel()->defaultParticleMaterialItem());
    
    ItemWithParticles* SampleEditorController::createAndInitItem(ParticleCatalog::Type type) const
    
        auto* newItem = ParticleCatalog::create(type, 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,
    
    {
        auto* particleCoreShell = widget->coreShellItem();
    
    
        if (particleCoreShell->coreItem() == nullptr)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
            particleCoreShell->createCoreItem(materialModel());
    
        particleCoreShell->coreItem()->setFormfactor(FormfactorCatalog::create(type));
    
        widget->createCoreWidgets();
    
        emit gDoc->sampleChanged();
    
    void SampleEditorController::setShellFormfactor(CoreAndShellForm* widget,
    
    {
        auto* particleCoreShell = widget->coreShellItem();
    
    
        if (particleCoreShell->shellItem() == nullptr)
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
            particleCoreShell->createShellItem(materialModel());
    
        particleCoreShell->shellItem()->setFormfactor(FormfactorCatalog::create(type));
    
        widget->createShellWidgets();
    
        emit gDoc->sampleChanged();
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
    ParticleLayoutItem* SampleEditorController::parentLayoutItem(ItemWithParticles* item)
    
        for (auto* layoutForm : m_sample_form->findChildren<ParticleLayoutForm*>())
    
            if (Vec::containsPtr(item, layoutForm->layoutItem()->itemsWithParticles()))
    
                return layoutForm->layoutItem();
        return nullptr;
    }
    
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
    CompoundItem* SampleEditorController::parentCompoundItem(ItemWithParticles* item)
    
        for (auto* compoundForm : m_sample_form->findChildren<CompoundForm*>())
    
            if (Vec::containsPtr(item, compoundForm->compositionItem()->itemsWithParticles()))
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
                return compoundForm->compositionItem();
    
        return nullptr;
    }
    
    
    void SampleEditorController::removeParticle(ItemWithParticles* itemToRemove)
    
        ASSERT(m_sample_form);
    
        for (auto* layoutForm : m_sample_form->findChildren<ParticleLayoutForm*>())
    
            if (Vec::containsPtr(itemToRemove, layoutForm->layoutItem()->itemsWithParticles())) {
    
                layoutForm->onAboutToRemoveParticle(itemToRemove);
    
                emit aboutToRemoveItem(itemToRemove);
    
                layoutForm->layoutItem()->removeItemWithParticle(itemToRemove);
    
                emit gDoc->sampleChanged();
    
        for (auto* c : m_sample_form->findChildren<CompoundForm*>())
    
            if (Vec::containsPtr(itemToRemove, c->compositionItem()->itemsWithParticles())) {
    
                c->onAboutToRemoveParticle(itemToRemove);
    
                emit aboutToRemoveItem(itemToRemove);
    
                c->compositionItem()->removeItemWithParticle(itemToRemove);
    
                emit gDoc->sampleChanged();
    
    MaterialsSet* SampleEditorController::materialModel() const
    
        return &m_sample_item->materialModel();
    
    void SampleEditorController::selectMaterial(ItemWithMaterial* item,
                                                const QString& newMaterialIdentifier)
    {
        item->setMaterial(newMaterialIdentifier);
    
        //  update Layer title
    
        ASSERT(m_sample_form);
        for (auto* c : m_sample_form->findChildren<LayerForm*>())
    
            if (c->layerItem() == item)
                c->updateTitle();
    
        // #baLayerEditor notify all material users (update link info)
    
        emit gDoc->sampleChanged();
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    void SampleEditorController::setMaterialValue(ItemWithMaterial* item, double value,
    
        d.setDVal(value);
    
        emit gDoc->sampleChanged();
    
    
        // -- notify all other users of this material (update values in the UI)
    
        ASSERT(m_sample_form);
        for (auto* c : m_sample_form->findChildren<MaterialInplaceForm*>())
    
            if (c->itemWithMaterial() != item
                && c->itemWithMaterial()->materialIdentifier() == item->materialIdentifier())
                c->updateValues();
    
        emit gDoc->sampleChanged();
    
    //! notify the containing particle layout UI about changed value
    void SampleEditorController::setDensityRelatedValue(InterferenceItem* interferenceItem)
    {
    
        ASSERT(m_sample_form);
        for (auto* c : m_sample_form->findChildren<ParticleLayoutForm*>())
    
            if (c->layoutItem()->interferenceSelection().certainItem() == interferenceItem) {
    
    void SampleEditorController::onStoppedToMoveLayer(QWidget* widgetToMove,
                                                      QWidget* moveAboveThisWidget)
    {
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        auto* formToMove = dynamic_cast<LayerContainerForm*>(widgetToMove);
        ASSERT(formToMove);
        auto* itemToMove = formToMove->item();
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        LayerStackItem* parentStack = m_sample_item->parentOfComponent(itemToMove);
        ASSERT(parentStack);
    
        LayerStackForm* parentStackForm = m_sample_form->formOfStackItem(parentStack);
        ASSERT(parentStackForm);
    
        const LayerContainerForm* moveAboveThisLayerForm =
            parentStackForm->findNextLayerContainerForm(moveAboveThisWidget);
    
        auto* moveAboveThisItem = moveAboveThisLayerForm ? moveAboveThisLayerForm->item() : nullptr;
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        m_sample_item->moveComponent(itemToMove, moveAboveThisItem);
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        parentStackForm->onComponentMoved(itemToMove);
    
        m_sample_form->updateRowVisibilities();
    
        emit gDoc->sampleChanged();
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    void SampleEditorController::setMesocrystalBasis(MesocrystalForm* widget,
    
        auto* meso = widget->mesocrystalItem();
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        meso->setBasisItem(createAndInitItem(type));
    
        widget->createBasisWidgets();
    
        emit gDoc->sampleChanged();
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    void SampleEditorController::setMesocrystalBasis(MesocrystalForm* widget,
    
        auto* meso = widget->mesocrystalItem();
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        meso->setBasisItem(createAndInitItem(type));
    
        widget->createBasisWidgets();
    
        emit gDoc->sampleChanged();
    
    void SampleEditorController::selectInterference(InterferenceForm* widget, int newIndex)
    
        widget->layoutItem()->interferenceSelection().setCertainIndex(newIndex);
    
        widget->onInterferenceTypeChanged();
    
    
        // 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();
    
        emit gDoc->sampleChanged();