From 3f146be3d1ba21b7c915dd16fb529521b0fe015d Mon Sep 17 00:00:00 2001
From: Matthias Puchner <github@mpuchner.de>
Date: Wed, 22 Dec 2021 06:33:05 +0100
Subject: [PATCH] complete parameter tree builder

---
 GUI/Model/Job/ParameterTreeUtils.cpp | 226 ++++++++++++++++++---------
 GUI/Model/Job/ParameterTreeUtils.h   |  11 +-
 2 files changed, 160 insertions(+), 77 deletions(-)

diff --git a/GUI/Model/Job/ParameterTreeUtils.cpp b/GUI/Model/Job/ParameterTreeUtils.cpp
index 94135cc3020..0ea7c68acc2 100644
--- a/GUI/Model/Job/ParameterTreeUtils.cpp
+++ b/GUI/Model/Job/ParameterTreeUtils.cpp
@@ -19,21 +19,24 @@
 #include "GUI/Model/Instrument/InstrumentItems.h"
 #include "GUI/Model/Job/JobItem.h"
 #include "GUI/Model/Material/MaterialItem.h"
+#include "GUI/Model/Sample/FTDecayFunctionItemCatalogs.h"
+#include "GUI/Model/Sample/FTDistributionItemCatalogs.h"
 #include "GUI/Model/Sample/FTDistributionItems.h"
+#include "GUI/Model/Sample/FormFactorItemCatalog.h"
 #include "GUI/Model/Sample/InterferenceItemCatalog.h"
 #include "GUI/Model/Sample/InterferenceItems.h"
+#include "GUI/Model/Sample/ItemWithParticlesCatalog.h"
+#include "GUI/Model/Sample/Lattice2DItemCatalog.h"
 #include "GUI/Model/Sample/Lattice2DItems.h"
 #include "GUI/Model/Sample/LayerItem.h"
+#include "GUI/Model/Sample/MesoCrystalItem.h"
 #include "GUI/Model/Sample/MultiLayerItem.h"
+#include "GUI/Model/Sample/ParticleCompositionItem.h"
 #include "GUI/Model/Sample/ParticleCoreShellItem.h"
 #include "GUI/Model/Sample/ParticleItem.h"
 #include "GUI/Model/Sample/ParticleLayoutItem.h"
+#include "GUI/Model/Sample/RotationItemCatalog.h"
 #include "GUI/Model/Types/VectorDescriptor.h"
-#include "GUI/Util/Error.h"
-#include <QStack>
-#include <boost/polymorphic_cast.hpp>
-
-using boost::polymorphic_downcast;
 
 namespace {
 
@@ -43,7 +46,7 @@ QString labelWithUnit(const QString& label, variant<QString, Unit> unit)
                                                             : unitAsString(std::get<Unit>(unit));
 
     if (!s.isEmpty())
-        return label + "[" + s + "]";
+        return label + " [" + s + "]";
 
     return label;
 }
@@ -88,6 +91,21 @@ void handleItem(ParameterContainerItem* container, QObject* tree, SessionItem* s
     }
 }
 
+template <typename Catalog>
+ParameterLabelItem* addLabel(ParameterLabelItem* parent, const QString& category,
+                             const typename Catalog::CatalogedType* p)
+{
+    const auto title = category + " (" + Catalog::uiInfo(Catalog::type(p)).menuEntry + ")";
+    return new ParameterLabelItem(title, parent);
+}
+
+template <typename Catalog>
+ParameterLabelItem* addLabel(ParameterLabelItem* parent, const typename Catalog::CatalogedType* p)
+{
+    const auto title = Catalog::uiInfo(Catalog::type(p)).menuEntry;
+    return new ParameterLabelItem(title, parent);
+}
+
 } // namespace
 
 ParameterTreeBuilder::ParameterTreeBuilder(JobItem* jobItem, bool recreateBackupValues)
@@ -154,7 +172,7 @@ void ParameterTreeBuilder::addSample()
             addInterference(layoutLabel, layout);
 
             for (auto* p : layout->particles())
-                addParticle(layoutLabel, p);
+                addParticle(layoutLabel, p, true);
         }
     }
 }
@@ -166,7 +184,7 @@ void ParameterTreeBuilder::addInstrument()
                m_recreateBackupValues);
 }
 
