BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
RealSpaceMesoCrystalUtils.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/Realspace/RealSpaceMesoCrystalUtils.cpp
6 //! @brief Defines GUI::RealSpace::BuilderUtils namespace
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
16 #include "Base/Const/Units.h"
22 #include "Sample/HardParticle/HardParticles.h"
23 #include "Sample/Particle/MesoCrystal.h"
24 #include "Sample/Particle/Particle.h"
25 #include "Sample/Particle/ParticleCoreShell.h"
26 
27 namespace {
28 
29 const int n = 10; // TODO: Adjust this parameter based on the size of the mesocrystal
30 
31 bool isPositionInsideMesoCrystal(const IFormFactor* outerShape, R3 positionInside)
32 {
33  bool check(false);
34  if (const auto* ff_Pyramid2 = dynamic_cast<const Pyramid2*>(outerShape)) {
35  double L = ff_Pyramid2->length();
36  double W = ff_Pyramid2->width();
37  double H = ff_Pyramid2->height();
38  double alpha = ff_Pyramid2->alpha();
39 
40  double l_z =
41  L / 2
42  - positionInside.z() / std::tan(alpha); // half-length of rectangle at a given height
43  double w_z =
44  W / 2
45  - positionInside.z() / std::tan(alpha); // half-width of rectangle at a given height
46  if (std::abs(positionInside.x()) <= l_z && std::abs(positionInside.y()) <= w_z
47  && (positionInside.z() >= 0 && positionInside.z() <= H))
48  check = true;
49  } else if (const auto* ff_BarGauss = dynamic_cast<const BarGauss*>(outerShape)) {
50  double L = ff_BarGauss->length();
51  double W = ff_BarGauss->width();
52  double H = ff_BarGauss->height();
53 
54  if (std::abs(positionInside.x()) <= L / 2 && std::abs(positionInside.y()) <= W / 2
55  && (positionInside.z() >= 0 && positionInside.z() <= H))
56  check = true;
57  } else if (const auto* ff_BarLorentz = dynamic_cast<const BarLorentz*>(outerShape)) {
58  double L = ff_BarLorentz->length();
59  double W = ff_BarLorentz->width();
60  double H = ff_BarLorentz->height();
61 
62  if (std::abs(positionInside.x()) <= L / 2 && std::abs(positionInside.y()) <= W / 2
63  && (positionInside.z() >= 0 && positionInside.z() <= H))
64  check = true;
65  } else if (const auto* ff_Box = dynamic_cast<const Box*>(outerShape)) {
66  double L = ff_Box->length();
67  double W = ff_Box->width();
68  double H = ff_Box->height();
69 
70  if (std::abs(positionInside.x()) <= L / 2 && std::abs(positionInside.y()) <= W / 2
71  && (positionInside.z() >= 0 && positionInside.z() <= H))
72  check = true;
73  } else if (const auto* ff_Cone = dynamic_cast<const Cone*>(outerShape)) {
74  double R = ff_Cone->radius();
75  double H = ff_Cone->height();
76  double alpha = ff_Cone->alpha();
77 
78  if (std::abs(positionInside.x()) > R || std::abs(positionInside.y()) > R
79  || positionInside.z() < 0 || positionInside.z() > H)
80  return check;
81 
82  double R_z = R - positionInside.z() / std::tan(alpha);
83  if (std::pow(positionInside.x() / R_z, 2) + std::pow(positionInside.y() / R_z, 2) <= 1)
84  check = true;
85  } else if (const auto* ff_Pyramid6 = dynamic_cast<const Pyramid6*>(outerShape)) {
86  double B = ff_Pyramid6->baseEdge();
87  double H = ff_Pyramid6->height();
88  double alpha = ff_Pyramid6->alpha();
89 
90  if (std::abs(positionInside.x()) > B || std::abs(positionInside.y()) > B
91  || positionInside.z() < 0 || positionInside.z() > H)
92  return check;
93 
94  double l_z = B - positionInside.z() / std::tan(alpha); // edge of hexagon at a given height
95  double theta_prime = 0; // angle between positionInside & x-axis in positionInside.z() plane
96  if (positionInside.x() != 0 || positionInside.y() != 0)
97  theta_prime = Units::rad2deg(std::asin(
98  std::abs(positionInside.y())
99  / std::sqrt(std::pow(positionInside.x(), 2) + std::pow(positionInside.y(), 2))));
100  int c = static_cast<int>(theta_prime / 60); // multiplication constant
101  double theta = Units::deg2rad(theta_prime - c * 60);
102  double k_z = l_z / (std::cos(theta) + std::sin(theta) / std::tan(M_PI / 3));
103 
104  if (std::pow(positionInside.x(), 2) + std::pow(positionInside.y(), 2) <= std::pow(k_z, 2))
105  check = true;
106  } else if (const auto* ff_Bipyramid4 = dynamic_cast<const Bipyramid4*>(outerShape)) {
107  double L = ff_Bipyramid4->length();
108  double H = ff_Bipyramid4->height();
109  double rH = ff_Bipyramid4->heightRatio();
110  double alpha = ff_Bipyramid4->alpha();
111 
112  double total_Height = H + rH * H;
113 
114  if (std::abs(positionInside.x()) > L / 2 || std::abs(positionInside.y()) > L / 2
115  || positionInside.z() < 0 || positionInside.z() > total_Height)
116  return check;
117 
118  // half-length of square (i.e. horizontal cross-section of Bipyramid4) at a given height
119  double l_z = L / 2 - std::abs(H - positionInside.z()) / std::tan(alpha);
120  if (std::abs(positionInside.x()) <= l_z && std::abs(positionInside.y()) <= l_z)
121  check = true;
122  } else if (const auto* ff_Cylinder = dynamic_cast<const Cylinder*>(outerShape)) {
123  double R = ff_Cylinder->radius();
124  double H = ff_Cylinder->height();
125 
126  if (std::abs(positionInside.x()) > R || std::abs(positionInside.y()) > R
127  || positionInside.z() < 0 || positionInside.z() > H)
128  return check;
129 
130  if (std::pow(positionInside.x() / R, 2) + std::pow(positionInside.y() / R, 2) <= 1)
131  check = true;
132  } else if (dynamic_cast<const Dodecahedron*>(outerShape)) {
133  // TODO: Implement Dodecahedron
134  std::ostringstream ostr;
135  ostr << "Sorry, outer shape Dodecahedron not yet implemented for Mesocrystal";
136  ostr << "\n\nStay tuned!";
137  throw std::runtime_error(ostr.str());
138  } else if (const auto* ff_EllipsoidalCylinder =
139  dynamic_cast<const EllipsoidalCylinder*>(outerShape)) {
140  double a = ff_EllipsoidalCylinder->radiusX(); // semi-axis length along x
141  double b = ff_EllipsoidalCylinder->radiusY(); // semi-axis length along y
142  double H = ff_EllipsoidalCylinder->height();
143 
144  if (std::abs(positionInside.x()) > a || std::abs(positionInside.y()) > b
145  || positionInside.z() < 0 || positionInside.z() > H)
146  return check;
147 
148  if (std::pow(positionInside.x() / a, 2) + std::pow(positionInside.y() / b, 2) <= 1)
149  check = true;
150  } else if (const auto* ff_Sphere = dynamic_cast<const Sphere*>(outerShape)) {
151  double R = ff_Sphere->radius();
152 
153  if (std::abs(positionInside.x()) > R || std::abs(positionInside.y()) > R
154  || positionInside.z() < 0 || positionInside.z() > 2 * R)
155  return check;
156 
157  if (std::pow(positionInside.x() / R, 2) + std::pow(positionInside.y() / R, 2)
158  + std::pow((positionInside.z() - R) / R, 2)
159  <= 1)
160  check = true;
161  } else if (const auto* ff_Spheroid = dynamic_cast<const Spheroid*>(outerShape)) {
162  double a = ff_Spheroid->radius(); // semi-axis length along x and y
163  double H = ff_Spheroid->height();
164  double c = H / 2; // semi-axis length along z
165 
166  if (std::abs(positionInside.x()) > a || std::abs(positionInside.y()) > a
167  || positionInside.z() < 0 || positionInside.z() > H)
168  return check;
169 
170  if (std::pow(positionInside.x() / a, 2) + std::pow(positionInside.y() / a, 2)
171  + std::pow((positionInside.z() - c) / c, 2)
172  <= 1)
173  check = true;
174  } else if (const auto* ff_HemiEllipsoid = dynamic_cast<const HemiEllipsoid*>(outerShape)) {
175  double a = ff_HemiEllipsoid->radiusX(); // semi-axis length along x
176  double b = ff_HemiEllipsoid->radiusY(); // semi-axis length along y
177  double c = ff_HemiEllipsoid->height(); // semi-axis length along z
178 
179  if (std::abs(positionInside.x()) > a || std::abs(positionInside.y()) > b
180  || positionInside.z() < 0 || positionInside.z() > c)
181  return check;
182 
183  if (std::pow(positionInside.x() / a, 2) + std::pow(positionInside.y() / b, 2)
184  + std::pow(positionInside.z() / c, 2)
185  <= 1)
186  check = true;
187  } else if (dynamic_cast<const Icosahedron*>(outerShape)) {
188  // TODO: Implement Icosahedron
189  std::ostringstream ostr;
190  ostr << "Sorry, outer shape Icosahedron not yet implemented for Mesocrystal";
191  ostr << "\n\nStay tuned!";
192  throw std::runtime_error(ostr.str());
193  } else if (const auto* ff_Prism3 = dynamic_cast<const Prism3*>(outerShape)) {
194  double B = ff_Prism3->baseEdge();
195  double H = ff_Prism3->height();
196 
197  double l = B * std::sin(M_PI / 3);
198  double x_shift = B / 2 * std::tan(M_PI / 6);
199 
200  if (positionInside.x() + x_shift < 0 || positionInside.x() + x_shift > l
201  || std::abs(positionInside.y()) > B / 2 || positionInside.z() < 0
202  || positionInside.z() > H)
203  return check;
204 
205  double theta = 0; // angle between positionInside & x-axis in positionInside.z() plane
206  if (positionInside.x() + x_shift != 0 || positionInside.y() != 0)
207  theta = std::asin(std::abs(positionInside.y())
208  / std::sqrt(std::pow(positionInside.x() + x_shift, 2)
209  + std::pow(positionInside.y(), 2)));
210 
211  double k = l / (std::sin(theta) / std::tan(M_PI / 6) + std::cos(theta));
212 
213  if (std::pow(positionInside.x() + x_shift, 2) + std::pow(positionInside.y(), 2)
214  <= std::pow(k, 2))
215  check = true;
216  } else if (const auto* ff_Prism6 = dynamic_cast<const Prism6*>(outerShape)) {
217  double B = ff_Prism6->baseEdge();
218  double H = ff_Prism6->height();
219 
220  if (std::abs(positionInside.x()) > B || std::abs(positionInside.y()) > B
221  || positionInside.z() < 0 || positionInside.z() > H)
222  return check;
223 
224  double theta_prime = 0; // angle between positionInside & x-axis in positionInside.z() plane
225  if (positionInside.x() != 0 || positionInside.y() != 0)
226  theta_prime = Units::rad2deg(std::asin(
227  std::abs(positionInside.y())
228  / std::sqrt(std::pow(positionInside.x(), 2) + std::pow(positionInside.y(), 2))));
229  int c = static_cast<int>(theta_prime / 60); // multiplicative constant
230  double theta = Units::deg2rad(theta_prime - c * 60);
231  double k_z = B / (std::cos(theta) + std::sin(theta) / std::tan(M_PI / 3));
232 
233  if (std::pow(positionInside.x(), 2) + std::pow(positionInside.y(), 2) <= std::pow(k_z, 2))
234  check = true;
235  } else if (const auto* ff_Pyramid4 = dynamic_cast<const Pyramid4*>(outerShape)) {
236  double B = ff_Pyramid4->baseEdge();
237  double H = ff_Pyramid4->height();
238  double alpha = ff_Pyramid4->alpha();
239 
240  double l_z =
241  B / 2 - positionInside.z() / std::tan(alpha); // half-length of square at a given height
242  if (std::abs(positionInside.x()) <= l_z && std::abs(positionInside.y()) <= l_z
243  && (positionInside.z() >= 0 && positionInside.z() <= H))
244  check = true;
245  } else if (dynamic_cast<const CosineRippleBox*>(outerShape)) {
246  // TODO: Implement CosineRippleBox
247  std::ostringstream ostr;
248  ostr << "Sorry, outer shape CosineRippleBox not yet implemented for Mesocrystal";
249  ostr << "\n\nStay tuned!";
250  throw std::runtime_error(ostr.str());
251  } else if (dynamic_cast<const CosineRippleGauss*>(outerShape)) {
252  // TODO: Implement CosineRippleGauss
253  std::ostringstream ostr;
254  ostr << "Sorry, outer shape CosineRippleGauss not yet implemented for Mesocrystal";
255  ostr << "\n\nStay tuned!";
256  throw std::runtime_error(ostr.str());
257  } else if (dynamic_cast<const CosineRippleLorentz*>(outerShape)) {
258  // TODO: Implement CosineRippleLorentz
259  std::ostringstream ostr;
260  ostr << "Sorry, outer shape CosineRippleLorentz not yet implemented for Mesocrystal";
261  ostr << "\n\nStay tuned!";
262  throw std::runtime_error(ostr.str());
263  } else if (dynamic_cast<const SawtoothRippleBox*>(outerShape)) {
264  // TODO: Implement SawtoothRippleBox
265  std::ostringstream ostr;
266  ostr << "Sorry, outer shape SawtoothRippleBox not yet implemented for Mesocrystal";
267  ostr << "\n\nStay tuned!";
268  throw std::runtime_error(ostr.str());
269  } else if (dynamic_cast<const SawtoothRippleGauss*>(outerShape)) {
270  // TODO: Implement SawtoothRippleGauss
271  std::ostringstream ostr;
272  ostr << "Sorry, outer shape SawtoothRippleGauss not yet implemented for Mesocrystal";
273  ostr << "\n\nStay tuned!";
274  throw std::runtime_error(ostr.str());
275  } else if (dynamic_cast<const SawtoothRippleLorentz*>(outerShape)) {
276  // TODO: Implement SawtoothRippleLorentz
277  std::ostringstream ostr;
278  ostr << "Sorry, outer shape SawtoothRippleLorentz not yet implemented for Mesocrystal";
279  ostr << "\n\nStay tuned!";
280  throw std::runtime_error(ostr.str());
281  } else if (const auto* ff_Pyramid3 = dynamic_cast<const Pyramid3*>(outerShape)) {
282  double B = ff_Pyramid3->baseEdge();
283  double H = ff_Pyramid3->height();
284  double alpha = ff_Pyramid3->alpha();
285 
286  double B_z =
287  B - positionInside.z() * 2 / std::tan(alpha); // edge of triangle at a given height
288 
289  double l = B_z * std::sin(M_PI / 3);
290  double x_shift = B_z / 2 * std::tan(M_PI / 6);
291 
292  if (positionInside.x() + x_shift < 0 || positionInside.x() + x_shift > l
293  || std::abs(positionInside.y()) > B_z / 2 || positionInside.z() < 0
294  || positionInside.z() > H)
295  return check;
296 
297  double theta = 0; // angle between positionInside & x-axis in positionInside.z() plane
298  if (positionInside.x() + x_shift != 0 || positionInside.y() != 0)
299  theta = std::asin(std::abs(positionInside.y())
300  / std::sqrt(std::pow(positionInside.x() + x_shift, 2)
301  + std::pow(positionInside.y(), 2)));
302 
303  double k = l / (std::sin(theta) / std::tan(M_PI / 6) + std::cos(theta));
304 
305  if (std::pow(positionInside.x() + x_shift, 2) + std::pow(positionInside.y(), 2)
306  <= std::pow(k, 2))
307  check = true;
308  } else if (dynamic_cast<const TruncatedCube*>(outerShape)) {
309  // TODO: Implement Truncated cube
310  std::ostringstream ostr;
311  ostr << "Sorry, outer shape Truncated cube not yet implemented for Mesocrystal";
312  ostr << "\n\nStay tuned!";
313  throw std::runtime_error(ostr.str());
314  } else if (const auto* ff_TruncatedSphere = dynamic_cast<const TruncatedSphere*>(outerShape)) {
315  double R = ff_TruncatedSphere->radius();
316  double H = ff_TruncatedSphere->height();
317  double deltaH = ff_TruncatedSphere->removedTop();
318  if (std::abs(positionInside.x()) > R || std::abs(positionInside.y()) > R
319  || positionInside.z() < 0 || positionInside.z() > (H - deltaH))
320  return check;
321 
322  if (std::pow(positionInside.x() / R, 2) + std::pow(positionInside.y() / R, 2)
323  + std::pow((positionInside.z() - (H - R)) / R, 2)
324  <= 1)
325  check = true;
326  } else if (dynamic_cast<const TruncatedSpheroid*>(outerShape)) {
327  // TODO: Implement Truncated spheroid
328  std::ostringstream ostr;
329  ostr << "Sorry, outer shape Truncated spheroid not yet implemented for Mesocrystal";
330  ostr << "\n\nStay tuned!";
331  throw std::runtime_error(ostr.str());
332  } else if (dynamic_cast<const CantellatedCube*>(outerShape)) {
333  // TODO: Implement Cantellated cube
334  std::ostringstream ostr;
335  ostr << "Sorry, outer shape Cantellated cube not yet implemented for Mesocrystal";
336  ostr << "\n\nStay tuned!";
337  throw std::runtime_error(ostr.str());
338  } else if (dynamic_cast<const HorizontalCylinder*>(outerShape)) {
339  // TODO: Implement Horizontal cylinder
340  std::ostringstream ostr;
341  ostr << "Sorry, outer shape Horizontal cylinder not yet implemented for Mesocrystal";
342  ostr << "\n\nStay tuned!";
343  throw std::runtime_error(ostr.str());
344  } else if (dynamic_cast<const PlatonicOctahedron*>(outerShape)) {
345  // TODO: Implement Platonic octahedron
346  std::ostringstream ostr;
347  ostr << "Sorry, outer shape Platonic octahedron not yet implemented for Mesocrystal";
348  ostr << "\n\nStay tuned!";
349  throw std::runtime_error(ostr.str());
350  } else if (dynamic_cast<const PlatonicTetrahedron*>(outerShape)) {
351  // TODO: Implement Platonic tetrahedron
352  std::ostringstream ostr;
353  ostr << "Sorry, outer shape Platonic tetrahedron not yet implemented for Mesocrystal";
354  ostr << "\n\nStay tuned!";
355  throw std::runtime_error(ostr.str());
356  }
357  return check;
358 }
359 
360 } // namespace
361 
362 
364  const MesoCrystalItem* mesoCrystalItem, double total_abundance, const QVector3D& origin,
365  std::function<QColor(const QString&)> fnColorFromMaterialName)
366 {
367  m_mesoCrystalItem = mesoCrystalItem;
368  m_total_abundance = total_abundance;
369  m_origin = origin;
370  m_builderUtils = std::make_unique<GUI::RealSpace::BuilderUtils>(fnColorFromMaterialName);
371 }
372 
374 {
375  auto mesoCrystal = m_mesoCrystalItem->createMesoCrystal();
376 
377  std::unique_ptr<MesoCrystal> M_clone(mesoCrystal->clone()); // clone of the mesoCrystal
378 
379  // These methods DO NOT add rotation/translation of the mesoCrystal to its children
380  // and hence they need to be added manually
381  auto lattice = m_mesoCrystalItem->getLattice();
382  auto particleBasis = m_mesoCrystalItem->getBasis();
383  auto outerShapeff = m_mesoCrystalItem->getOuterShape();
384 
385  const auto* mesoCrystal_rotation = M_clone->rotation();
386  auto mesoCrystal_translation = M_clone->particlePosition();
387 
388  Particle3DContainer mesoCrystalBasis3DContainer;
389 
390  if (dynamic_cast<const ParticleComposition*>(particleBasis.get())) {
391  const auto* particleComposition =
392  dynamic_cast<const ParticleComposition*>(particleBasis.get());
393  mesoCrystalBasis3DContainer =
394  m_builderUtils->particleComposition3DContainer(*particleComposition, 1.0, m_origin);
395  } else if (dynamic_cast<const ParticleCoreShell*>(particleBasis.get())) {
396  const auto* particleCoreShell = dynamic_cast<const ParticleCoreShell*>(particleBasis.get());
397  mesoCrystalBasis3DContainer =
398  m_builderUtils->particleCoreShell3DContainer(*particleCoreShell, 1.0, m_origin);
399  } else if (dynamic_cast<const MesoCrystal*>(particleBasis.get())) {
400  // TODO: Implement method to populate MesoCrystal from CORE and NOT from MesoCrystalItem
401  // as it is done currently in GUI::RealSpace::BuilderUtils::mesoCrystal3DContainer
402  std::ostringstream ostr;
403  ostr << "Sorry, MesoCrystal inside MesoCrystal not yet implemented";
404  ostr << "\n\nStay tuned!";
405  throw std::runtime_error(ostr.str());
406  } else {
407  const auto* particle = dynamic_cast<const Particle*>(particleBasis.get());
408  mesoCrystalBasis3DContainer =
409  m_builderUtils->singleParticle3DContainer(*particle, 1.0, m_origin);
410  }
411 
412  Particle3DContainer mesoCrystal3DContainer;
413 
414  for (int k = -n; k <= n; k++) {
415  for (int j = -n; j <= n; j++) {
416  for (int i = -n; i <= n; i++) {
417  auto positionInside = i * lattice.basisVectorA() + j * lattice.basisVectorB()
418  + k * lattice.basisVectorC();
419 
420  if (isPositionInsideMesoCrystal(outerShapeff.get(), positionInside)) {
421  for (size_t it = 0; it < mesoCrystalBasis3DContainer.containerSize(); ++it) {
422  auto particle3D = mesoCrystalBasis3DContainer.createParticle(it);
423  particle3D->addTranslation(
424  QVector3D(static_cast<float>(positionInside.x()),
425  static_cast<float>(positionInside.y()),
426  static_cast<float>(positionInside.z())));
427 
428  particle3D->addExtrinsicRotation(
429  m_builderUtils->implementParticleRotationfromIRotation(
430  mesoCrystal_rotation));
431 
432  particle3D->addTranslation(
433  QVector3D(static_cast<float>(mesoCrystal_translation.x()),
434  static_cast<float>(mesoCrystal_translation.y()),
435  static_cast<float>(mesoCrystal_translation.z())));
436 
437  mesoCrystal3DContainer.addParticle(
438  particle3D.release(), mesoCrystalBasis3DContainer.particle3DBlend(it));
439  }
440  }
441  }
442  }
443  }
444 
445  // Add outer shape for visualization
446  auto outerShape3D = GUI::View::TransformTo3D::createParticlefromFormfactor(outerShapeff.get());
447  outerShape3D->addTransform(
448  m_builderUtils->implementParticleRotationfromIRotation(mesoCrystal_rotation),
449  QVector3D(static_cast<float>(mesoCrystal_translation.x()),
450  static_cast<float>(mesoCrystal_translation.y()),
451  static_cast<float>(mesoCrystal_translation.z())));
452 
453  // assign grey (default) color to the outer shape
454  QColor color = {};
455  color.setAlphaF(0.3);
456  outerShape3D->color = color;
457  mesoCrystal3DContainer.addParticle(outerShape3D.release(), true);
458 
459  // set the correct abundance for the MesoCrystal
460  mesoCrystal3DContainer.setCumulativeAbundance(M_clone->abundance() / m_total_abundance);
461  mesoCrystal3DContainer.setParticleType("MesoCrystal");
462 
463  return mesoCrystal3DContainer;
464 }
Defines class MesoCrystalItem.
Implements namespace GUI::View::TransformTo3D.
Defines class ParticleCompositionItem.
Defines GUI::RealSpace::BuilderUtils namespace.
Defines GUI::RealSpace::BuilderUtils namespace.
Defines namespace GUI::View::TransformTo3D.
std::unique_ptr< IFormFactor > getOuterShape() const
std::unique_ptr< MesoCrystal > createMesoCrystal() const
std::unique_ptr< IParticle > getBasis() const
Lattice3D getLattice() const
std::unique_ptr< GUI::RealSpace::Particles::Particle > createParticle(const size_t &index) const
void setCumulativeAbundance(double cumulativeAbundance)
void addParticle(GUI::RealSpace::Particles::Particle *particle3D, bool blend)
size_t containerSize() const
void setParticleType(QString particleType)
bool particle3DBlend(const size_t &index) const
std::unique_ptr< GUI::RealSpace::BuilderUtils > m_builderUtils
RealSpaceMesoCrystal(const MesoCrystalItem *mesoCrystalItem, double total_abundance, const QVector3D &origin, std::function< QColor(const QString &)> fnColorFromMaterialName)
Particle3DContainer populateMesoCrystal()
const MesoCrystalItem * m_mesoCrystalItem
std::unique_ptr< GUI::RealSpace::Particles::Particle > createParticlefromFormfactor(const IFormFactor *ff)