File: BuilderUtil.cpp

package info (click to toggle)
bornagain 23.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 103,948 kB
  • sloc: cpp: 423,131; python: 40,997; javascript: 11,167; awk: 630; sh: 318; ruby: 173; xml: 130; makefile: 51; ansic: 24
file content (283 lines) | stat: -rw-r--r-- 11,287 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Img3D/Build/BuilderUtil.cpp
//! @brief     Implements Img3D::BuilderUtils namespace.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "Img3D/Build/BuilderUtil.h"
#include "Base/Const/Units.h"
#include "Base/Util/Assert.h"
#include "Img3D/Build/Particle3DContainer.h"
#include "Img3D/Model/ParticleFromFF.h"
#include "Img3D/Model/Particles.h"
#include "Sample/Lattice/Lattice3D.h"
#include "Sample/Particle/Compound.h"
#include "Sample/Particle/CoreAndShell.h"
#include "Sample/Particle/Crystal.h"
#include "Sample/Particle/IFormfactor.h"
#include "Sample/Particle/Mesocrystal.h"
#include "Sample/Particle/Particle.h"
#include "Sample/Scattering/Rotations.h"

using Img3D::F3;
using Img3D::Particle3DContainer;

namespace {

R3 to_kvector(const F3& origin)
{
    return {static_cast<double>(origin.x()), static_cast<double>(origin.y()),
            static_cast<double>(origin.z())};
}

//! Returns the Euler angles from an IRotation object.
//! The result is encoded as F3, but does not have vector semantics.
Img3D::F3 EulerAngles(const IRotation*& rotation)
{
    double alpha = 0;
    double beta = 0;
    double gamma = 0;

    if (const auto* rotX = dynamic_cast<const RotationX*>(rotation)) {
        beta = rotX->angle(); // about x-axis
    } else if (const auto* rotY = dynamic_cast<const RotationY*>(rotation)) {
        alpha = Units::deg2rad(90);
        beta = rotY->angle(); // about y-axis
        gamma = Units::deg2rad(-90);
    } else if (const auto* rotZ = dynamic_cast<const RotationZ*>(rotation)) {
        alpha = rotZ->angle(); // about z-axis
    } else if (const auto* rotEuler = dynamic_cast<const RotationEuler*>(rotation)) {
        alpha = rotEuler->alpha();
        beta = rotEuler->beta();
        gamma = rotEuler->gamma();
    }
    return Img3D::F3fromR3({alpha, beta, gamma});
}

//! Apply transformations (translation, rotation) to a 3D Particle
//! or to a particle belonging to a Compound
void applyParticleTransformations(const Particle& particle, Img3D::PlotParticle& particle3D,
                                  const R3& origin)
{
    // rotation
    F3 particle_rotate;
    const IRotation* rotation = particle.rotation();

    if (rotation)
        particle_rotate = EulerAngles(rotation);

    // translation
    F3 position = Img3D::F3fromR3(particle.particlePosition() + origin);

    // If the particle belongs to a particle composition, along with the particle's
    // intrinsic transformations, position() and rotation() methods also account for the
    // translation and rotation (if present) of the particle composition as the
    // particleComposition's decompose() method already does this

    particle3D.addTransform(particle_rotate, position);
}

//! Apply transformations (translation, rotation) to a particle (core/shell) in a CoreAndShell
void applyCoreAndShellTransformations(const Particle& particle, Img3D::PlotParticle& particle3D,
                                      const CoreAndShell& particleCoreShell, const R3& origin)
{
    std::unique_ptr<Particle> P_clone(particle.clone()); // clone of the current particle

    // rotation
    F3 particle_rotate;
    const IRotation* rotationCoreShell = particleCoreShell.rotation();

    if (rotationCoreShell)
        P_clone->rotate(*rotationCoreShell);

    const IRotation* rotation = P_clone->rotation();

    if (rotation)
        particle_rotate = EulerAngles(rotation);

    // translation
    R3 positionCoreShell = particleCoreShell.particlePosition();

    P_clone->translate(positionCoreShell);

    F3 position = Img3D::F3fromR3(P_clone->particlePosition() + origin);

    particle3D.transform(particle_rotate, position);
}

} // namespace


Img3D::BuilderUtils::BuilderUtils(std::function<QColor(const QString&)> fnColorFromMaterialName)
    : m_fn_color_from_material_name(fnColorFromMaterialName)
{
    ASSERT(fnColorFromMaterialName);
}

void Img3D::BuilderUtils::applyParticleColor(const Particle& particle,
                                             Img3D::PlotParticle& particle3D, double alpha)
{
    // assign correct color to the particle from the knowledge of its material
    const Material* particle_material = particle.material();
    auto color =
        m_fn_color_from_material_name(QString::fromStdString(particle_material->materialName()));
    color.setAlphaF(alpha);
    particle3D.setColor(color);
}

Particle3DContainer Img3D::BuilderUtils::singleParticle3DContainer(const Particle& particle,
                                                                   double total_abundance,
                                                                   const F3& origin)
{
    std::unique_ptr<Particle> P_clone(particle.clone()); // clone of the particle

    const IFormfactor* ff = P_clone->pFormfactor();

    auto particle3D = Img3D::particle3DfromFF(ff);
    applyParticleTransformations(*P_clone, *particle3D, to_kvector(origin));
    applyParticleColor(*P_clone, *particle3D);

    Particle3DContainer result;
    result.addParticle3D(particle3D.release());
    result.setCumulativeAbundance(P_clone->abundance() / total_abundance);

    return result;
}

