35 QVTKOpenGLStereoWidget*
const qvtkGLwidget_in,
55 ,playbackTimer(new QTimer(this))
86 controls::box(
"Navigation",
this,
"Select simulation outputs, control playback and capture images.");
88 auto *frameIDLayout =
new QGridLayout();
89 auto *incrementLayout =
new QGridLayout();
90 framesLayout->addLayout(frameIDLayout, 0, 0);
91 framesLayout->addLayout(incrementLayout, 0, 1);
92 framesLayout->setColumnStretch(0, 1);
93 framesLayout->setColumnStretch(1, 1);
96 "Simulation output runID to display. Press Enter or leave this field to load it and apply camera rotation and advance; output IDs may not be consecutive.");
99 "RunID increment used by previous, next and playback controls. Normally matches outputFrequency.");
100 auto *axisLayout =
new QGridLayout();
101 auto *angleLayout =
new QGridLayout();
102 framesLayout->addLayout(axisLayout, 1, 0);
103 framesLayout->addLayout(angleLayout, 1, 1);
107 "Global x y z rotation axis through the simulation-box center. Automatic rotation keeps the box centered. Enter a nonzero vector; it is normalized automatically. Applies on Enter or when loading a frame.");
109 "View rotation in degrees equals this value times the simulation frameID, including skipped IDs. Positive angles follow the right-hand rule. Zero disables rotation. Applies on Enter or when loading a frame.");
110 connect(
rotationAxisEdit, &QLineEdit::returnPressed,
this, &DefectiveCrystalActor::updateRotationSettings);
111 connect(
rotationAngleEdit, &QLineEdit::returnPressed,
this, &DefectiveCrystalActor::updateRotationSettings);
112 auto *advanceLayout =
new QGridLayout();
113 framesLayout->addLayout(advanceLayout, 2, 0, 1, 2);
116 "Camera travel toward and through the box center, in simulation length units b times frameID, including skipped IDs. Positive advances, negative retreats, zero disables travel. Combines with rotation and uses perspective projection. Applies on Enter or when loading a frame.");
117 connect(
cameraAdvanceEdit, &QLineEdit::returnPressed,
this, &DefectiveCrystalActor::updateRotationSettings);
118 auto *playback =
new QWidget(
this);
119 auto *playbackLayout =
new QHBoxLayout(playback);
120 playbackLayout->setContentsMargins(0, 0, 0, 0);
121 minusFrameButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipBackward));
124 plusFrameButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipForward));
131 button->setToolButtonStyle(Qt::ToolButtonIconOnly);
132 button->setIconSize(QSize(20, 20));
133 button->setFixedSize(32, 32);
136 playbackLayout->addSpacing(12);
138 playbackLayout->addWidget(button);
140 playbackLayout->addStretch();
141 minusFrameButton->setToolTip(
"Load the previous output using the frame increment.");
142 plusFrameButton->setToolTip(
"Load the next output using the frame increment.");
143 playFrameButton->setToolTip(
"Advance through available frames until stopped.");
145 framesLayout->addWidget(playback, 3, 0, 1, 2);
146 framesLayout->addWidget(
saveImage, 4, 0, 1, 2);
150 saveImage->setToolTip(
"Save a PNG of each loaded frame to the simulation output folder.");
153 mainLayout->addWidget(periodicImages, 1, 0, 1, 6);
167 connect(
frameIDedit, &QLineEdit::editingFinished,
this, [
this]()
172 updateConfiguration();
175 connect(
plusFrameButton,SIGNAL(pressed()),
this, SLOT(nextConfiguration()));
176 connect(
minusFrameButton,SIGNAL(pressed()),
this, SLOT(prevConfiguration()));
177 connect(
playFrameButton,SIGNAL(pressed()),
this, SLOT(playConfigurations()));
180 playbackTimer->setObjectName(
"framePlaybackTimer");
181 playbackTimer->setSingleShot(
true);
182 playbackTimer->setInterval(100);
184 connect(playbackTimer, &QTimer::timeout,
this, [
this]()
186 if(playing && nextConfiguration() && playing)
188 playbackTimer->start();
348 bool DefectiveCrystalActor::updateConfiguration(
const size_t& frameID)
354 QScopedValueRollback<bool> updating(updatingFrame,
true);
355 auto nextConfig = std::make_unique<DefectiveCrystalConfigIO<3>>(
ddBase.simulationParameters.traitsIO.configFolder);
356 auto nextAux = std::make_unique<DefectiveCrystalAuxIO<3>>(
ddBase.simulationParameters.traitsIO.auxFolder);
361 nextConfig->read(frameID);
362 if(!nextAux->isBinGood(frameID) && !nextAux->isTxtGood(frameID))
364 throw std::runtime_error(
"No auxiliary file for frame " + std::to_string(frameID));
366 nextAux->read(frameID);
368 catch(
const std::exception& error)
370 frameStatus->setText(QString::fromStdString(error.what()));
371 frameIDedit->setText(QString::number(loadedFrameID));
376 const bool ready =
renderWindow->GetReadyForRendering();
378 configuration.swap(nextConfig);
379 auxiliary.swap(nextAux);
382 refreshFrame(frameID);
384 catch(
const std::exception& error)
386 configuration.swap(nextConfig);
387 auxiliary.swap(nextAux);
388 QString message = QString::fromStdString(error.what());
391 refreshFrame(loadedFrameID);
393 catch(
const std::exception& restoreError)
395 message +=
" Restoration failed: " + QString::fromStdString(restoreError.what());
398 frameIDedit->setText(QString::number(loadedFrameID));
405 loadedFrameID = frameID;
408 readRotationSettings();
409 frameRotation.apply(renderer->GetActiveCamera(),
411 rotationAxis, rotationDegreesPerFrame, frameID, cameraAdvancePerFrame);
412 renderer->ResetCameraClippingRange();
423 const QString filename = QString::fromStdString(
ddBase.simulationParameters.traitsIO.configFolder +
424 "/img_" + std::to_string(frameID) +
".png");
427 frameStatus->setText(
"Frame loaded, but PNG capture failed.");