timelapse replay layout edits then map edits

This commit is contained in:
garak 2024-01-10 14:34:48 -05:00
parent cd5b1f98d2
commit 99eb92c3b2
3 changed files with 121 additions and 87 deletions

View file

@ -27,6 +27,7 @@ public:
private: private:
Ui::MapImageExporter *ui; Ui::MapImageExporter *ui;
Layout *layout = nullptr;
Map *map = nullptr; Map *map = nullptr;
Editor *editor = nullptr; Editor *editor = nullptr;
QGraphicsScene *scene = nullptr; QGraphicsScene *scene = nullptr;

View file

@ -1208,6 +1208,7 @@ void MainWindow::scrollTreeView(QString itemName) {
} }
} }
// !TODO: remove this?
void MainWindow::sortMapList() { void MainWindow::sortMapList() {
} }
@ -2564,6 +2565,17 @@ void MainWindow::on_action_Export_Map_Image_triggered() {
} }
void MainWindow::on_actionExport_Stitched_Map_Image_triggered() { void MainWindow::on_actionExport_Stitched_Map_Image_triggered() {
if (!this->editor->map) {
QMessageBox warning(this);
warning.setText("Notice");
warning.setInformativeText("Map stich images are not possible without a map selected.");
warning.setStandardButtons(QMessageBox::Ok);
warning.setDefaultButton(QMessageBox::Cancel);
warning.setIcon(QMessageBox::Warning);
warning.exec();
return;
}
showExportMapImageWindow(ImageExporterMode::Stitch); showExportMapImageWindow(ImageExporterMode::Stitch);
} }

View file

