BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
SampleViewAligner.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/Views/SampleDesigner/SampleViewAligner.cpp
6 //! @brief Implements class SampleViewAligner
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 
20 #include <QModelIndex>
21 
22 namespace {
23 int step_width()
24 {
25  return StyleUtils::SizeOfLetterM().width() * 12.5;
26 }
27 int step_height()
28 {
29  return StyleUtils::SizeOfLetterM().height() * 11;
30 }
31 } // namespace
32 
34 {
35  ASSERT(m_scene);
36 }
37 
38 //! Spring based implified algorithm for smart alignment
40 {
41  m_views.clear();
42  updateViews();
43  updateForces();
44  advance();
45 }
46 
47 //! Forms list of all views which are subject for smart alignment (i.e. views
48 //! which do not have parent view)
49 void SampleViewAligner::updateViews(const QModelIndex& parentIndex)
50 {
51  SampleModel* sampleModel = m_scene->getSampleModel();
52  for (int i_row = 0; i_row < sampleModel->rowCount(parentIndex); ++i_row) {
53  QModelIndex itemIndex = sampleModel->index(i_row, 0, parentIndex);
54  IView* view = getViewForIndex(itemIndex);
55  if (view && !view->parentObject()) {
56  m_views.append(view);
57  }
58  updateViews(itemIndex);
59  }
60 }
61 
62 //! Calculates forces acting on all views for smart alignment
64 {
65  m_viewToPos.clear();
66  for (IView* view : m_views) {
67  calculateForces(view);
68  }
69 }
70 
71 //! Calculates forces acting on single view (simplified force directed spring algorithm)
72 //! and deduce new position of views.
74 {
75  qreal xvel = 0;
76  qreal yvel = 0;
77 
78  // repulsive forces which are pushing items away
79 
80  double weight1(200.0);
81  for (IView* other : m_views) {
82  QPointF vec = view->mapToItem(other, other->boundingRect().center());
83  qreal dx = view->boundingRect().center().x() - vec.x();
84  qreal dy = view->boundingRect().center().y() - vec.y();
85  double l = (dx * dx + dy * dy);
86  if (l > 0) {
87  xvel -= (dx * weight1) / l;
88  yvel -= (dy * weight1) / l;
89  }
90  }
91  // attracting forces which are pulling views together
92  double weight2(100.0);
93  for (IView* other : getConnectedViews(view)) {
94  QPointF vec = view->mapToItem(other, other->boundingRect().center());
95  qreal dx = view->boundingRect().center().x() - vec.x();
96  qreal dy = view->boundingRect().center().y() - vec.y();
97  xvel += dx / weight2;
98  yvel += dy / weight2;
99  }
100  QPointF newPos = view->pos() + QPointF(xvel, yvel);
101  m_viewToPos[view] = newPos;
102 }
103 
104 //! Applies calculated positions to views
106 {
107  for (IView* view : m_views) {
108  view->setPos(m_viewToPos[view]);
109  }
110 }
111 
112 //! Returns list of views connected with given view for the subsequent force calculation.
113 //!
114 //! Weirdness of given function is due to the fact, that, for example, ParticleLayout view
115 //! should interact not with Layer view, but with its parent - MultiLayer view.
116 //! Similarly, MultiLayer is not interacting with its Layers, but directly with the ParticleLayout.
118 {
119  QList<IView*> result;
120 
121  SessionItem* itemOfView = view->getItem();
122 
123  QList<SessionItem*> connected_items;
124 
125  if (itemOfView->parent()->modelType() == "Layer") {
126  // e.g. we are dealing here with ParticleLayout, so we will use directly MultiLayer to
127  // interact with
128  connected_items.append(itemOfView->parent()->parent());
129  } else {
130  connected_items.append(itemOfView->parent());
131  }
132  if (itemOfView->modelType() == "MultiLayer") {
133  // MultiLayer will not interact with its Layers, but with they children, e.g. with
134  // ParticleLayouts
135  for (auto child : itemOfView->children()) {
136  connected_items.append(child->children().toList());
137  }
138  } else {
139  connected_items.append(itemOfView->children().toList());
140  }
141  for (auto item : connected_items) {
142  IView* view = m_scene->getViewForItem(item);
143  if (view) {
144  result.append(view);
145  }
146  }
147  return result;
148 }
149 
150 //! Aligns sample starting from
151 void SampleViewAligner::alignSample(SessionItem* item, QPointF reference, bool force_alignment)
152 {
153  ASSERT(item);
154  alignSample(m_scene->getSampleModel()->indexOfItem(item), reference, force_alignment);
155 }
156 
157 //! Aligns sample starting from reference point.
158 //! If force_alignment=false, view's position will be changed only if it has Null coordinate,
159 //! if force_alignment=true the position will be changed anyway.
160 //! Position of View which has parent item (like Layer) will remain unchainged.
161 void SampleViewAligner::alignSample(const QModelIndex& parentIndex, QPointF reference,
162  bool force_alignment)
163 {
164  SampleModel* sampleModel = m_scene->getSampleModel();
165 
166  if (IView* view = getViewForIndex(parentIndex)) {
167  if ((force_alignment || view->pos().isNull()) && !view->parentObject())
168  view->setPos(reference);
169 
170  if (view->parentObject()) {
171  reference = view->mapToScene(view->pos());
172  } else {
173  reference = view->pos();
174  }
175  }
176  int child_counter = 0;
177  for (int i_row = 0; i_row < sampleModel->rowCount(parentIndex); ++i_row) {
178  QModelIndex itemIndex = sampleModel->index(i_row, 0, parentIndex);
179  if (!getViewForIndex(itemIndex))
180  continue;
181  QPointF child_reference =
182  reference + QPointF(-step_width(), step_height() * child_counter++);
183  alignSample(itemIndex, child_reference, force_alignment);
184  }
185 }
186 
187 IView* SampleViewAligner::getViewForIndex(const QModelIndex& index)
188 {
189  SampleModel* sampleModel = m_scene->getSampleModel();
190  SessionItem* item = sampleModel->itemForIndex(index);
191  return m_scene->getViewForItem(item);
192 }
#define ASSERT(condition)
Definition: Assert.h:31
Defines class DesignerScene.
Defines interface IView.
Defines class SampleModel.
Defines class SampleViewAligner.
DefinesStyleUtils namespace.
Main class which represents SessionModel on graphics scene.
Definition: DesignerScene.h:37
SampleModel * getSampleModel()
Definition: DesignerScene.h:49
IView * getViewForItem(SessionItem *item)
parent class for graphic representation of all ISampleNode's
Definition: IView.h:25
virtual SessionItem * getItem()
Definition: IView.h:59
Main model to hold sample items.
Definition: SampleModel.h:24
void advance()
Applies calculated positions to views.
void updateForces()
Calculates forces acting on all views for smart alignment.
void alignSample(SessionItem *item, QPointF reference={}, bool force_alignment=false)
Aligns sample starting from.
void smartAlign()
Spring based implified algorithm for smart alignment.
void calculateForces(IView *view)
Calculates forces acting on single view (simplified force directed spring algorithm) and deduce new p...
DesignerScene * m_scene
QList< IView * > m_views
list of all views which are subject to smart align
QList< IView * > getConnectedViews(IView *view)
Returns list of views connected with given view for the subsequent force calculation.
void updateViews(const QModelIndex &parentIndex={})
Forms list of all views which are subject for smart alignment (i.e.
QMap< IView *, QPointF > m_viewToPos
IView * getViewForIndex(const QModelIndex &index)
SampleViewAligner(DesignerScene *scene)
SessionItem * parent() const
Returns parent of this item.
Definition: SessionItem.cpp:73
QVector< SessionItem * > children() const
Returns vector of all children.
QString modelType() const
Get model type.
SessionItem * itemForIndex(const QModelIndex &index) const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
virtual int rowCount(const QModelIndex &parent) const
QModelIndex indexOfItem(SessionItem *item) const
QSize SizeOfLetterM(const QWidget *widget=nullptr)
Returns size of largest letter of default system font.
Definition: StyleUtils.cpp:110