From bb99d195082fe6ea8f47c1e91fab36536f768994 Mon Sep 17 00:00:00 2001
From: Matthias Puchner <github@mpuchner.de>
Date: Sat, 18 Dec 2021 09:02:19 +0100
Subject: [PATCH] free ItemWithParticles from SessionItem

---
 GUI/Model/Sample/ItemWithParticles.cpp        | 133 +++---------------
 GUI/Model/Sample/ItemWithParticles.h          |  51 +++----
 GUI/Model/Sample/ItemWithParticlesCatalog.cpp |  84 +++++++++++
 GUI/Model/Sample/ItemWithParticlesCatalog.h   |  55 ++++++++
 4 files changed, 175 insertions(+), 148 deletions(-)
 create mode 100644 GUI/Model/Sample/ItemWithParticlesCatalog.cpp
 create mode 100644 GUI/Model/Sample/ItemWithParticlesCatalog.h

diff --git a/GUI/Model/Sample/ItemWithParticles.cpp b/GUI/Model/Sample/ItemWithParticles.cpp
index 7d83a527051..ac4423b69cd 100644
--- a/GUI/Model/Sample/ItemWithParticles.cpp
+++ b/GUI/Model/Sample/ItemWithParticles.cpp
@@ -7,161 +7,66 @@
 //!
 //! @homepage  http://www.bornagainproject.org
 //! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @copyright Forschungszentrum Jülich GmbH 2021
 //! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
 //
 //  ************************************************************************************************
 
 #include "GUI/Model/Sample/ItemWithParticles.h"
 #include "Base/Vector/RotMatrix.h"
-#include "GUI/Model/Session/SessionItemUtils.h"
-#include "GUI/Model/Session/SessionModel.h"
-#include "GUI/Model/Trafo/RotationItems.h"
-#include "GUI/Model/Trafo/TransformationItem.h"
-#include "GUI/Model/Types/VectorDescriptor.h"
-#include "MesoCrystalItem.h"
-#include "ParticleCompositionItem.h"
-#include "ParticleCoreShellItem.h"
+#include "GUI/Model/Sample/RotationItemCatalog.h"
 #include "Sample/Particle/IParticle.h"
 #include "Sample/Scattering/Rotations.h"
 
-DoubleDescriptor ItemWithParticles::abundance() const
-{
-    return DoubleDescriptor(getItem(P_ABUNDANCE), Unit::unitless);
-}
-
-void ItemWithParticles::setAbundance(const double abundance)
-{
-    setItemValue(P_ABUNDANCE, abundance);
-}
 
-SessionItem* ItemWithParticles::abundanceItem() const
+ItemWithParticles::ItemWithParticles(const QString& abundanceTooltip,
+                                     const QString& positionTooltip)
 {
-    return getItem(P_ABUNDANCE);
+    m_abundance.init("Abundance", abundanceTooltip, 1.0, Unit::unitless, 3,
+                     RealLimits::limited(0.0, 1.0), "abundance");
+    m_position.init("Position Offset", positionTooltip, Unit::nanometer, "pos");
+    m_rotation.init<RotationItemCatalog>("Rotation", "", "rotation");
 }
 
-void ItemWithParticles::enableAbundance(bool b)
+DoubleDescriptor ItemWithParticles::abundance() const
 {
-    getItem(P_ABUNDANCE)->setEnabled(b);
+    return m_abundance;
 }
 