@ -29,15 +29,18 @@ MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExpor
{ {
ui->setupUi(this); ui->setupUi(this);
this->map = editor_->map; this->map = editor_->map;
this->layout = editor_->layout;
this->editor = editor_; this->editor = editor_;
this->mode = mode; this->mode = mode;
this->setWindowTitle(getTitle(this->mode)); this->setWindowTitle(getTitle(this->mode));
this->ui->groupBox_Connections->setVisible(this->mode == ImageExporterMode::Normal); this->ui->groupBox_Connections->setVisible(this->mode == ImageExporterMode::Normal);
this->ui->groupBox_Timelapse->setVisible(this->mode == ImageExporterMode::Timelapse); this->ui->groupBox_Timelapse->setVisible(this->mode == ImageExporterMode::Timelapse);
this->ui->comboBox_MapSelection->addItems(editor->project->mapNames); if (this->map) {
this->ui->comboBox_MapSelection->setCurrentText(map->name); this->ui->comboBox_MapSelection->addItems(editor->project->mapNames);
this->ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down this->ui->comboBox_MapSelection->setCurrentText(map->name);
this->ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down
}
updatePreview(); updatePreview();
} }
@ -53,13 +56,13 @@ void MapImageExporter::saveImage() {
switch (this->mode) switch (this->mode)
{ {
case ImageExporterMode::Normal: case ImageExporterMode::Normal:
defaultFilename = map->name; defaultFilename = this->map? this->map->name : this->layout->name;
break; break;
case ImageExporterMode::Stitch: case ImageExporterMode::Stitch:
defaultFilename = QString("Stitch_From_%1").arg(map->name); defaultFilename = QString("Stitch_From_%1").arg(this->map? this->map->name : this->layout->name);
break; break;
case ImageExporterMode::Timelapse: case ImageExporterMode::Timelapse:
defaultFilename = QString("Timelapse_%1").arg(map->name); defaultFilename = QString("Timelapse_%1").arg(this->map? this->map->name : this->layout->name);
break; break;
} }
@ -91,89 +94,98 @@ void MapImageExporter::saveImage() {
} }
case ImageExporterMode::Timelapse: case ImageExporterMode::Timelapse:
// !TODO: also need layout editHistory! // !TODO: also need layout editHistory!
QProgressDialog progress("Building map timelapse...", "Cancel", 0, 1, this); QGifImage timelapseImg;
progress.setAutoClose(true);
progress.setWindowModality(Qt::WindowModal);
progress.setModal(true);
progress.setMaximum(1);
progress.setValue(0);
Layout *layout = this->map->layout;
if (!layout) break;
int maxWidth = layout->getWidth() * 16;
int maxHeight = layout->getHeight() * 16;
if (showBorder) {
maxWidth += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
maxHeight += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
}
// Rewind to the specified start of the map edit history.
int i = 0;
while (this->map->editHistory.canUndo()) {
progress.setValue(i);
this->map->editHistory.undo();
int width = this->map->getWidth() * 16;
int height = this->map->getHeight() * 16;
if (showBorder) {
width += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
height += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
}
if (width > maxWidth) {
maxWidth = width;
}
if (height > maxHeight) {
maxHeight = height;
}
i++;
}
QGifImage timelapseImg(QSize(maxWidth, maxHeight));
timelapseImg.setDefaultDelay(timelapseDelayMs); timelapseImg.setDefaultDelay(timelapseDelayMs);
timelapseImg.setDefaultTransparentColor(QColor(0, 0, 0)); timelapseImg.setDefaultTransparentColor(QColor(0, 0, 0));
// Draw each frame, skpping the specified number of map edits in
// the undo history. auto generateTimelapseFromHistory = [=, this, &timelapseImg](QString progressText, QUndoStack &historyStack){
progress.setMaximum(i); //
while (i > 0) { QProgressDialog progress(progressText, "Cancel", 0, 1, this);
if (progress.wasCanceled()) { progress.setAutoClose(true);
progress.close(); progress.setWindowModality(Qt::WindowModal);
while (i > 0 && this->map->editHistory.canRedo()) { progress.setModal(true);
i--; progress.setMaximum(1);
this->map->editHistory.redo(); progress.setValue(0);
int maxWidth = this->layout->getWidth() * 16;
int maxHeight = this->layout->getHeight() * 16;
if (showBorder) {
maxWidth += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
maxHeight += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
}
// Rewind to the specified start of the map edit history.
int i = 0;
while (historyStack.canUndo()) {
progress.setValue(i);
historyStack.undo();
int width = this->layout->getWidth() * 16;
int height = this->layout->getHeight() * 16;
if (showBorder) {
width += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
height += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
} }
return; if (width > maxWidth) {
maxWidth = width;
}
if (height > maxHeight) {
maxHeight = height;
}
i++;
} }
while (this->map->editHistory.canRedo() &&
!historyItemAppliesToFrame(this->map->editHistory.command(this->map->editHistory.index()))) { // Draw each frame, skpping the specified number of map edits in
i--; // the undo history.
this->map->editHistory.redo(); progress.setMaximum(i);
} while (i > 0) {
progress.setValue(progress.maximum() - i); if (progress.wasCanceled()) {
QPixmap pixmap = this->getFormattedMapPixmap(this->map, !this->showBorder); progress.close();
if (pixmap.width() < maxWidth || pixmap.height() < maxHeight) { while (i > 0 && historyStack.canRedo()) {
QPixmap pixmap2 = QPixmap(maxWidth, maxHeight);
QPainter painter(&pixmap2);
pixmap2.fill(QColor(0, 0, 0));
painter.drawPixmap(0, 0, pixmap.width(), pixmap.height(), pixmap);
painter.end();
pixmap = pixmap2;
}
timelapseImg.addFrame(pixmap.toImage());
for (int j = 0; j < timelapseSkipAmount; j++) {
if (i > 0) {
i--;
this->map->editHistory.redo();
while (this->map->editHistory.canRedo() &&
!historyItemAppliesToFrame(this->map->editHistory.command(this->map->editHistory.index()))) {
i--; i--;
this->map->editHistory.redo(); historyStack.redo();
}
return;
}
while (historyStack.canRedo() &&
!historyItemAppliesToFrame(historyStack.command(historyStack.index()))) {
i--;
historyStack.redo();
}
progress.setValue(progress.maximum() - i);
QPixmap pixmap = this->getFormattedMapPixmap(this->map, !this->showBorder);
if (pixmap.width() < maxWidth || pixmap.height() < maxHeight) {
QPixmap pixmap2 = QPixmap(maxWidth, maxHeight);
QPainter painter(&pixmap2);
pixmap2.fill(QColor(0, 0, 0));
painter.drawPixmap(0, 0, pixmap.width(), pixmap.height(), pixmap);
painter.end();
pixmap = pixmap2;
}
timelapseImg.addFrame(pixmap.toImage());
for (int j = 0; j < timelapseSkipAmount; j++) {
if (i > 0) {
i--;
historyStack.redo();
while (historyStack.canRedo() &&
!historyItemAppliesToFrame(historyStack.command(historyStack.index()))) {
i--;
historyStack.redo();
}
} }
} }
} }
} // The latest map state is the last animated frame.
// The latest map state is the last animated frame. QPixmap pixmap = this->getFormattedMapPixmap(this->map, !this->showBorder);
QPixmap pixmap = this->getFormattedMapPixmap(this->map, !this->showBorder); timelapseImg.addFrame(pixmap.toImage());
timelapseImg.addFrame(pixmap.toImage()); progress.close();
};
if (this->layout)
generateTimelapseFromHistory("Building layout timelapse...", this->layout->editHistory);
if (this->map)
generateTimelapseFromHistory("Building map timelapse...", this->map->editHistory);
timelapseImg.save(filepath); timelapseImg.save(filepath);
progress.close();
break; break;
} }
this->close(); this->close();
@ -358,17 +370,22 @@ void MapImageExporter::updatePreview() {
scene->itemsBoundingRect().height() + 2); scene->itemsBoundingRect().height() + 2);
} }
// THIS
QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
QPixmap pixmap; QPixmap pixmap;
// draw background layer / base image Layout *layout;
Layout *layout = map->layout;
if (!layout) {
return QPixmap();
}
layout->render(true); // draw background layer / base image
pixmap = layout->pixmap; if (!this->map) {
layout = this->layout;
layout->render(true);
pixmap = layout->pixmap;
} else {
layout = map->layout;
map->layout->render(true);
pixmap = map->layout->pixmap;
}
if (showCollision) { if (showCollision) {
QPainter collisionPainter(&pixmap); QPainter collisionPainter(&pixmap);
@ -401,6 +418,10 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
pixmap = newPixmap; pixmap = newPixmap;
} }
if (!this->map) {
return pixmap;
}
if (!this->mode) { if (!this->mode) {
// if showing connections, draw on outside of image // if showing connections, draw on outside of image
QPainter connectionPainter(&pixmap); QPainter connectionPainter(&pixmap);