Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
PeriodicImagesWidget.h
Go to the documentation of this file.
1/* This file is part of MoDELib, distributed under GNU GPL v2. */
2#ifndef model_PeriodicImagesWidget_H_
3#define model_PeriodicImagesWidget_H_
4
5#include <DDqtControls.h>
6#include <QSpinBox>
7#include <Eigen/Dense>
8#include <vtkActor.h>
9#include <vtkCallbackCommand.h>
10#include <vtkCommand.h>
11#include <vtkGenericOpenGLRenderWindow.h>
12#include <vtkMatrix4x4.h>
13#include <vtkPropCollection.h>
14#include <vtkRenderer.h>
15#include <vtkSmartPointer.h>
16#include <array>
17#include <map>
18#include <set>
19#include <vector>
20
21namespace model
22{
23 // Synchronize immediately before rendering, including actors added or removed
24 // by Fields controls. Each copy shares its source mapper and properties.
26 {
27 struct Images
28 {
29 vtkSmartPointer<vtkActor> source;
30 std::vector<vtkSmartPointer<vtkActor>> copies;
31 };
32 vtkRenderer* renderer;
33 vtkSmartPointer<vtkCallbackCommand> callback;
34 unsigned long observer = 0;
35 std::map<vtkActor*, Images> images;
36 std::vector<Eigen::Vector3d> translations;
37
38 public:
39 explicit PeriodicImages(vtkRenderer* rendererIn) : renderer(rendererIn),
40 callback(vtkSmartPointer<vtkCallbackCommand>::New())
41 {
42 callback->SetClientData(this);
43 callback->SetCallback([](vtkObject*, unsigned long, void* data, void*)
44 {
45 static_cast<PeriodicImages*>(data)->synchronize();
46 });
47 observer = renderer->AddObserver(vtkCommand::StartEvent, callback);
48 }
49
51 {
52 renderer->RemoveObserver(observer);
53 clear();
54 }
57
58 void clear()
59 {
60 for(auto& entry : images)
61 {
62 for(auto& copy : entry.second.copies)
63 {
64 renderer->RemoveActor(copy);
65 }
66 }
67 images.clear();
68 }
69
70 void setTranslations(std::vector<Eigen::Vector3d> values)
71 {
72 clear();
73 translations = std::move(values);
75 }
76
78 {
79 if(translations.empty())
80 {
81 return;
82 }
83 std::set<vtkActor*> copies;
84 for(const auto& entry : images)
85 {
86 for(const auto& copy : entry.second.copies)
87 {
88 copies.insert(copy);
89 }
90 }
91 std::set<vtkActor*> sources;
92 auto* props = renderer->GetViewProps();
93 vtkCollectionSimpleIterator iterator;
94 props->InitTraversal(iterator);
95 while(auto* prop = props->GetNextProp(iterator))
96 {
97 auto* actor = vtkActor::SafeDownCast(prop);
98 // Overlays, labels and coordinate axes are not periodic geometry.
99 if(actor && actor->GetMapper() && !actor->IsA("vtkCubeAxesActor") && !copies.count(actor))
100 {
101 sources.insert(actor);
102 }
103 }
104 for(auto it = images.begin(); it != images.end();)
105 {
106 if(!sources.count(it->first))
107 {
108 for(auto& copy : it->second.copies)
109 {
110 renderer->RemoveActor(copy);
111 }
112 it = images.erase(it);
113 }
114 else
115 {
116 ++it;
117 }
118 }
119 for(auto* source : sources)
120 {
121 auto& entry = images[source];
122 entry.source = source;
123 if(entry.copies.empty())
124 {
125 for(size_t i = 0; i < translations.size(); ++i)
126 {
127 auto copy = vtkSmartPointer<vtkActor>::New();
128 renderer->AddActor(copy);
129 entry.copies.push_back(copy);
130 }
131 }
132 for(size_t i = 0; i < translations.size(); ++i)
133 {
134 auto& copy = entry.copies[i];
135 copy->ShallowCopy(source);
136 // Compose translation after the complete source transform.
137 auto matrix = vtkSmartPointer<vtkMatrix4x4>::New();
138 matrix->DeepCopy(source->GetMatrix());
139 for(int row = 0; row < 3; ++row)
140 {
141 matrix->SetElement(row, 3, matrix->GetElement(row, 3) + translations[i][row]);
142 }
143 copy->SetOrigin(0, 0, 0);
144 copy->SetPosition(0, 0, 0);
145 copy->SetOrientation(0, 0, 0);
146 copy->SetScale(1, 1, 1);
147 copy->SetUserMatrix(matrix);
148 // Picking must continue to refer to original simulation entities.
149 copy->PickableOff();
150 }
151 }
152 }
153 };
154
155 class PeriodicImagesWidget : public QWidget
156 {
157 const Eigen::Matrix<double, 3, Eigen::Dynamic> basis;
158 PeriodicImages images;
159 vtkGenericOpenGLRenderWindow* window;
160 vtkRenderer* renderer;
161 QGroupBox* group;
162 std::array<QSpinBox*, 3> counts{{nullptr, nullptr, nullptr}};
163
164 void updateImages()
165 {
166 std::vector<Eigen::Vector3d> shifts;
167 if(group->isChecked())
168 {
169 std::array<int, 3> n{{0, 0, 0}};
170 for(int i = 0; i < basis.cols(); ++i)
171 {
172 n[i] = counts[i]->value();
173 }
174 for(int i = -n[0]; i <= n[0]; ++i)
175 {
176 for(int j = -n[1]; j <= n[1]; ++j)
177 {
178 for(int k = -n[2]; k <= n[2]; ++k)
179 {
180 if(i != 0 || j != 0 || k != 0)
181 {
182 Eigen::Vector3d shift = Eigen::Vector3d::Zero();
183 const std::array<int, 3> indices{{i, j, k}};
184 for(int axis = 0; axis < basis.cols(); ++axis)
185 {
186 shift += indices[axis] * basis.col(axis);
187 }
188 shifts.push_back(shift);
189 }
190 }
191 }
192 }
193 }
194 images.setTranslations(std::move(shifts));
195 renderer->ResetCameraClippingRange();
196 if(window->GetReadyForRendering())
197 {
198 window->Render();
199 }
200 }
201
202 public:
203 PeriodicImagesWidget(vtkGenericOpenGLRenderWindow* windowIn, vtkRenderer* rendererIn,
204 const Eigen::Matrix<double, 3, Eigen::Dynamic>& basisIn, QWidget* parent) :
205 QWidget(parent), basis(basisIn), images(rendererIn), window(windowIn), renderer(rendererIn),
206 group(controls::box("Periodic images", this,
207 "Display translated copies of the spatial actors along the active periodicity vectors. Fields and clipping are copied from the original cell, without recomputing physics."))
208 {
209 auto* layout = new QVBoxLayout(this);
210 layout->setContentsMargins(0, 0, 0, 0);
211 layout->addWidget(group);
212 group->setObjectName("periodicImagesGroup");
213 group->setCheckable(true);
214 group->setChecked(false);
215 group->setEnabled(basis.cols() > 0);
216 for(int i = 0; i < basis.cols(); ++i)
217 {
218 counts[i] = new QSpinBox(group);
219 counts[i]->setObjectName(QString("periodicImageCount%1").arg(i + 1));
220 counts[i]->setRange(0, 5);
221 counts[i]->setKeyboardTracking(false);
223 QString("v%1: (%2, %3, %4)").arg(i + 1).arg(basis(0, i), 0, 'g', 5)
224 .arg(basis(1, i), 0, 'g', 5).arg(basis(2, i), 0, 'g', 5),
225 counts[i], "Additional cells on each side along this vector (shown in simulation length units). 0: original only; 1: indices -1, 0, +1. Counts combine across directions. Maximum 5 per side.");
226 connect(counts[i], &QSpinBox::valueChanged, this, [this](int)
227 {
228 updateImages();
229 });
230 }
231 connect(group, &QGroupBox::toggled, this, [this](bool)
232 {
233 updateImages();
234 });
235 }
236 };
237}
238#endif
PeriodicImages(const PeriodicImages &)=delete
PeriodicImages(vtkRenderer *rendererIn)
PeriodicImages & operator=(const PeriodicImages &)=delete
void setTranslations(std::vector< Eigen::Vector3d > values)
PeriodicImagesWidget(vtkGenericOpenGLRenderWindow *windowIn, vtkRenderer *rendererIn, const Eigen::Matrix< double, 3, Eigen::Dynamic > &basisIn, QWidget *parent)
QGridLayout * grid(QGroupBox *group)
void field(QGridLayout *layout, int row, const QString &title, QWidget *input, const QString &description)
Eigen::MatrixXd matrix(const Form &form)
int j
int parent
int axis
int k
int i