-bool ItemWithParticles::parentHasOwnAbundance() const
+void ItemWithParticles::setAbundance(const double abundance)
 {
-    return dynamic_cast<ParticleCoreShellItem*>(parent())
-           || dynamic_cast<ParticleCompositionItem*>(parent())
-           || dynamic_cast<MesoCrystalItem*>(parent());
+    m_abundance.set(abundance);
 }
 
 R3 ItemWithParticles::position() const
 {
-    return item<VectorItem>(P_POSITION)->getVector();
+    return m_position;
 }
 
 void ItemWithParticles::setPosition(const R3& position)
 {
-    item<VectorItem>(P_POSITION)->setVector(position);
+    m_position.set(position);
 }
 
 VectorDescriptor ItemWithParticles::positionVector() const
 {
-    return VectorDescriptor(item<VectorItem>(P_POSITION), Unit::nanometer);
-}
-
-VectorItem* ItemWithParticles::positionItem() const
-{
-    return item<VectorItem>(P_POSITION);
-}
-
-TransformationItem* ItemWithParticles::createTransformationItem()
-{
-    return model()->insertItem<TransformationItem>(this, -1, T_TRANSFORMATION);
-}
-
-void ItemWithParticles::setTransformation(RotationItem* transformation)
-{
-    model()->moveItem(transformation, this, -1, T_TRANSFORMATION);
-}
-
-RotationItem* ItemWithParticles::rotationItem() const
-{
-    auto* transformationItem = dynamic_cast<TransformationItem*>(getItem(T_TRANSFORMATION));
-    return transformationItem ? transformationItem->rotationItem() : nullptr;
-}
-
-bool ItemWithParticles::isTransformationTagName(const QString& name)
-{
-    return name == T_TRANSFORMATION;
-}
-
-RotMatrix ItemWithParticles::rotation() const
-{
-    auto* const item = rotationItem();
-    return item ? item->rotation() : RotMatrix();
+    return m_position;
 }
 
 SelectionDescriptor<RotationItem*> ItemWithParticles::rotationMethod()
 {
-    SelectionDescriptor<RotationItem*> d;
-
-    // we need a special filling for this selection descriptor (not just from a GroupItem), since
-    // the rotation is stored in a TransformationItem instance, which can be present or not.
-
-    static QVector<QPair<QString, QString>> map = {{"None", ""},
-                                                   {"X axis Rotation", XRotationItem::M_TYPE},
-                                                   {"Y axis Rotation", YRotationItem::M_TYPE},
-                                                   {"Z axis Rotation", ZRotationItem::M_TYPE},
-                                                   {"Euler Rotation", EulerRotationItem::M_TYPE}};
-
-    d.label = "Rotation";
-
-    for (auto [title, type] : map)
-        d.options << title;
-
-    d.currentItem = [=]() -> RotationItem* { return rotationItem(); };
-
-    d.currentIndexSetter = [=](int current) {
-        if (auto* item = getItem(T_TRANSFORMATION))
-            model()->removeItem(item);
-        if (current > 0)
-            createTransformationItem()->setRotationType(map[current].second);
-    };
-
-    d.currentIndexGetter = [=]() {
-        auto* item = rotationItem();
-        if (item == nullptr)
-            return 0;
-        for (int i = 1; i < map.size(); i++)
-            if (map[i].second == item->modelType())
-                return i;
-
-        return 0;
-    };
-
-    return d;
-}
-
-ItemWithParticles::ItemWithParticles(const QString& model_type, const QString& abundance_tooltip,
-                                     const QString& position_tooltip)
-    : SessionItem(model_type)
-{
-    addProperty(P_ABUNDANCE, 1.0)
-        ->setLimits(RealLimits::limited(0.0, 1.0))
-        .setDecimals(3)
-        .setToolTip(abundance_tooltip);
-    addProperty<VectorItem>(P_POSITION)->setToolTip(position_tooltip);
-
-    registerTag(T_TRANSFORMATION, 0, 1, {TransformationItem::M_TYPE});
+    return m_rotation;
 }
 