-void ParameterTreeBuilder::addParameterItem(ParameterLabelItem* parent, DoubleDescriptor d)
+void ParameterTreeBuilder::addParameterItem(ParameterLabelItem* parent, const DoubleDescriptor& d)
 {
     auto* parameterItem = new ParameterItem(parent);
     parameterItem->setTitle(labelWithUnit(d.label, d.unit));
@@ -175,11 +193,12 @@ void ParameterTreeBuilder::addParameterItem(ParameterLabelItem* parent, DoubleDe
         m_jobItem->parameterContainerItem()->setBackupValue(parameterItem->link(), d.get());
 }
 
-void ParameterTreeBuilder::addParameterItem(ParameterLabelItem* parent, VectorDescriptor d)
+void ParameterTreeBuilder::addParameterItem(ParameterLabelItem* parent, const VectorDescriptor& d)
 {
-    addParameterItem(parent, d.x);
-    addParameterItem(parent, d.y);
-    addParameterItem(parent, d.z);
+    auto* label = new ParameterLabelItem(d.label, parent);
+    addParameterItem(label, d.x);
+    addParameterItem(label, d.y);
+    addParameterItem(label, d.z);
 }
 
 ParameterContainerItem* ParameterTreeBuilder::parameterContainer()
@@ -196,73 +215,132 @@ bool ParameterTreeBuilder::allowMagneticFields() const
 void ParameterTreeBuilder::addInterference(ParameterLabelItem* layoutLabel,
                                            const ParticleLayoutItem* layout)
 {
-    if (!layout->interference().currentItem())
+    const auto* interference = layout->interference().currentItem();
+    if (!interference)
         return;
 
-    const auto itfType = InterferenceItemCatalog::type(layout->interference().currentItem());
+    const auto itfType = InterferenceItemCatalog::type(interference);
     const QString title = InterferenceItemCatalog::uiInfo(itfType).menuEntry;
 
-    auto* itfLabel = new ParameterLabelItem(title, layoutLabel);
-
-    Q_UNUSED(itfLabel); // temporary
-    // later in handleItems the rotation of an interference may have to be ignored.
-    // #baMigration implement the following!
-    //         if (auto* c = dynamic_cast<Interference2DAbstractLatticeItem*>(child))
-    //             if (c->latticeType().currentItem())
-    //                 c->latticeType().currentItem()->enableRotationAngle(!c->xiIntegration());
-
-    // #baMigration see the following!
-    // if PDF1 and PDF2 are of same type, add a number to the name
-    // if (auto* itf = dynamic_cast<Interference2DParaCrystalItem*>(
-    //        layout->interference().currentItem())) {
-    //    SessionItem* pdf1 = itf->probabilityDistribution1().currentItem();
-    //    SessionItem* pdf2 = itf->probabilityDistribution2().currentItem();
-    //
-    //    if (pdf1 && pdf2) {
-    //        if (pdf1->modelType() == pdf2->modelType()) {
-    //            pdf1->setDisplayName(pdf1->modelType() + "0");
-    //            pdf2->setDisplayName(pdf2->modelType() + "1");
-    //        } else {
-    //            pdf1->setDisplayName(pdf1->modelType());
-    //            pdf2->setDisplayName(pdf2->modelType());
-    //        }
-    //    }
-    //}
-
-    throw std::logic_error("The method or operation is not implemented.");
+    auto* itfLabel = new ParameterLabelItem("Interference (" + title + ")", layoutLabel);
+
+
+    if (const auto* itf = dynamic_cast<const InterferenceRadialParaCrystalItem*>(interference)) {
+        addParameterItem(itfLabel, itf->positionVariance());
+        addParameterItem(itfLabel, itf->peakDistance());
+        addParameterItem(itfLabel, itf->dampingLength());
+        addParameterItem(itfLabel, itf->domainSize());
+        addParameterItem(itfLabel, itf->kappa());
+
+        const auto* pdf = itf->probabilityDistribution().currentItem();
+        auto* pdfLabel = addLabel<FTDistribution1DItemCatalog>(itfLabel, "PDF", pdf);
+        for (const auto& d : pdf->valueDescriptors())
+            addParameterItem(pdfLabel, d);
+    } else if (const auto* itf = dynamic_cast<const Interference2DParaCrystalItem*>(interference)) {
+        addParameterItem(itfLabel, itf->positionVariance());
+        addParameterItem(itfLabel, itf->dampingLength());
+        addParameterItem(itfLabel, itf->domainSize1());
+        addParameterItem(itfLabel, itf->domainSize2());
+        addLattice(itfLabel, itf);
+
+        const auto* pdf1 = itf->probabilityDistribution1().currentItem();
+        const auto* pdf2 = itf->probabilityDistribution2().currentItem();
+        const bool samePdfTypes =
+            FTDistribution2DItemCatalog::type(pdf1) == FTDistribution2DItemCatalog::type(pdf2);
+        auto* pdf1Label =
+            addLabel<FTDistribution2DItemCatalog>(itfLabel, samePdfTypes ? "PDF1" : "PDF", pdf1);
+        for (const auto& d : pdf1->valueDescriptors())
+            addParameterItem(pdf1Label, d);
+        auto* pdf2Label =
+            addLabel<FTDistribution2DItemCatalog>(itfLabel, samePdfTypes ? "PDF2" : "PDF", pdf2);
+        for (const auto& d : pdf2->valueDescriptors())
+            addParameterItem(pdf2Label, d);
+    } else if (const auto* itf = dynamic_cast<const Interference1DLatticeItem*>(interference)) {
+        addParameterItem(itfLabel, itf->positionVariance());
+        addParameterItem(itfLabel, itf->length());
+        addParameterItem(itfLabel, itf->rotationAngle());
+
+        const auto* df = itf->decayFunction().currentItem();
+        auto* dfLabel = addLabel<FTDecayFunction1DItemCatalog>(itfLabel, "Decay function", df);
+        for (const auto& d : df->valueDescriptors())
+            addParameterItem(dfLabel, d);
+    } else if (const auto* itf = dynamic_cast<const Interference2DLatticeItem*>(interference)) {
+        addParameterItem(itfLabel, itf->positionVariance());
+        addLattice(itfLabel, itf);
+
+        const auto* df = itf->decayFunction().currentItem();
+        auto* dfLabel = addLabel<FTDecayFunction2DItemCatalog>(itfLabel, "Decay function", df);
+        for (const auto& d : df->valueDescriptors())
+            addParameterItem(dfLabel, d);
+    } else if (const auto* itf =
+                   dynamic_cast<const InterferenceFinite2DLatticeItem*>(interference)) {
+        // domainSize1 and domainSize2 are of type UInt (not matching the double approach for tuning
+        // and fitting). In BornAgain 1.18 these values have not been added to the tuning tree, and
+        // also not to the fitting parameters. Maybe this should be necessary, but for now this
+        // stays the same and the two sizes are not added
+        addParameterItem(itfLabel, itf->positionVariance());
+        addLattice(itfLabel, itf);
+    } else if (const auto* itf = dynamic_cast<const InterferenceHardDiskItem*>(interference)) {
+        addParameterItem(itfLabel, itf->positionVariance());
+        addParameterItem(itfLabel, itf->radius());
+        addParameterItem(itfLabel, itf->density());
+    }
 }
 
-void ParameterTreeBuilder::addParticle(ParameterLabelItem* parentLabel, ItemWithParticles* p)
+void ParameterTreeBuilder::addParticle(ParameterLabelItem* parentLabel, ItemWithParticles* p,
+                                       bool enableAbundance, bool enablePosition)
 {
-    Q_UNUSED(parentLabel); // temporary
-    Q_UNUSED(p);           // temporary
-    // #baMigration implement the following!
-    // if (auto* c = dynamic_cast<ItemWithParticles*>(child))
-    //     c->enableAbundance(!c->parentHasOwnAbundance());
-    //
-    // function was:
-    //
-    //         bool ItemWithParticles::parentHasOwnAbundance() const
-    //         {
-    //             return dynamic_cast<ParticleCoreShellItem*>(parent())
-    //                    || dynamic_cast<ParticleCompositionItem*>(parent())
-    //                    || dynamic_cast<MesoCrystalItem*>(parent());
-    //         }
-
-
-    //
-    // later in handleItems the position of a shell particle has to be ignored.
-    // #baMigration implement the following!
-    //         if (auto* c = dynamic_cast<ParticleCoreShellItem*>(child))
-    //             if (c->shell())
-    //                 c->shell()->positionItem()->setEnabled(false);
-
-    // later in handleItems the rotation of an interference may have to be ignored.
-    // #baMigration implement the following!
-    //         if (auto* c = dynamic_cast<Interference2DAbstractLatticeItem*>(child))
-    //             if (c->latticeType().currentItem())
-    //                 c->latticeType().currentItem()->enableRotationAngle(!c->xiIntegration());
-
-
-    throw std::logic_error("The method or operation is not implemented.");
+    auto* particleLabel = addLabel<ItemWithParticlesCatalog>(parentLabel, p);
+
+    if (enableAbundance)
+        addParameterItem(particleLabel, p->abundance());
+    if (enablePosition)
+        addParameterItem(particleLabel, p->positionVector());
+    addRotation(particleLabel, p);
+
+    if (const auto* particle = dynamic_cast<const ParticleItem*>(p)) {
+        const auto* formFactor = particle->formfactor();
+        auto* ffLabel = addLabel<FormFactorItemCatalog>(particleLabel, "Formfactor", formFactor);
+        for (const auto& d : formFactor->geometryValues())
+            addParameterItem(ffLabel, d);
+    } else if (const auto* particleComposition = dynamic_cast<const ParticleCompositionItem*>(p)) {
+        for (auto* p : particleComposition->particles())
+            addParticle(particleLabel, p, false);
+    } else if (const auto* coreShell = dynamic_cast<const ParticleCoreShellItem*>(p)) {
+        // #baMigration improve: add "Core"/"Shell" to label title
+        addParticle(particleLabel, coreShell->core(), false);
+        addParticle(particleLabel, coreShell->shell(), false, false);
+    } else if (const auto* meso = dynamic_cast<const MesoCrystalItem*>(p)) {
+        addParameterItem(particleLabel, meso->vectorA());
+        addParameterItem(particleLabel, meso->vectorB());
+        addParameterItem(particleLabel, meso->vectorC());
+
+        const auto* outerShape = meso->outerShape().currentItem();
+        auto* ffLabel = addLabel<FormFactorItemCatalog>(particleLabel, "Outer shape", outerShape);
+        for (const auto& d : outerShape->geometryValues())
+            addParameterItem(ffLabel, d);
+
+        // #baMigration improve: add "Basisparticle" to label title
+        addParticle(particleLabel, meso->basisParticle(), false);
+    }
+}
+
+void ParameterTreeBuilder::addLattice(ParameterLabelItem* parentLabel,
+                                      const Interference2DAbstractLatticeItem* itf)
+{
+    const auto* lattice = itf->latticeType().currentItem();
+    auto* latticeLabel = addLabel<Lattice2DItemCatalog>(parentLabel, "Lattice", lattice);
+    for (const auto& d : lattice->geometryValues(!itf->xiIntegration()))
+        addParameterItem(latticeLabel, d);
+}
+
+void ParameterTreeBuilder::addRotation(ParameterLabelItem* parentLabel, ItemWithParticles* p)
+{
+    const auto* r = p->rotationMethod().currentItem();
+    if (!r)
+        return;
+
+    auto* label = addLabel<RotationItemCatalog>(parentLabel, "Rotation", r);
+    for (const auto& d : r->rotationValues())
+        addParameterItem(label, d);
 }
diff --git a/GUI/Model/Job/ParameterTreeUtils.h b/GUI/Model/Job/ParameterTreeUtils.h
index 1ba47e89df7..d708ce92ebc 100644
--- a/GUI/Model/Job/ParameterTreeUtils.h
+++ b/GUI/Model/Job/ParameterTreeUtils.h
@@ -27,6 +27,7 @@ class DoubleDescriptor;
 class VectorDescriptor;
 class ParticleLayoutItem;
 class ItemWithParticles;
+class Interference2DAbstractLatticeItem;
 
 //! The ParameterTreeBuilder contains helper functions to create container
 //! with ParameterItems. The ParameterItem appears in RealTimeView and provides real
@@ -45,13 +46,17 @@ private:
     //! add the job's sample
     void addSample();
     void addInstrument();
-    void addParameterItem(ParameterLabelItem* parent, DoubleDescriptor d);
-    void addParameterItem(ParameterLabelItem* parent, VectorDescriptor d);
+    void addParameterItem(ParameterLabelItem* parent, const DoubleDescriptor& d);
+    void addParameterItem(ParameterLabelItem* parent, const VectorDescriptor& d);
     ParameterContainerItem* parameterContainer();
     bool allowMagneticFields() const;
 
     void addInterference(ParameterLabelItem* layoutLabel, const ParticleLayoutItem* layout);
-    void addParticle(ParameterLabelItem* parentLabel, ItemWithParticles* p);
+    void addParticle(ParameterLabelItem* parentLabel, ItemWithParticles* p, bool enableAbundance,
+                     bool enablePosition = true);
+    void addLattice(ParameterLabelItem* parentLabel, const Interference2DAbstractLatticeItem* itf);
+    void addRotation(ParameterLabelItem* parentLabel, ItemWithParticles* p);
+
 
 private:
     JobItem* m_jobItem;
-- 
GitLab