Particle3DContainer
Img3D::BuilderUtils::particleCoreShell3DContainer(const CoreAndShell& particleCoreShell,
                                                  double total_abundance, const F3& origin)
{
    // clone of the particleCoreShell
    std::unique_ptr<CoreAndShell> PCS_clone(particleCoreShell.clone());

    const auto* coreff = PCS_clone->coreParticle()->pFormfactor();
    const auto* shellff = PCS_clone->shellParticle()->pFormfactor();

    auto outCore = Img3D::particle3DfromFF(coreff);
    auto outShell = Img3D::particle3DfromFF(shellff);

    // core
    applyCoreAndShellTransformations(*PCS_clone->coreParticle(), *outCore, *PCS_clone,
                                     to_kvector(origin));
    applyParticleColor(*PCS_clone->coreParticle(), *outCore);

    // shell (set an alpha value of 0.5 for transparency)
    applyCoreAndShellTransformations(*PCS_clone->shellParticle(), *outShell, *PCS_clone,
                                     to_kvector(origin));
    applyParticleColor(*PCS_clone->shellParticle(), *outShell, 0.5);

    Particle3DContainer result;

    result.addParticle3D(outCore.release());  // index 0
    result.addParticle3D(outShell.release()); // index 1
    result.setCumulativeAbundance(PCS_clone->abundance() / total_abundance);

    return result;
}

Particle3DContainer
Img3D::BuilderUtils::particleComposition3DContainer(const Compound& particleComposition,
                                                    double total_abundance, const F3& origin)
{
    // clone of the particleComposition
    std::unique_ptr<Compound> PC_clone(particleComposition.clone());

    Particle3DContainer result;

    for (const auto* pc_particle : PC_clone->decompose()) {
        ASSERT(pc_particle);
        Particle3DContainer particle3DContainer;
        if (const auto* p = dynamic_cast<const CoreAndShell*>(pc_particle)) {
            particle3DContainer = particleCoreShell3DContainer(*p, 1.0, origin);
        } else if (dynamic_cast<const Mesocrystal*>(pc_particle)) {
            // TODO: Implement method to populate Mesocrystal from CORE and NOT from MesocrystalItem
            // as it is done currently in BuilderUtils::mesocrystal3DContainer
            throw std::runtime_error("Mesocrystal inside compound particle is not supported");
        } else if (const auto* p = dynamic_cast<const Particle*>(pc_particle))
            particle3DContainer = singleParticle3DContainer(*p, 1.0, origin);
        else
            ASSERT_NEVER;

        // keep result flat
        for (size_t i = 0; i < particle3DContainer.containerSize(); ++i) {
            result.addParticle3D(particle3DContainer.createParticle(i).release());
        }
    }

    // set the correct abundance for the entire Compound
    // (no abundances are associated with the individual components of Compound)
    result.setCumulativeAbundance(PC_clone->abundance() / total_abundance);

    return result;
}

Particle3DContainer Img3D::BuilderUtils::mesocrystal3DContainer(Mesocrystal* const mesocrystal,
                                                                double total_abundance,
                                                                const F3& origin)
{
    std::unique_ptr<Mesocrystal> M_clone(mesocrystal->clone()); // clone of the mesocrystal

    // These methods DO NOT add rotation/translation of the mesocrystal to its children
    // and hence they need to be added manually
    const auto* particleBasis = mesocrystal->particleStructure().basis();
    const auto* outerShapeff = mesocrystal->outerShape();

    const auto* mesocrystal_rotation = M_clone->rotation();
    const auto mesocrystal_translation = Img3D::F3fromR3(M_clone->particlePosition());

    Particle3DContainer outBasis;

    if (const auto* p = dynamic_cast<const Compound*>(particleBasis)) {
        outBasis = particleComposition3DContainer(*p, 1.0, origin);

    } else if (const auto* p = dynamic_cast<const CoreAndShell*>(particleBasis)) {
        outBasis = particleCoreShell3DContainer(*p, 1.0, origin);

    } else if (dynamic_cast<const Mesocrystal*>(particleBasis)) {
        // TODO: Implement method to populate Mesocrystal from CORE and NOT from MesocrystalItem
        // as it is done currently in Img3D::BuilderUtils::mesocrystal3DContainer
        throw std::runtime_error("Mesocrystal inside mesocrystal is not supported");

    } else if (const auto* p = dynamic_cast<const Particle*>(particleBasis)) {
        outBasis = singleParticle3DContainer(*p, 1.0, origin);

    } else
        ASSERT_NEVER;

    Particle3DContainer result;

    // Add inner particles for visualization
    std::vector<R3> basisPositions = mesocrystal->calcBasisPositions();
    for (const R3& position : basisPositions)
        for (size_t it = 0; it < outBasis.containerSize(); ++it) {
            auto particle3D = outBasis.createParticle(it);
            particle3D->addTranslation(Img3D::F3fromR3(position));
            particle3D->addExtrinsicRotation(EulerAngles(mesocrystal_rotation));
            particle3D->addTranslation(mesocrystal_translation);
            result.addParticle3D(particle3D.release());
        }

    // Add outer shape for visualization
    auto outerShape3D = Img3D::particle3DfromFF(outerShapeff);
    outerShape3D->addTransform(EulerAngles(mesocrystal_rotation), mesocrystal_translation + origin);

    // assign grey (default) color to the outer shape
    QColor color = {};
    color.setAlphaF(0.3);
    outerShape3D->setColor(color);
    result.addParticle3D(outerShape3D.release());

    // set the correct abundance for the Mesocrystal
    result.setCumulativeAbundance(M_clone->abundance() / total_abundance);

    return result;
}