-void ItemWithParticles::setDefaultTagTransformation()
+void ItemWithParticles::setRotation(RotationItem* p)
 {
-    setDefaultTag(T_TRANSFORMATION);
+    m_rotation.set(p);
 }
 
 void ItemWithParticles::setTransformationInfo(IParticle* result) const
 {
     result->setParticlePosition(position());
-    const RotMatrix r = rotation();
+    const RotMatrix r = m_rotation.get() ? m_rotation.get()->rotation() : RotMatrix();
     if (!r.isIdentity()) {
         std::unique_ptr<IRotation> rotation(IRotation::createRotation(r));
         result->setRotation(*rotation);
diff --git a/GUI/Model/Sample/ItemWithParticles.h b/GUI/Model/Sample/ItemWithParticles.h
index 0c19710c7e9..8ec9eb4a509 100644
--- a/GUI/Model/Sample/ItemWithParticles.h
+++ b/GUI/Model/Sample/ItemWithParticles.h
@@ -7,7 +7,7 @@
 //!
 //! @homepage  http://www.bornagainproject.org
 //! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @copyright Forschungszentrum Jülich GmbH 2021
 //! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
 //
 //  ************************************************************************************************
@@ -15,50 +15,32 @@
 #ifndef BORNAGAIN_GUI_MODEL_SAMPLE_ITEMWITHPARTICLES_H
 #define BORNAGAIN_GUI_MODEL_SAMPLE_ITEMWITHPARTICLES_H
 
-#include "GUI/Model/Group/SelectionDescriptor.h"
-#include "GUI/Model/Session/SessionItem.h"
-#include <heinz/Vectors3D.h>
+#include "GUI/Model/Sample/RotationItems.h"
+#include "GUI/Model/Types/DoubleProperty.h"
+#include "GUI/Model/Types/SelectionProperty.h"
+#include "GUI/Model/Types/VectorProperty.h"
 
-class DoubleDescriptor;
 class IParticle;
-class IRotation;
-class RotationItem;
 class RotMatrix;
-class TransformationItem;
-class VectorDescriptor;
-class VectorItem;
-
-class ItemWithParticles : public virtual SessionItem {
-private:
-    static constexpr auto P_ABUNDANCE{"Abundance"};
-    static constexpr auto P_POSITION{"Position Offset"};
-    static constexpr auto T_TRANSFORMATION{"Transformation Tag"};
 
+class ItemWithParticles {
 public:
+    virtual ~ItemWithParticles() = default;
     DoubleDescriptor abundance() const;
     void setAbundance(double abundance);
-    SessionItem* abundanceItem() const;
-    bool parentHasOwnAbundance() const;
-
-    // #baMigration Use only while not migrated from SessionModel!
-    void enableAbundance(bool b);
 
     R3 position() const;
     void setPosition(const R3& position);
     VectorDescriptor positionVector() const;
-    VectorItem* positionItem() const;
-
-    TransformationItem* createTransformationItem();
-    void setTransformation(RotationItem* transformation);
-    static bool isTransformationTagName(const QString& name);
-
-    //! Returns identity transformation if no rotation is defined at all
-    RotMatrix rotation() const;
 
     //! Returns selection descriptor for rotation methods.
     SelectionDescriptor<RotationItem*> rotationMethod();
 
-    void setTransformationInfo(IParticle* result) const;
+    //! nullptr is allowed and sets to "no rotation"
+    void setRotation(RotationItem* p);
+
+    void
+    setTransformationInfo(IParticle* result) const; // #baMigration rename; it's a getter/reader
 
     //! Return full hierarchical contained items with particles.
     //!
@@ -66,15 +48,16 @@ public:
     virtual QVector<ItemWithParticles*> containedItemsWithParticles() const = 0;
 
 protected:
-    ItemWithParticles(const QString& model_type, const QString& abundance_tooltip,
-                      const QString& position_tooltip);
-
-    void setDefaultTagTransformation();
+    ItemWithParticles(const QString& abundanceTooltip, const QString& positionTooltip);
 
 private:
     //! Convenience method to return a rotation item from the contained transformation item.
     //! nullptr, if no transformation item defined.
     RotationItem* rotationItem() const;
+
+    DoubleProperty m_abundance;
+    VectorProperty m_position;
+    SelectionProperty<RotationItem*> m_rotation;
 };
 
 #endif // BORNAGAIN_GUI_MODEL_SAMPLE_ITEMWITHPARTICLES_H
diff --git a/GUI/Model/Sample/ItemWithParticlesCatalog.cpp b/GUI/Model/Sample/ItemWithParticlesCatalog.cpp
new file mode 100644
index 00000000000..7a519735769
--- /dev/null
+++ b/GUI/Model/Sample/ItemWithParticlesCatalog.cpp
@@ -0,0 +1,84 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Model/Sample/ItemWithParticlesCatalog.cpp
+//! @brief     Implements class ItemWithParticlesCatalog
+//!
+//! @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/Model/Sample/ItemWithParticlesCatalog.h"
+#include "GUI/Model/Sample/MesoCrystalItem.h"
+#include "GUI/Model/Sample/ParticleCompositionItem.h"
+#include "GUI/Model/Sample/ParticleCoreShellItem.h"
+#include "GUI/Model/Sample/ParticleItem.h"
+
+ItemWithParticles* ItemWithParticlesCatalog::create(Type type)
+{
+    switch (type) {
+    case Type::Particle:
+        return new ParticleItem();
+    case Type::Composition:
+        return new ParticleCompositionItem();
+    case Type::CoreShell:
+        return new ParticleCoreShellItem();
+    case Type::MesoCrystal:
+        return new MesoCrystalItem();
+    default:
+        ASSERT(false);
+    }
+}
+
+QVector<ItemWithParticlesCatalog::Type> ItemWithParticlesCatalog::types()
+{
+    return {Type::Particle, Type::Composition, Type::CoreShell, Type::MesoCrystal};
+}
+
+ItemWithParticlesCatalog::UiInfo ItemWithParticlesCatalog::uiInfo(Type type)
+{
+    auto createUiInfo = [](const QString& menuEntry, const QString& iconName,
+                           const QString& description) {
+        UiInfo info;
+        info.menuEntry = menuEntry;
+        info.iconPath = ":/SampleDesignerToolbox/images/" + iconName;
+        info.description = description;
+        return info;
+    };
+
+    switch (type) {
+    case Type::Particle:
+        return createUiInfo("Particle", "", ""); // particle is not on UI, only its form factor
+    case Type::Composition:
+        return createUiInfo("Particle Composition", "ParticleComposition_64x64.png",
+                            "Composition of particles with fixed positions");
+    case Type::CoreShell:
+        return createUiInfo("Core shell particle", "ParticleCoreShell_64x64.png",
+                            "A particle with a core/shell geometry");
+    case Type::MesoCrystal:
+        return createUiInfo("Meso Crystal", "Mesocrystal_64x64.png",
+                            "A 3D crystal structure of nanoparticles");
+    default:
+        ASSERT(false);
+    }
+}
+
+ItemWithParticlesCatalog::Type ItemWithParticlesCatalog::type(ItemWithParticles* item)
+{
+    ASSERT(item);
+
+    if (dynamic_cast<ParticleItem*>(item))
+        return Type::Particle;
+    if (dynamic_cast<ParticleCompositionItem*>(item))
+        return Type::Composition;
+    if (dynamic_cast<MesoCrystalItem*>(item))
+        return Type::MesoCrystal;
+    if (dynamic_cast<ParticleCoreShellItem*>(item))
+        return Type::CoreShell;
+
+    ASSERT(false);
+}
diff --git a/GUI/Model/Sample/ItemWithParticlesCatalog.h b/GUI/Model/Sample/ItemWithParticlesCatalog.h
new file mode 100644
index 00000000000..bcf7b60b7d5
--- /dev/null
+++ b/GUI/Model/Sample/ItemWithParticlesCatalog.h
@@ -0,0 +1,55 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Model/Sample/ItemWithParticlesCatalog.h
+//! @brief     Defines class ItemWithParticlesCatalog
+//!
+//! @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)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_GUI_MODEL_SAMPLE_ITEMWITHPARTICLESCATALOG_H
+#define BORNAGAIN_GUI_MODEL_SAMPLE_ITEMWITHPARTICLESCATALOG_H
+
+#include <QString>
+
+class ItemWithParticles;
+
+class ItemWithParticlesCatalog {
+public:
+    using CatalogedType = ItemWithParticles;
+
+    // Do not change the numbering! It is serialized!
+    enum class Type : uint8_t { Particle = 1, Composition = 2, CoreShell = 3, MesoCrystal = 4 };
+
+    struct UiInfo {
+        QString menuEntry;
+        QString description;
+        QString iconPath;
+    };
+
+    //! Creates the item of the given type.
+    //!
+    //! If type is "None", a nullptr is returned.
+    static ItemWithParticles* create(Type type);
+
+    //! Available types of interference items.
+    //!
+    //! Contains also type "None".
+    //! This list is sorted as expected in the UI (e.g. in combo box)
+    static QVector<Type> types();
+
+    //! UiInfo on the given type.
+    static UiInfo uiInfo(Type t);
+
+    //! Returns the enum type of the given item.
+    static Type type(ItemWithParticles* item);
+
+    static QString menuEntry(ItemWithParticles* item);
+};
+
+#endif // BORNAGAIN_GUI_MODEL_SAMPLE_ITEMWITHPARTICLESCATALOG_H
-- 
GitLab