diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index 02f0d81c9..76645e477 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -295,6 +295,7 @@ MainWindow::MainWindow(QWidget* parent) : mapLevel = combo->currentIndex(); ui->mapView->setScene(controller.scene(mapLevel)); + ui->mapView->setViewports(); ui->minimapView->setScene(controller.miniScene(mapLevel)); }); layout->addWidget(combo, c == ui->menuView ? 1 : 0); @@ -1336,9 +1337,10 @@ void MainWindow::on_actionUpdate_appearance_triggered() controller.scene(mapLevel)->selectionObjectsView.deselectObject(obj); continue; } - + std::vector selectedTiles; for(auto & offset : obj->appearance->getBlockedOffsets()) - controller.scene(mapLevel)->selectionTerrainView.select(obj->pos + offset); + selectedTiles.push_back(obj->pos + offset); + controller.scene(mapLevel)->selectionTerrainView.select(selectedTiles); } else { @@ -1497,8 +1499,6 @@ void MainWindow::on_actionLock_triggered() } controller.scene(mapLevel)->selectionObjectsView.clear(); } - controller.scene(mapLevel)->objectsView.update(); - controller.scene(mapLevel)->selectionObjectsView.update(); } } @@ -1510,7 +1510,6 @@ void MainWindow::on_actionUnlock_triggered() controller.scene(mapLevel)->selectionObjectsView.unlockAll(); controller.scene(mapLevel)->objectsView.unlockAll(); } - controller.scene(mapLevel)->objectsView.update(); } diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index cdf170fd1..b3769c6a9 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -233,7 +233,7 @@ void MapController::setMap(std::unique_ptr cmap) _miniscenes[i].reset(new MinimapScene(i)); } resetMapHandler(); - sceneForceUpdate(); + initializeMap(); connectScenes(); @@ -260,21 +260,24 @@ void MapController::initObstaclePainters(CMap * map) } } +void MapController::initializeMap() +{ + for(int i = 0; i < _map->mapLevels; i++) + { + _scenes[i]->createMap(); + _miniscenes[i]->createMap(); + } +} + void MapController::sceneForceUpdate() { for(int i = 0; i < _map->mapLevels; i++) { - _scenes[i]->updateViews(); - _miniscenes[i]->updateViews(); + _scenes[i]->updateMap(); + _miniscenes[i]->updateMap(); } } -void MapController::sceneForceUpdate(int level) -{ - _scenes[level]->updateViews(); - _miniscenes[level]->updateViews(); -} - void MapController::resetMapHandler() { if(!_mapHandler) @@ -297,16 +300,13 @@ void MapController::commitTerrainChange(int level, const TerrainId & terrain) return; _scenes[level]->selectionTerrainView.clear(); - _scenes[level]->selectionTerrainView.draw(); _map->getEditManager()->getTerrainSelection().setSelection(v); _map->getEditManager()->drawTerrain(terrain, terrainDecorationPercentageLevel, &CRandomGenerator::getDefault()); - for(auto & t : v) - _scenes[level]->terrainView.setDirty(t); - _scenes[level]->terrainView.draw(); + _scenes[level]->terrainView.redrawTerrain(v); - _miniscenes[level]->updateViews(); + _miniscenes[level]->updateMap(); main->mapChanged(); } @@ -318,19 +318,16 @@ void MapController::commitRoadOrRiverChange(int level, ui8 type, bool isRoad) return; _scenes[level]->selectionTerrainView.clear(); - _scenes[level]->selectionTerrainView.draw(); _map->getEditManager()->getTerrainSelection().setSelection(v); if(isRoad) _map->getEditManager()->drawRoad(RoadId(type), &CRandomGenerator::getDefault()); else _map->getEditManager()->drawRiver(RiverId(type), &CRandomGenerator::getDefault()); + + _scenes[level]->terrainView.redrawTerrain(v); - for(auto & t : v) - _scenes[level]->terrainView.setDirty(t); - _scenes[level]->terrainView.draw(); - - _miniscenes[level]->updateViews(); + _miniscenes[level]->updateMap(); main->mapChanged(); } @@ -355,15 +352,13 @@ void MapController::commitObjectErase(int level) { //invalidate tiles under objects _mapHandler->removeObject(obj); - _scenes[level]->objectsView.setDirty(obj); } + _scenes[level]->objectsView.redrawObjects(selectedObjects); _scenes[level]->selectionObjectsView.clear(); - _scenes[level]->objectsView.draw(); - _scenes[level]->selectionObjectsView.draw(); - _scenes[level]->passabilityView.update(); + _scenes[level]->passabilityView.redraw(); - _miniscenes[level]->updateViews(); + _miniscenes[level]->updateMap(); main->mapChanged(); } @@ -411,11 +406,11 @@ void MapController::pasteFromClipboard(int level) if(!errors.isEmpty()) QMessageBox::warning(main, QObject::tr("Can't place object"), errors.join('\n')); - _scenes[level]->objectsView.draw(); - _scenes[level]->passabilityView.update(); - _scenes[level]->selectionObjectsView.draw(); + _scenes[level]->objectsView.redraw(); + _scenes[level]->passabilityView.redraw(); + _scenes[level]->selectionObjectsView.redraw(); - _miniscenes[level]->updateViews(); + _miniscenes[level]->updateMap(); main->mapChanged(); } @@ -425,9 +420,8 @@ bool MapController::discardObject(int level) const if(_scenes[level]->selectionObjectsView.newObject) { _scenes[level]->selectionObjectsView.newObject.reset(); - _scenes[level]->selectionObjectsView.shift = QPoint(0, 0); + _scenes[level]->selectionObjectsView.setShift(0, 0); _scenes[level]->selectionObjectsView.selectionMode = SelectionObjectsLayer::NOTHING; - _scenes[level]->selectionObjectsView.draw(); return true; } return false; @@ -437,7 +431,7 @@ void MapController::createObject(int level, std::shared_ptr ob { _scenes[level]->selectionObjectsView.newObject = obj; _scenes[level]->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT; - _scenes[level]->selectionObjectsView.draw(); + _scenes[level]->selectionObjectsView.redraw(); } void MapController::commitObstacleFill(int level) @@ -467,29 +461,26 @@ void MapController::commitObstacleFill(int level) for(auto o : sel.second->placeObstacles(CRandomGenerator::getDefault())) { _mapHandler->invalidate(o.get()); - _scenes[level]->objectsView.setDirty(o.get()); + _scenes[level]->objectsView.redrawObjects({o.get()}); } } _scenes[level]->selectionTerrainView.clear(); - _scenes[level]->selectionTerrainView.draw(); - _scenes[level]->objectsView.draw(); + _scenes[level]->objectsView.update(); _scenes[level]->passabilityView.update(); - _miniscenes[level]->updateViews(); + _miniscenes[level]->updateMap(); main->mapChanged(); } void MapController::commitObjectChange(int level) { - for( auto * o : _scenes[level]->selectionObjectsView.getSelection()) - _scenes[level]->objectsView.setDirty(o); + _scenes[level]->objectsView.redrawObjects(_scenes[level]->selectionObjectsView.getSelection()); - _scenes[level]->objectsView.draw(); - _scenes[level]->selectionObjectsView.draw(); - _scenes[level]->passabilityView.update(); + _scenes[level]->selectionObjectsView.redraw(); + _scenes[level]->passabilityView.redraw(); - _miniscenes[level]->updateViews(); + _miniscenes[level]->updateMap(); main->mapChanged(); } @@ -506,29 +497,30 @@ void MapController::commitObjectShift(int level) bool makeShift = !shift.isNull(); if(makeShift) { - for(auto * obj : _scenes[level]->selectionObjectsView.getSelection()) + std::set movedObjects = _scenes[level]->selectionObjectsView.getSelection(); + for(auto * obj : movedObjects) { int3 pos = obj->pos; pos.z = level; pos.x += shift.x(); pos.y += shift.y(); - _scenes[level]->objectsView.setDirty(obj); //set dirty before movement _map->getEditManager()->moveObject(obj, pos); _mapHandler->invalidate(obj); } + _scenes[level]->objectsView.redrawObjects(movedObjects); } _scenes[level]->selectionObjectsView.newObject = nullptr; - _scenes[level]->selectionObjectsView.shift = QPoint(0, 0); + _scenes[level]->selectionObjectsView.setShift(0, 0); _scenes[level]->selectionObjectsView.selectionMode = SelectionObjectsLayer::NOTHING; if(makeShift) { - _scenes[level]->objectsView.draw(); - _scenes[level]->selectionObjectsView.draw(); - _scenes[level]->passabilityView.update(); + _scenes[level]->objectsView.redraw(); + _scenes[level]->passabilityView.redraw(); + _scenes[level]->selectionObjectsView.redraw(); - _miniscenes[level]->updateViews(); + _miniscenes[level]->updateMap(); main->mapChanged(); } } @@ -551,16 +543,14 @@ void MapController::commitObjectCreate(int level) _map->getEditManager()->insertObject(newObj); _mapHandler->invalidate(newObj.get()); - _scenes[level]->objectsView.setDirty(newObj.get()); + _scenes[level]->objectsView.redrawObjects({newObj.get()}); _scenes[level]->selectionObjectsView.newObject = nullptr; - _scenes[level]->selectionObjectsView.shift = QPoint(0, 0); + _scenes[level]->selectionObjectsView.setShift(0, 0); _scenes[level]->selectionObjectsView.selectionMode = SelectionObjectsLayer::NOTHING; - _scenes[level]->objectsView.draw(); - _scenes[level]->selectionObjectsView.draw(); - _scenes[level]->passabilityView.update(); + _scenes[level]->passabilityView.redraw(); - _miniscenes[level]->updateViews(); + _miniscenes[level]->updateMap(); main->mapChanged(); } diff --git a/mapeditor/mapcontroller.h b/mapeditor/mapcontroller.h index 38c92ab65..5edb97b1c 100644 --- a/mapeditor/mapcontroller.h +++ b/mapeditor/mapcontroller.h @@ -48,8 +48,8 @@ public: void resetMapHandler(); + void initializeMap(); void sceneForceUpdate(); - void sceneForceUpdate(int level); void commitTerrainChange(int level, const TerrainId & terrain); void commitRoadOrRiverChange(int level, ui8 type, bool isRoad); diff --git a/mapeditor/maphandler.cpp b/mapeditor/maphandler.cpp index 7ae93122c..7bbd16af7 100644 --- a/mapeditor/maphandler.cpp +++ b/mapeditor/maphandler.cpp @@ -109,18 +109,17 @@ void MapHandler::initTerrainGraphics() loadFlipped(roadAnimations, roadImages, roadFiles); } -void MapHandler::drawTerrainTile(QPainter & painter, int x, int y, int z) +void MapHandler::drawTerrainTile(QPainter & painter, int x, int y, int z, QPointF offset) { const auto & tinfo = map->getTile(int3(x, y, z)); auto terrainName = tinfo.getTerrain()->getJsonKey(); if(terrainImages.at(terrainName).size() <= tinfo.terView) return; - - painter.drawImage(x * tileSize, y * tileSize, flippedImage(terrainImages.at(terrainName)[tinfo.terView], tinfo.extTileFlags)); + painter.drawImage(x * tileSize - offset.x(), y * tileSize - offset.y(), flippedImage(terrainImages.at(terrainName)[tinfo.terView], tinfo.extTileFlags)); } -void MapHandler::drawRoad(QPainter & painter, int x, int y, int z) +void MapHandler::drawRoad(QPainter & painter, int x, int y, int z, QPointF offset) { const auto & tinfo = map->getTile(int3(x, y, z)); auto * tinfoUpper = map->isInTheMap(int3(x, y - 1, z)) ? &map->getTile(int3(x, y - 1, z)) : nullptr; @@ -132,7 +131,7 @@ void MapHandler::drawRoad(QPainter & painter, int x, int y, int z) { const QRect source{0, tileSize / 2, tileSize, tileSize / 2}; const ui8 rotationFlags = tinfoUpper->extTileFlags >> 4; - painter.drawImage(QPoint(x * tileSize, y * tileSize), flippedImage(roadImages.at(roadName)[tinfoUpper->roadDir], rotationFlags), source); + painter.drawImage(QPoint(x * tileSize - offset.x(), y * tileSize - offset.y()), flippedImage(roadImages.at(roadName)[tinfoUpper->roadDir], rotationFlags), source); } } @@ -143,12 +142,12 @@ void MapHandler::drawRoad(QPainter & painter, int x, int y, int z) { const QRect source{0, 0, tileSize, tileSize / 2}; const ui8 rotationFlags = tinfo.extTileFlags >> 4; - painter.drawImage(QPoint(x * tileSize, y * tileSize + tileSize / 2), flippedImage(roadImages.at(roadName)[tinfo.roadDir], rotationFlags), source); + painter.drawImage(QPoint(x * tileSize - offset.x(), y * tileSize + tileSize / 2 - offset.y()), flippedImage(roadImages.at(roadName)[tinfo.roadDir], rotationFlags), source); } } } -void MapHandler::drawRiver(QPainter & painter, int x, int y, int z) +void MapHandler::drawRiver(QPainter & painter, int x, int y, int z, QPointF offset) { const auto & tinfo = map->getTile(int3(x, y, z)); @@ -162,7 +161,7 @@ void MapHandler::drawRiver(QPainter & painter, int x, int y, int z) return; const ui8 rotationFlags = tinfo.extTileFlags >> 2; - painter.drawImage(x * tileSize, y * tileSize, flippedImage(riverImages.at(riverName)[tinfo.riverDir], rotationFlags)); + painter.drawImage(x * tileSize - offset.x(), y * tileSize - offset.y(), flippedImage(riverImages.at(riverName)[tinfo.riverDir], rotationFlags)); } void setPlayerColor(QImage * sur, PlayerColor player) @@ -366,7 +365,7 @@ std::vector & MapHandler::getObjects(int x, int y, int z) return tileObjects[index(x, y, z)]; } -void MapHandler::drawObjects(QPainter & painter, int x, int y, int z, const std::set & locked) +void MapHandler::drawObjects(QPainter & painter, int x, int y, int z, QPointF offset, const std::set & locked) { painter.setRenderHint(QPainter::Antialiasing, false); painter.setRenderHint(QPainter::SmoothPixmapTransform, false); @@ -390,7 +389,7 @@ void MapHandler::drawObjects(QPainter & painter, int x, int y, int z, const std: { auto pos = obj->anchorPos(); - painter.drawImage(QPoint(x * tileSize, y * tileSize), *objData.objBitmap, object.rect, Qt::AutoColor | Qt::NoOpaqueDetection); + painter.drawImage(QPoint(x * tileSize - offset.x(), y * tileSize - offset.y()), *objData.objBitmap, object.rect, Qt::AutoColor | Qt::NoOpaqueDetection); if(locked.count(obj)) { @@ -402,13 +401,13 @@ void MapHandler::drawObjects(QPainter & painter, int x, int y, int z, const std: if(objData.flagBitmap) { if(x == pos.x && y == pos.y) - painter.drawImage(QPoint((x - 2) * tileSize, (y - 1) * tileSize), *objData.flagBitmap); + painter.drawImage(QPoint((x - 2) * tileSize - offset.x(), (y - 1) * tileSize - offset.y()), *objData.flagBitmap); } } } } -void MapHandler::drawObjectAt(QPainter & painter, const CGObjectInstance * obj, int x, int y) +void MapHandler::drawObjectAt(QPainter & painter, const CGObjectInstance * obj, int x, int y, QPointF offset) { if (!obj) { @@ -424,10 +423,10 @@ void MapHandler::drawObjectAt(QPainter & painter, const CGObjectInstance * obj, if (objData.objBitmap) { - painter.drawImage(QPoint((x + 1) * tileSize - objData.objBitmap->width(), (y + 1) * tileSize - objData.objBitmap->height()), *objData.objBitmap); + painter.drawImage(QPoint((x + 1) * tileSize - objData.objBitmap->width() - offset.x(), (y + 1) * tileSize - objData.objBitmap->height() - offset.y()), *objData.objBitmap); if (objData.flagBitmap) - painter.drawImage(QPoint((x + 1) * tileSize - objData.objBitmap->width(), (y + 1) * tileSize - objData.objBitmap->height()), *objData.flagBitmap); + painter.drawImage(QPoint((x + 1) * tileSize - objData.objBitmap->width() - offset.x(), (y + 1) * tileSize - objData.objBitmap->height() - offset.y()), *objData.flagBitmap); } } diff --git a/mapeditor/maphandler.h b/mapeditor/maphandler.h index 3f8cb5a26..e4bfbf948 100644 --- a/mapeditor/maphandler.h +++ b/mapeditor/maphandler.h @@ -92,11 +92,11 @@ public: void reset(const CMap * Map); - void drawTerrainTile(QPainter & painter, int x, int y, int z); + void drawTerrainTile(QPainter & painter, int x, int y, int z, QPointF offset); /// draws a river segment on current tile - void drawRiver(QPainter & painter, int x, int y, int z); + void drawRiver(QPainter & painter, int x, int y, int z, QPointF offset); /// draws a road segment on current tile - void drawRoad(QPainter & painter, int x, int y, int z); + void drawRoad(QPainter & painter, int x, int y, int z, QPointF offset); std::set invalidate(const CGObjectInstance *); //invalidates object rects void invalidateObjects(); //invalidates all objects on the map @@ -111,8 +111,8 @@ public: std::set addObject(const CGObjectInstance * object); /// draws all objects on current tile (higher-level logic, unlike other draw*** methods) - void drawObjects(QPainter & painter, int x, int y, int z, const std::set & locked); - void drawObjectAt(QPainter & painter, const CGObjectInstance * object, int x, int y); + void drawObjects(QPainter & painter, int x, int y, int z, QPointF offset, const std::set & locked); + void drawObjectAt(QPainter & painter, const CGObjectInstance * object, int x, int y, QPointF offset); void drawMinimapTile(QPainter & painter, int x, int y, int z); diff --git a/mapeditor/mapview.cpp b/mapeditor/mapview.cpp index 1d478040d..bde5906e2 100644 --- a/mapeditor/mapview.cpp +++ b/mapeditor/mapview.cpp @@ -59,6 +59,8 @@ MapView::MapView(QWidget * parent): QGraphicsView(parent), selectionTool(MapView::SelectionTool::None) { + connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &MapView::setViewports); + connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &MapView::setViewports); } void MapView::cameraChanged(const QPointF & pos) @@ -71,6 +73,11 @@ void MapView::setController(MapController * ctrl) controller = ctrl; } +void MapView::resizeEvent(QResizeEvent * event) +{ + setViewports(); +} + void MapView::mouseMoveEvent(QMouseEvent *mouseEvent) { this->update(); @@ -96,24 +103,24 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent) { case MapView::SelectionTool::Brush: if(mouseEvent->buttons() & Qt::RightButton) - sc->selectionTerrainView.erase(tile); + sc->selectionTerrainView.erase({tile}); else if(mouseEvent->buttons() == Qt::LeftButton) - sc->selectionTerrainView.select(tile); - sc->selectionTerrainView.draw(); + sc->selectionTerrainView.select({tile}); break; case MapView::SelectionTool::Brush2: { std::array extra{ int3{0, 0, 0}, int3{1, 0, 0}, int3{0, 1, 0}, int3{1, 1, 0} }; + std::vector tiles; for(auto & e : extra) { - if(mouseEvent->buttons() & Qt::RightButton) - sc->selectionTerrainView.erase(tile + e); - else if(mouseEvent->buttons() == Qt::LeftButton) - sc->selectionTerrainView.select(tile + e); + tiles.push_back(tile + e); } + if(mouseEvent->buttons() & Qt::RightButton) + sc->selectionTerrainView.erase(tiles); + else if(mouseEvent->buttons() == Qt::LeftButton) + sc->selectionTerrainView.select(tiles); } - sc->selectionTerrainView.draw(); break; case MapView::SelectionTool::Brush4: @@ -124,33 +131,37 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent) int3{-1, 1, 0}, int3{0, 1, 0}, int3{1, 1, 0}, int3{2, 1, 0}, int3{-1, 2, 0}, int3{0, 2, 0}, int3{1, 2, 0}, int3{2, 2, 0} }; + std::vector tiles; for(auto & e : extra) { - if(mouseEvent->buttons() & Qt::RightButton) - sc->selectionTerrainView.erase(tile + e); - else if(mouseEvent->buttons() == Qt::LeftButton) - sc->selectionTerrainView.select(tile + e); + tiles.push_back(tile + e); } + if(mouseEvent->buttons() & Qt::RightButton) + sc->selectionTerrainView.erase(tiles); + else if(mouseEvent->buttons() == Qt::LeftButton) + sc->selectionTerrainView.select(tiles); } - sc->selectionTerrainView.draw(); break; case MapView::SelectionTool::Area: + { if(mouseEvent->buttons() & Qt::RightButton || !(mouseEvent->buttons() & Qt::LeftButton)) break; sc->selectionTerrainView.clear(); + std::vector selectedTiles; for(int j = std::min(tile.y, tileStart.y); j < std::max(tile.y, tileStart.y); ++j) { for(int i = std::min(tile.x, tileStart.x); i < std::max(tile.x, tileStart.x); ++i) { - sc->selectionTerrainView.select(int3(i, j, sc->level)); + selectedTiles.emplace_back(i, j, sc->level); } } - sc->selectionTerrainView.draw(); + sc->selectionTerrainView.select(selectedTiles); break; - + } case MapView::SelectionTool::Line: + { { assert(tile.z == tileStart.z); const auto diff = tile - tileStart; @@ -174,39 +185,45 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent) if(mouseEvent->buttons() == Qt::LeftButton) { - for(auto & ts : temporaryTiles) - sc->selectionTerrainView.erase(ts); - + std::vectorerasedTiles(temporaryTiles.begin(), temporaryTiles.end()); + sc->selectionTerrainView.erase(erasedTiles); + + std::vectorselectedTiles; for(auto ts = tileStart; ts.dist2d(tileStart) < edge; ts += dir) { if(!controller->map()->isInTheMap(ts)) break; if(!sc->selectionTerrainView.selection().count(ts)) temporaryTiles.insert(ts); - sc->selectionTerrainView.select(ts); + selectedTiles.push_back(ts); } + sc->selectionTerrainView.select(selectedTiles); } if(mouseEvent->buttons() == Qt::RightButton) { - for(auto & ts : temporaryTiles) - sc->selectionTerrainView.select(ts); - + std::vectorselectedTiles(temporaryTiles.begin(), temporaryTiles.end()); + sc->selectionTerrainView.select(selectedTiles); + + std::vectorerasedTiles; for(auto ts = tileStart; ts.dist2d(tileStart) < edge; ts += dir) { if(!controller->map()->isInTheMap(ts)) break; if(sc->selectionTerrainView.selection().count(ts)) temporaryTiles.insert(ts); - sc->selectionTerrainView.erase(ts); + erasedTiles.push_back(ts); } + sc->selectionTerrainView.erase(selectedTiles); } - sc->selectionTerrainView.draw(); break; } - + } + case MapView::SelectionTool::Lasso: + { if(mouseEvent->buttons() == Qt::LeftButton) { + std::vectortiles; for(auto i = tilePrev; i != tile;) { int length = std::numeric_limits::max(); @@ -220,18 +237,19 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent) } } i += dir; - sc->selectionTerrainView.select(i); + tiles.push_back(i); } - sc->selectionTerrainView.draw(); + sc->selectionTerrainView.select(tiles); } break; + } case MapView::SelectionTool::None: if(mouseEvent->buttons() & Qt::RightButton) break; auto sh = tile - tileStart; - sc->selectionObjectsView.shift = QPoint(sh.x, sh.y); + sc->selectionObjectsView.setShift(sh.x, sh.y); if(sh.x || sh.y) { @@ -244,8 +262,6 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent) } } } - - sc->selectionObjectsView.draw(); break; } @@ -276,34 +292,31 @@ void MapView::mousePressEvent(QMouseEvent *event) case MapView::SelectionTool::Brush: case MapView::SelectionTool::Line: sc->selectionObjectsView.clear(); - sc->selectionObjectsView.draw(); if(event->button() == Qt::RightButton) - sc->selectionTerrainView.erase(tileStart); + sc->selectionTerrainView.erase({tileStart}); else if(event->button() == Qt::LeftButton) - sc->selectionTerrainView.select(tileStart); - sc->selectionTerrainView.draw(); + sc->selectionTerrainView.select({tileStart}); break; case MapView::SelectionTool::Brush2: sc->selectionObjectsView.clear(); - sc->selectionObjectsView.draw(); { std::array extra{ int3{0, 0, 0}, int3{1, 0, 0}, int3{0, 1, 0}, int3{1, 1, 0} }; + std::vector tiles; for(auto & e : extra) { - if(event->button() == Qt::RightButton) - sc->selectionTerrainView.erase(tileStart + e); - else if(event->button() == Qt::LeftButton) - sc->selectionTerrainView.select(tileStart + e); + tiles.push_back(tileStart + e); } + if(event->buttons() & Qt::RightButton) + sc->selectionTerrainView.erase(tiles); + else if(event->buttons() == Qt::LeftButton) + sc->selectionTerrainView.select(tiles); } - sc->selectionTerrainView.draw(); break; case MapView::SelectionTool::Brush4: sc->selectionObjectsView.clear(); - sc->selectionObjectsView.draw(); { std::array extra{ int3{-1, -1, 0}, int3{0, -1, 0}, int3{1, -1, 0}, int3{2, -1, 0}, @@ -311,15 +324,16 @@ void MapView::mousePressEvent(QMouseEvent *event) int3{-1, 1, 0}, int3{0, 1, 0}, int3{1, 1, 0}, int3{2, 1, 0}, int3{-1, 2, 0}, int3{0, 2, 0}, int3{1, 2, 0}, int3{2, 2, 0} }; + std::vector tiles; for(auto & e : extra) { - if(event->button() == Qt::RightButton) - sc->selectionTerrainView.erase(tileStart + e); - else if(event->button() == Qt::LeftButton) - sc->selectionTerrainView.select(tileStart + e); + tiles.push_back(tileStart + e); } + if(event->buttons() & Qt::RightButton) + sc->selectionTerrainView.erase(tiles); + else if(event->buttons() == Qt::LeftButton) + sc->selectionTerrainView.select(tiles); } - sc->selectionTerrainView.draw(); break; case MapView::SelectionTool::Area: @@ -328,63 +342,63 @@ void MapView::mousePressEvent(QMouseEvent *event) break; sc->selectionTerrainView.clear(); - sc->selectionTerrainView.draw(); sc->selectionObjectsView.clear(); - sc->selectionObjectsView.draw(); break; - + case MapView::SelectionTool::Fill: - { - if(event->button() != Qt::RightButton && event->button() != Qt::LeftButton) - break; - - std::vector queue; - queue.push_back(tileStart); - - const std::array dirs{ int3{1, 0, 0}, int3{-1, 0, 0}, int3{0, 1, 0}, int3{0, -1, 0} }; - - while(!queue.empty()) - { - auto tile = queue.back(); - queue.pop_back(); - if(event->button() == Qt::LeftButton) - sc->selectionTerrainView.select(tile); - else - sc->selectionTerrainView.erase(tile); - for(auto & d : dirs) - { - auto tilen = tile + d; - if(!controller->map()->isInTheMap(tilen)) - continue; - if(event->button() == Qt::LeftButton) - { - if(controller->map()->getTile(tile).roadType - && controller->map()->getTile(tile).roadType != controller->map()->getTile(tilen).roadType) - continue; - else if(controller->map()->getTile(tile).riverType - && controller->map()->getTile(tile).riverType != controller->map()->getTile(tilen).riverType) - continue; - else if(controller->map()->getTile(tile).terrainType != controller->map()->getTile(tilen).terrainType) - continue; - } - if(event->button() == Qt::LeftButton && sc->selectionTerrainView.selection().count(tilen)) - continue; - if(event->button() == Qt::RightButton && !sc->selectionTerrainView.selection().count(tilen)) - continue; - queue.push_back(tilen); - } - } - - - sc->selectionTerrainView.draw(); - sc->selectionObjectsView.clear(); - sc->selectionObjectsView.draw(); + { + if(event->button() != Qt::RightButton && event->button() != Qt::LeftButton) break; + + std::vector queue; + std::set tilesToFill; + queue.push_back(tileStart); + + const std::array dirs{ int3{1, 0, 0}, int3{-1, 0, 0}, int3{0, 1, 0}, int3{0, -1, 0} }; + + while(!queue.empty()) + { + auto tile = queue.back(); + queue.pop_back(); + tilesToFill.insert(tile); + for(auto & d : dirs) + { + auto tilen = tile + d; + if (tilesToFill.count(tilen)) + continue; + if(!controller->map()->isInTheMap(tilen)) + continue; + if(event->button() == Qt::LeftButton) + { + if(controller->map()->getTile(tile).roadType + && controller->map()->getTile(tile).roadType != controller->map()->getTile(tilen).roadType) + continue; + else if(controller->map()->getTile(tile).riverType + && controller->map()->getTile(tile).riverType != controller->map()->getTile(tilen).riverType) + continue; + else if(controller->map()->getTile(tile).terrainType != controller->map()->getTile(tilen).terrainType) + continue; + } + if(event->button() == Qt::LeftButton && sc->selectionTerrainView.selection().count(tilen)) + continue; + if(event->button() == Qt::RightButton && !sc->selectionTerrainView.selection().count(tilen)) + continue; + queue.push_back(tilen); + } } + std::vector result(tilesToFill.begin(), tilesToFill.end()); + + if(event->button() == Qt::LeftButton) + sc->selectionTerrainView.select(result); + else + sc->selectionTerrainView.erase(result); + + sc->selectionObjectsView.clear(); + break; + } case MapView::SelectionTool::None: sc->selectionTerrainView.clear(); - sc->selectionTerrainView.draw(); if(sc->selectionObjectsView.newObject && sc->selectionObjectsView.isSelected(sc->selectionObjectsView.newObject.get())) { @@ -426,15 +440,14 @@ void MapView::mousePressEvent(QMouseEvent *event) { sc->selectionObjectsView.clear(); sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::SELECTION; - + if(!rubberBand) rubberBand = new QRubberBand(QRubberBand::Rectangle, this); rubberBand->setGeometry(QRect(mapFromScene(mouseStart), QSize())); rubberBand->show(); } } - sc->selectionObjectsView.shift = QPoint(0, 0); - sc->selectionObjectsView.draw(); + sc->selectionObjectsView.setShift(0, 0); } break; } @@ -474,7 +487,7 @@ void MapView::mouseReleaseEvent(QMouseEvent *event) case MapView::SelectionTool::Lasso: { if(event->button() == Qt::RightButton) break; - + std::vectorinitialTiles; //connect with initial tile for(auto i = tilePrev; i != tileStart;) { @@ -489,9 +502,10 @@ void MapView::mouseReleaseEvent(QMouseEvent *event) } } i += dir; - sc->selectionTerrainView.select(i); + initialTiles.push_back(i); } - + sc->selectionTerrainView.select(initialTiles); + //key: y position of tile //value.first: x position of left tile //value.second: x postiion of right tile @@ -532,10 +546,8 @@ void MapView::mouseReleaseEvent(QMouseEvent *event) selectionByY.insert(int3(selectionRange.first, i, sc->level)); } std::set_intersection(selectionByX.begin(), selectionByX.end(), selectionByY.begin(), selectionByY.end(), std::back_inserter(finalSelection)); - for(auto & lassoTile : finalSelection) - sc->selectionTerrainView.select(lassoTile); - - sc->selectionTerrainView.draw(); + sc->selectionTerrainView.select(finalSelection); + break; } @@ -556,8 +568,7 @@ void MapView::mouseReleaseEvent(QMouseEvent *event) else { sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::NOTHING; - sc->selectionObjectsView.shift = QPoint(0, 0); - sc->selectionObjectsView.draw(); + sc->selectionObjectsView.setShift(0, 0); tab = true; } auto selection = sc->selectionObjectsView.getSelection(); @@ -642,10 +653,9 @@ void MapView::dragMoveEvent(QDragMoveEvent * event) if(sc->selectionObjectsView.newObject) { - sc->selectionObjectsView.shift = QPoint(tile.x, tile.y); - sc->selectionObjectsView.selectObject(sc->selectionObjectsView.newObject.get()); sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT; - sc->selectionObjectsView.draw(); + sc->selectionObjectsView.selectObject(sc->selectionObjectsView.newObject.get()); + sc->selectionObjectsView.setShift(tile.x, tile.y); } event->acceptProposedAction(); @@ -655,23 +665,25 @@ void MapView::dragLeaveEvent(QDragLeaveEvent * event) { if(!controller || !controller->map()) return; - + auto * sc = static_cast(scene()); if(!sc) return; - + controller->discardObject(sc->level); } - -bool MapView::viewportEvent(QEvent *event) +void MapView::setViewports() { - if(auto * sc = static_cast(scene())) + if(auto * sc = dynamic_cast(scene())) { auto rect = mapToScene(viewport()->geometry()).boundingRect(); controller->miniScene(sc->level)->viewport.setViewport(rect.x() / 32, rect.y() / 32, rect.width() / 32, rect.height() / 32); + for (auto * layer : sc->getDynamicLayers()) + { + layer->setViewport(rect); + } } - return QGraphicsView::viewportEvent(event); } MapSceneBase::MapSceneBase(int lvl): @@ -682,18 +694,31 @@ MapSceneBase::MapSceneBase(int lvl): void MapSceneBase::initialize(MapController & controller) { - for(auto * layer : getAbstractLayers()) + for(auto * layer : getStaticLayers()) + layer->initialize(controller); + for(auto * layer : getDynamicLayers()) layer->initialize(controller); } -void MapSceneBase::updateViews() +void MapSceneBase::createMap() { - for(auto * layer : getAbstractLayers()) + for(auto * layer : getStaticLayers()) layer->update(); + for(auto * layer : getDynamicLayers()) + layer->createLayer(); +} + +void MapSceneBase::updateMap() +{ + for(auto * layer : getStaticLayers()) + layer->update(); + for(auto * layer : getDynamicLayers()) + layer->redraw(); } MapScene::MapScene(int lvl): MapSceneBase(lvl), + emptyLayer(this), gridView(this), passabilityView(this), selectionTerrainView(this), @@ -708,7 +733,14 @@ MapScene::MapScene(int lvl): connect(&selectionObjectsView, &SelectionObjectsLayer::selectionMade, this, &MapScene::objectSelected); } -std::list MapScene::getAbstractLayers() +std::list MapScene::getStaticLayers() +{ + return { + &emptyLayer + }; +} + +std::list MapScene::getDynamicLayers() { //sequence is important because it defines rendering order return { @@ -722,9 +754,9 @@ std::list MapScene::getAbstractLayers() }; } -void MapScene::updateViews() +void MapScene::createMap() { - MapSceneBase::updateViews(); + MapSceneBase::createMap(); terrainView.show(true); objectsView.show(true); @@ -752,7 +784,7 @@ MinimapScene::MinimapScene(int lvl): { } -std::list MinimapScene::getAbstractLayers() +std::list MinimapScene::getStaticLayers() { //sequence is important because it defines rendering order return { @@ -761,10 +793,16 @@ std::list MinimapScene::getAbstractLayers() }; } -void MinimapScene::updateViews() +std::list MinimapScene::getDynamicLayers() { - MapSceneBase::updateViews(); - + //Nothing here + return {}; +} + +void MinimapScene::createMap() +{ + MapSceneBase::createMap(); + minimapView.show(true); viewport.show(true); } diff --git a/mapeditor/mapview.h b/mapeditor/mapview.h index 6c9c7b021..744932ce9 100644 --- a/mapeditor/mapview.h +++ b/mapeditor/mapview.h @@ -31,12 +31,11 @@ public: MapSceneBase(int lvl); const int level; - - virtual void updateViews(); + virtual void createMap(); + virtual void updateMap(); virtual void initialize(MapController &); - -protected: - virtual std::list getAbstractLayers() = 0; + virtual std::list getStaticLayers() = 0; + virtual std::list getDynamicLayers() = 0; }; class MinimapScene : public MapSceneBase @@ -44,13 +43,12 @@ class MinimapScene : public MapSceneBase public: MinimapScene(int lvl); - void updateViews() override; + void createMap() override; MinimapLayer minimapView; MinimapViewLayer viewport; - -protected: - std::list getAbstractLayers() override; + std::list getStaticLayers() override; + std::list getDynamicLayers() override; }; class MapScene : public MapSceneBase @@ -59,8 +57,11 @@ class MapScene : public MapSceneBase public: MapScene(int lvl); - void updateViews() override; + void createMap() override; + std::list getStaticLayers() override; + std::list getDynamicLayers() override; + EmptyLayer emptyLayer; GridLayer gridView; PassabilityLayer passabilityView; SelectionTerrainLayer selectionTerrainView; @@ -75,10 +76,8 @@ signals: public slots: void terrainSelected(bool anything); void objectSelected(bool anything); - -protected: - std::list getAbstractLayers() override; +protected: bool isTerrainSelected; bool isObjectSelected; @@ -100,6 +99,7 @@ public: SelectionTool selectionTool; public slots: + void resizeEvent (QResizeEvent * event) override; void mouseMoveEvent(QMouseEvent * mouseEvent) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; @@ -107,8 +107,8 @@ public slots: void dragMoveEvent(QDragMoveEvent *event) override; void dragLeaveEvent(QDragLeaveEvent *event) override; void dropEvent(QDropEvent * event) override; - void cameraChanged(const QPointF & pos); + void setViewports(); signals: void openObjectProperties(CGObjectInstance *, bool switchTab); @@ -116,7 +116,6 @@ signals: //void viewportChanged(const QRectF & rect); protected: - bool viewportEvent(QEvent *event) override; private: MapController * controller = nullptr; diff --git a/mapeditor/scenelayer.cpp b/mapeditor/scenelayer.cpp index 7f34be7ff..49a25f452 100644 --- a/mapeditor/scenelayer.cpp +++ b/mapeditor/scenelayer.cpp @@ -29,15 +29,32 @@ void AbstractLayer::initialize(MapController & controller) void AbstractLayer::show(bool show) { - if(isShown == show) - return; - isShown = show; - + redraw(); } -void AbstractLayer::redraw() + +int AbstractLayer::mapWidthPx() const +{ + return map ? map->width * tileSize : 0; +} + +int AbstractLayer::mapHeightPx() const +{ + return map ? map->height * tileSize : 0; +} + +int AbstractLayer::toInt(double value) const +{ + return static_cast(std::round(value)); // is rounded explicitly in order to avoid rounding down unprecise double values +} + +AbstractFixedLayer::AbstractFixedLayer(MapSceneBase * s): AbstractLayer(s) +{ +} + +void AbstractFixedLayer::redraw() { if(item) { @@ -55,65 +72,284 @@ void AbstractLayer::redraw() } } -GridLayer::GridLayer(MapSceneBase * s): AbstractLayer(s) +AbstractViewportLayer::AbstractViewportLayer(MapSceneBase * s): AbstractLayer(s) { } -void GridLayer::update() +void AbstractViewportLayer::createLayer() +{ + QListemptyList; + items.reset(scene->createItemGroup(emptyList)); +} + +void AbstractViewportLayer::setViewport(const QRectF & viewPort) { if(!map) return; - - pixmap.reset(new QPixmap(map->width * 32, map->height * 32)); - pixmap->fill(Qt::transparent); - QPainter painter(pixmap.get()); - painter.setPen(QColor(0, 0, 0, 190)); - - for(int j = 0; j < map->height; ++j) + if (items->boundingRect().contains(viewPort)) + return; + + std::vector outOfScreenSectors; + for (QGraphicsItem * sector : getAllSectors()) { - painter.drawLine(0, j * 32, map->width * 32 - 1, j * 32); + if (!viewPort.intersects(sector->sceneBoundingRect())) + outOfScreenSectors.push_back(sector); } - for(int i = 0; i < map->width; ++i) + for (QGraphicsItem * sector : outOfScreenSectors) { - painter.drawLine(i * 32, 0, i * 32, map->height * 32 - 1); + removeSector(sector); } - + + std::vector newAreas; + + int left = toInt(viewPort.left()); + int right = toInt(viewPort.right()); + int top = toInt(viewPort.top()); + int bottom = toInt(viewPort.bottom()); + int startX = left - (left % sectorSize); + int limitX = std::min(right + (sectorSize - right % sectorSize), mapWidthPx()); + int startY = top - (top % sectorSize); + int limitY = std::min(bottom + (sectorSize - bottom % sectorSize), mapHeightPx()); + + for (int x = startX; x < limitX; x += sectorSize) + { + for (int y = startY; y < limitY; y += sectorSize) + { + int width = x + sectorSize < limitX ? sectorSize : limitX - x; + int height = y + sectorSize < limitY ? sectorSize : limitY - y; + QRectF area(x, y, width, height); + if (!items->boundingRect().intersects(area)) + newAreas.emplace_back(area); + } + } + + for(QRectF newSection : newAreas) + { + QGraphicsItem * sector = draw(newSection); + if (sector) + addSector(sector); + } +} + +void AbstractViewportLayer::update() +{ redraw(); } -PassabilityLayer::PassabilityLayer(MapSceneBase * s): AbstractLayer(s) +void AbstractViewportLayer::redraw() { + std::set allSectors; + for (auto * sector : getAllSectors()) + allSectors.insert(sector); + redrawSectors(allSectors); } -void PassabilityLayer::update() +void AbstractViewportLayer::redraw(const std::vector & tiles) { - if(!map) - return; - - pixmap.reset(new QPixmap(map->width * 32, map->height * 32)); - pixmap->fill(Qt::transparent); - - QPainter painter(pixmap.get()); - for(int j = 0; j < map->height; ++j) + std::set sectorsToRedraw = getContainingSectors(tiles); + redrawSectors(sectorsToRedraw); +} + +void AbstractViewportLayer::redrawWithSurroundingTiles(const std::vector & tiles) +{ + int maxX = 0; + int maxY = 0; + int minX = INT_MAX; + int minY = INT_MAX; + for (const int3 tile : tiles) { - for(int i = 0; i < map->width; ++i) + maxX = std::max(tile.x, maxX); + maxY = std::max(tile.y, maxY); + minX = std::min(tile.x, minX); + minY = std::min(tile.y, minY); + } + + QRectF bounds((minX - 2) * tileSize, (minY - 2) * tileSize, (maxX - minX + 4) * tileSize, (maxY - minY + 4) * tileSize); //tiles start with 1, QRectF from 0 + redraw({bounds}); +} + +void AbstractViewportLayer::redraw(const std::set & objects) +{ + std::vector areas(objects.size()); + for (const CGObjectInstance * object : objects) + { + areas.push_back(getObjectArea(object)); + } + redraw(areas); +} + +void AbstractViewportLayer::redraw(const std::vector & areas) +{ + std::set intersectingSectors; + for (QGraphicsItem * existingSector : getAllSectors()) + { + for (auto area : areas) { - auto tl = map->getTile(int3(i, j, scene->level)); - if(tl.blocked() || tl.visitable()) + if (existingSector->sceneBoundingRect().intersects(area)) { - painter.fillRect(i * 32, j * 32, 31, 31, tl.visitable() ? QColor(200, 200, 0, 64) : QColor(255, 0, 0, 64)); + intersectingSectors.insert(existingSector); } } } - + redrawSectors(intersectingSectors); +} + +QRectF AbstractViewportLayer::getObjectArea(const CGObjectInstance * object) const +{ + auto pos = object->pos; + int x = ((pos.x + 1) * tileSize) - (object->getWidth() * tileSize); //Qt set 0,0 point on the top right corner, CGObjectInstance on the bottom left + int y = ((pos.y + 1) * tileSize) - (object->getHeight() * tileSize); + QRectF objectArea(x, y, object->getWidth() * tileSize, object->getHeight() * tileSize); + return objectArea; +} + +void AbstractViewportLayer::addSector(QGraphicsItem * sector) +{ + items->addToGroup(sector); +} + +void AbstractViewportLayer::removeSector(QGraphicsItem * sector) +{ + items->removeFromGroup(sector); + delete sector; +} + +void AbstractViewportLayer::redrawSectors(std::set & sectors) +{ + std::set sectorsToRemove; + + for (QGraphicsItem * existingSectors : getAllSectors()) + { + for (QGraphicsItem * sector : sectors) + { + if (existingSectors->sceneBoundingRect().contains(sector->sceneBoundingRect())) + sectorsToRemove.insert(existingSectors); + } + } + for (QGraphicsItem * sectorToRemove : sectorsToRemove) + { + addSector(draw(sectorToRemove->sceneBoundingRect())); + removeSector(sectorToRemove); + } +} + +const QList AbstractViewportLayer::getAllSectors() const //returning const is necessary to avoid "range-loop might detach Qt container" problem +{ + QList emptyList; + return items ? items->childItems() : emptyList; +} + +std::set AbstractViewportLayer::getContainingSectors(const std::vector & tiles) const +{ + std::set result; + for (QGraphicsItem * existingSector : getAllSectors()) { + for (const int3 tile : tiles) + { + if (existingSector->sceneBoundingRect().contains(QPointF(tile.x * tileSize, tile.y * tileSize))) + { + result.insert(existingSector); + break; + } + } + } + return result; +} + +std::set AbstractViewportLayer::getIntersectingSectors(const std::vector & areas) const +{ + std::set result; + for (QGraphicsItem * existingSector : getAllSectors()) { + for (QRectF area : areas) + { + if (existingSector->sceneBoundingRect().intersects(area)) + { + result.insert(existingSector); + } + } + } + return result; +} + +EmptyLayer::EmptyLayer(MapSceneBase * s): AbstractFixedLayer(s) +{ + isShown = true; +} + +void EmptyLayer::update() +{ + if(!map) + return; + + pixmap = std::make_unique(map->width * 32, map->height * 32); redraw(); } -ObjectPickerLayer::ObjectPickerLayer(MapSceneBase * s): AbstractLayer(s) +GridLayer::GridLayer(MapSceneBase * s): AbstractViewportLayer(s) { } -void ObjectPickerLayer::highlight(std::function predicate) +QGraphicsItem * GridLayer::draw(const QRectF & section) +{ + QPixmap pixmap(toInt(section.width()), toInt(section.height())); + pixmap.fill(Qt::transparent); + if (isShown) + { + QPainter painter(&pixmap); + painter.setPen(QColor(0, 0, 0, 190)); + + for(int j = 0; j <= pixmap.height(); j += tileSize) + { + painter.drawLine(0, j, pixmap.width(), j); + } + for(int i = 0; i <= pixmap.width(); i += tileSize) + { + painter.drawLine(i, 0, i, pixmap.height()); + } + } + + QGraphicsItem * result = scene->addPixmap(pixmap); + result->setPos(section.x(), section.y()); + + return result; +} + +PassabilityLayer::PassabilityLayer(MapSceneBase * s): AbstractViewportLayer(s) +{ +} + +QGraphicsItem * PassabilityLayer::draw(const QRectF & section) +{ + + QPixmap pixmap(toInt(section.width()), toInt(section.height())); + pixmap.fill(Qt::transparent); + + if(isShown) + { + QPainter painter(&pixmap); + for(int j = 0; j <= pixmap.height(); j += tileSize) + { + for(int i = 0; i < pixmap.width(); i += tileSize) + { + auto tl = map->getTile(int3(toInt(section.x())/tileSize + i/tileSize, toInt(section.y())/tileSize + j/tileSize, scene->level)); + if(tl.blocked() || tl.visitable()) + { + painter.fillRect(i, j, 31, 31, tl.visitable() ? QColor(200, 200, 0, 64) : QColor(255, 0, 0, 64)); + } + } + } + } + + QGraphicsItem * result = scene->addPixmap(pixmap); + result->setPos(section.x(), section.y()); + + return result; +} + +ObjectPickerLayer::ObjectPickerLayer(MapSceneBase * s): AbstractViewportLayer(s) +{ +} + +void ObjectPickerLayer::highlight(const std::function & predicate) { if(!map) return; @@ -151,29 +387,33 @@ void ObjectPickerLayer::clear() isActive = false; } -void ObjectPickerLayer::update() +QGraphicsItem * ObjectPickerLayer::draw(const QRectF & section) { - if(!map) - return; - - pixmap.reset(new QPixmap(map->width * 32, map->height * 32)); - pixmap->fill(Qt::transparent); - if(isActive) - pixmap->fill(QColor(255, 255, 255, 128)); - - - QPainter painter(pixmap.get()); + + int offsetX = toInt(section.x()); + int offsetY = toInt(section.y()); + QPixmap pixmap(toInt(section.width()), toInt(section.height())); + pixmap.fill(Qt::transparent); + + if(isVisible()) + pixmap.fill(QColor(255, 255, 255, 128)); + + + QPainter painter(&pixmap); painter.setCompositionMode(QPainter::CompositionMode_Source); - for(auto * obj : possibleObjects) + for(const auto * obj : possibleObjects) { if(obj->pos.z != scene->level) continue; - - for(auto & pos : obj->getBlockedPos()) - painter.fillRect(pos.x * 32, pos.y * 32, 32, 32, QColor(255, 211, 0, 64)); + + for(const auto & pos : obj->getBlockedPos()) + painter.fillRect(pos.x * tileSize - offsetX, pos.y * tileSize - offsetY, tileSize, tileSize, QColor(255, 211, 0, 64)); } - painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - redraw(); + + QGraphicsItem * result = scene->addPixmap(pixmap); + result->setPos(section.x(), section.y()); + + return result; } void ObjectPickerLayer::select(const CGObjectInstance * obj) @@ -181,92 +421,73 @@ void ObjectPickerLayer::select(const CGObjectInstance * obj) if(obj && possibleObjects.count(obj)) { clear(); - selectionMade(obj); + Q_EMIT selectionMade(obj); } } void ObjectPickerLayer::discard() { clear(); - selectionMade(nullptr); + Q_EMIT selectionMade(nullptr); } -SelectionTerrainLayer::SelectionTerrainLayer(MapSceneBase * s): AbstractLayer(s) +SelectionTerrainLayer::SelectionTerrainLayer(MapSceneBase * s): AbstractViewportLayer(s) { } -void SelectionTerrainLayer::update() +QGraphicsItem * SelectionTerrainLayer::draw(const QRectF & section) { - if(!map) - return; - - area.clear(); - areaAdd.clear(); - areaErase.clear(); - onSelection(); - - pixmap.reset(new QPixmap(map->width * 32, map->height * 32)); - pixmap->fill(Qt::transparent); - - redraw(); -} + int offsetX = toInt(section.x()); + int offsetY = toInt(section.y()); + QPixmap pixmap(toInt(section.width()), toInt(section.height())); + pixmap.fill(Qt::transparent); + + QPainter painter(&pixmap); -void SelectionTerrainLayer::draw() -{ - if(!pixmap) - return; - - QPainter painter(pixmap.get()); painter.setCompositionMode(QPainter::CompositionMode_Source); - for(auto & t : areaAdd) + for(const auto & t : area) { - painter.fillRect(t.x * 32, t.y * 32, 31, 31, QColor(128, 128, 128, 96)); + if(section.contains(t.x * tileSize, t.y * tileSize)) + painter.fillRect(t.x * tileSize - offsetX, t.y * tileSize - offsetY, 31, 31, QColor(128, 128, 128, 96)); } - for(auto & t : areaErase) - { - painter.fillRect(t.x * 32, t.y * 32, 31, 31, QColor(0, 0, 0, 0)); - } - - areaAdd.clear(); - areaErase.clear(); - - redraw(); + + QGraphicsPixmapItem * result = scene->addPixmap(pixmap); + result->setPos(section.x(), section.y()); + + return result; } -void SelectionTerrainLayer::select(const int3 & tile) +void SelectionTerrainLayer::select(const std::vector & tiles) { - if(!map || !map->isInTheMap(tile)) - return; - - if(!area.count(tile)) + for (int3 tile : tiles) { - area.insert(tile); - areaAdd.insert(tile); - areaErase.erase(tile); + if(!area.count(tile)) + { + area.insert(tile); + } } + redraw(tiles); onSelection(); } -void SelectionTerrainLayer::erase(const int3 & tile) +void SelectionTerrainLayer::erase(const std::vector & tiles) { - if(!map || !map->isInTheMap(tile)) - return; - - if(area.count(tile)) + for (int3 tile : tiles) { - area.erase(tile); - areaErase.insert(tile); - areaAdd.erase(tile); + if(area.count(tile)) + { + area.erase(tile); + } } + redraw(tiles); onSelection(); } void SelectionTerrainLayer::clear() { - areaErase = area; - areaAdd.clear(); area.clear(); onSelection(); + redraw(); } const std::set & SelectionTerrainLayer::selection() const @@ -276,157 +497,87 @@ const std::set & SelectionTerrainLayer::selection() const void SelectionTerrainLayer::onSelection() { - selectionMade(!area.empty()); + Q_EMIT selectionMade(!area.empty()); } -TerrainLayer::TerrainLayer(MapSceneBase * s): AbstractLayer(s) +TerrainLayer::TerrainLayer(MapSceneBase * s): AbstractViewportLayer(s) { } -void TerrainLayer::update() +void TerrainLayer::redrawTerrain(const std::vector & tiles) { - if(!map) - return; - - pixmap.reset(new QPixmap(map->width * 32, map->height * 32)); - draw(false); + redrawWithSurroundingTiles(tiles); } -void TerrainLayer::setDirty(const int3 & tile) +QGraphicsItem * TerrainLayer::draw(const QRectF & section) { - dirty.insert(tile); -} + int left = toInt(section.left()); + int right = toInt(section.right()); + int top = toInt(section.top()); + int bottom = toInt(section.bottom()); + QPixmap pixmap(toInt(section.width()), toInt(section.height())); + pixmap.fill(Qt::transparent); -void TerrainLayer::draw(bool onlyDirty) -{ - if(!pixmap) - return; - - if(!map) - return; - - QPainter painter(pixmap.get()); - //painter.setCompositionMode(QPainter::CompositionMode_Source); - - if(onlyDirty) + QPainter painter(&pixmap); + + QPointF offset = section.topLeft(); + + for(int x = left/tileSize; x < right/tileSize; ++x) { - std::set forRedrawing(dirty); - std::set neighbours; - for(auto & t : dirty) + for(int y = top/tileSize; y < bottom/tileSize; ++y) { - for(auto & tt : int3::getDirs()) - { - if(map->isInTheMap(t + tt)) - neighbours.insert(t + tt); - } - } - for(auto & t : neighbours) - { - for(auto & tt : int3::getDirs()) - { - forRedrawing.insert(t); - if(map->isInTheMap(t + tt)) - forRedrawing.insert(t + tt); - } - } - for(auto & t : forRedrawing) - { - handler->drawTerrainTile(painter, t.x, t.y, scene->level); - handler->drawRiver(painter, t.x, t.y, scene->level); - handler->drawRoad(painter, t.x, t.y, scene->level); + handler->drawTerrainTile(painter, x, y, scene->level, offset); + handler->drawRiver(painter, x, y, scene->level, offset); + handler->drawRoad(painter, x, y, scene->level, offset); } } - else + + QGraphicsPixmapItem * result = scene->addPixmap(pixmap); + result->setPos(section.x(), section.y()); + + return result; +} + +ObjectsLayer::ObjectsLayer(MapSceneBase * s): AbstractViewportLayer(s) +{ +} + +QGraphicsItem * ObjectsLayer::draw(const QRectF & section) +{ + int left = toInt(section.left()); + int right = toInt(section.right()); + int top = toInt(section.top()); + int bottom = toInt(section.bottom()); + QPixmap pixmap(toInt(section.width()), toInt(section.height())); + pixmap.fill(Qt::transparent); + + if (isShown) { - for(int j = 0; j < map->height; ++j) + QPainter painter(&pixmap); + + QPointF offset = section.topLeft(); + + int margin = 2; // margin is necessary to properly display flags on heroes on a border between two sections + + for(int x = (left - margin)/tileSize; x < (right + margin)/tileSize; ++x) { - for(int i = 0; i < map->width; ++i) + for(int y = (top - margin)/tileSize; y < (bottom + margin)/tileSize; ++y) { - handler->drawTerrainTile(painter, i, j, scene->level); - handler->drawRiver(painter, i, j, scene->level); - handler->drawRoad(painter, i, j, scene->level); + handler->drawObjects(painter, x, y, scene->level, offset, lockedObjects); } } } - - dirty.clear(); - redraw(); + + QGraphicsPixmapItem * result = scene->addPixmap(pixmap); + result->setPos(section.x(), section.y()); + + return result; } -ObjectsLayer::ObjectsLayer(MapSceneBase * s): AbstractLayer(s) +void ObjectsLayer::redrawObjects(const std::set & objects) { -} - -void ObjectsLayer::update() -{ - if(!map) - return; - - pixmap.reset(new QPixmap(map->width * 32, map->height * 32)); - pixmap->fill(Qt::transparent); - draw(false); -} - -void ObjectsLayer::draw(bool onlyDirty) -{ - if(!pixmap) - return; - - if(!map) - return; - - QPainter painter(pixmap.get()); - - if(onlyDirty) - { - //objects could be modified - for(auto * obj : objDirty) - setDirty(obj); - - //clear tiles which will be redrawn. It's needed because some object could be replaced - painter.setCompositionMode(QPainter::CompositionMode_Source); - for(auto & p : dirty) - painter.fillRect(p.x * 32, p.y * 32, 32, 32, Qt::transparent); - painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - - for(auto & p : dirty) - handler->drawObjects(painter, p.x, p.y, p.z, lockedObjects); - } - else - { - pixmap->fill(Qt::transparent); - for(int j = 0; j < map->height; ++j) - { - for(int i = 0; i < map->width; ++i) - { - handler->drawObjects(painter, i, j, scene->level, lockedObjects); - } - } - } - - dirty.clear(); - redraw(); -} - -void ObjectsLayer::setDirty(int x, int y) -{ - int3 pos(x, y, scene->level); - if(map->isInTheMap(pos)) - dirty.insert(pos); -} - -void ObjectsLayer::setDirty(const CGObjectInstance * object) -{ - objDirty.insert(object); - //mark tiles under object as dirty - for(int j = 0; j < object->getHeight(); ++j) - { - for(int i = 0; i < object->getWidth(); ++i) - { - setDirty(object->anchorPos().x - i, object->anchorPos().y - j); - } - } + redraw(objects); } void ObjectsLayer::setLockObject(const CGObjectInstance * object, bool lock) @@ -442,64 +593,63 @@ void ObjectsLayer::unlockAll() lockedObjects.clear(); } -SelectionObjectsLayer::SelectionObjectsLayer(MapSceneBase * s): AbstractLayer(s), newObject(nullptr) +SelectionObjectsLayer::SelectionObjectsLayer(MapSceneBase * s): AbstractViewportLayer(s), newObject(nullptr) { } -void SelectionObjectsLayer::update() -{ - if(!map) - return; - - selectedObjects.clear(); - onSelection(); - shift = QPoint(); - newObject.reset(); - - pixmap.reset(new QPixmap(map->width * 32, map->height * 32)); - //pixmap->fill(QColor(0, 0, 0, 0)); - - draw(); -} -void SelectionObjectsLayer::draw() +QGraphicsItem * SelectionObjectsLayer::draw(const QRectF & section) { - if(!pixmap) - return; - - pixmap->fill(Qt::transparent); - - QPainter painter(pixmap.get()); - painter.setCompositionMode(QPainter::CompositionMode_Source); - painter.setPen(Qt::white); - - for(auto * obj : selectedObjects) + QPixmap pixmap(toInt(section.width()), toInt(section.height())); + pixmap.fill(Qt::transparent); + + if (isShown) { - if(obj != newObject.get()) + QPainter painter(&pixmap); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.setPen(Qt::white); + + QPointF offset = section.topLeft(); + + for(auto * obj : selectedObjects) { - QRect bbox(obj->anchorPos().x, obj->anchorPos().y, 1, 1); - for(auto & t : obj->getBlockedPos()) + auto objectArea = getObjectArea(obj); + if(obj != newObject.get() && section.intersects(objectArea)) { - QPoint topLeft(std::min(t.x, bbox.topLeft().x()), std::min(t.y, bbox.topLeft().y())); - bbox.setTopLeft(topLeft); - QPoint bottomRight(std::max(t.x, bbox.bottomRight().x()), std::max(t.y, bbox.bottomRight().y())); - bbox.setBottomRight(bottomRight); + auto pos = obj->anchorPos(); + QRectF bbox(pos.x, pos.y, 1, 1); + for(const auto & t : obj->getBlockedPos()) + { + QPointF topLeft(std::min(t.x * 1.0, bbox.topLeft().x()), std::min(t.y * 1.0, bbox.topLeft().y())); + bbox.setTopLeft(topLeft); + QPointF bottomRight(std::max(t.x * 1.0, bbox.bottomRight().x()), std::max(t.y * 1.0, bbox.bottomRight().y())); + bbox.setBottomRight(bottomRight); + } + //selection box's size was decreased by 1 px to get rid of a persistent bug + //with displaying a box on a border of two sectors. Bite me. + + painter.setOpacity(1.0); + QRectF rect((bbox.x() * tileSize + 1) - offset.x(), (bbox.y() * tileSize + 1) - offset.y(), (bbox.width() * tileSize) - 2, (bbox.height() * tileSize) - 2); + painter.drawRect(rect); + } + + if(selectionMode == SelectionMode::MOVEMENT && (shift.x() || shift.y())) + { + objectArea.moveTo(objectArea.topLeft() + (shift * tileSize)); + if (section.intersects(objectArea)) + { + painter.setOpacity(0.7); + auto newPos = QPoint(obj->anchorPos().x, obj->anchorPos().y) + shift; + handler->drawObjectAt(painter, obj, newPos.x(), newPos.y(), offset); + } } - - painter.setOpacity(1.0); - painter.drawRect(bbox.x() * 32, bbox.y() * 32, bbox.width() * 32, bbox.height() * 32); - } - - //show translation - if(selectionMode == SelectionMode::MOVEMENT && (shift.x() || shift.y())) - { - painter.setOpacity(0.7); - auto newPos = QPoint(obj->anchorPos().x, obj->anchorPos().y) + shift; - handler->drawObjectAt(painter, obj, newPos.x(), newPos.y()); } } - - redraw(); + + QGraphicsPixmapItem * result = scene->addPixmap(pixmap); + result->setPos(section.x(), section.y()); + + return result; } CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGObjectInstance * ignore) const @@ -558,7 +708,8 @@ void SelectionObjectsLayer::selectObjects(int x1, int y1, int x2, int y2) if(y1 > y2) std::swap(y1, y2); - + + std::set selectedObjects; for(int j = y1; j < y2; ++j) { for(int i = x1; i < x2; ++i) @@ -567,25 +718,36 @@ void SelectionObjectsLayer::selectObjects(int x1, int y1, int x2, int y2) { for(auto & o : handler->getObjects(i, j, scene->level)) if(!lockedObjects.count(o.obj)) - selectObject(const_cast(o.obj), false); //do not inform about each object added + { + selectedObjects.insert(const_cast(o.obj)); + } } } } - onSelection(); + selectObjects(selectedObjects); } -void SelectionObjectsLayer::selectObject(CGObjectInstance * obj, bool inform /* = true */) +void SelectionObjectsLayer::selectObject(CGObjectInstance * obj) { selectedObjects.insert(obj); - if (inform) + onSelection(); + redraw({obj}); +} + +void SelectionObjectsLayer::selectObjects(const std::set & objs) +{ + for (CGObjectInstance * obj : objs) { - onSelection(); + selectedObjects.insert(obj); } + onSelection(); + redraw(objs); } void SelectionObjectsLayer::deselectObject(CGObjectInstance * obj) { selectedObjects.erase(obj); + redraw({obj}); } bool SelectionObjectsLayer::isSelected(const CGObjectInstance * obj) const @@ -601,22 +763,47 @@ std::set SelectionObjectsLayer::getSelection() const void SelectionObjectsLayer::clear() { selectedObjects.clear(); - onSelection(); shift.setX(0); shift.setY(0); + redraw(); } void SelectionObjectsLayer::onSelection() { - selectionMade(!selectedObjects.empty()); + Q_EMIT selectionMade(!selectedObjects.empty()); } -void SelectionObjectsLayer::setLockObject(const CGObjectInstance * object, bool lock) +void SelectionObjectsLayer::setShift(int x, int y) +{ + std::vectorareas; + + if(shift.x() || shift.y()) + { + for (auto * selectedObject : selectedObjects) + { + QRectF formerArea = getObjectArea(selectedObject); + formerArea.moveTo(formerArea.topLeft() + (shift * tileSize)); + areas.emplace_back(formerArea); + } + } + + shift = QPoint(x, y); + for (auto * selectedObject : selectedObjects) + { + QRectF area = getObjectArea(selectedObject); + area.moveTo(area.topLeft() + (shift * tileSize)); + areas.emplace_back(area); + } + redraw(areas); +} + +void SelectionObjectsLayer::setLockObject(CGObjectInstance * object, bool lock) { if(lock) lockedObjects.insert(object); else lockedObjects.erase(object); + redraw({object}); } void SelectionObjectsLayer::unlockAll() @@ -624,18 +811,18 @@ void SelectionObjectsLayer::unlockAll() lockedObjects.clear(); } -MinimapLayer::MinimapLayer(MapSceneBase * s): AbstractLayer(s) +MinimapLayer::MinimapLayer(MapSceneBase * s): AbstractFixedLayer(s) { - + } void MinimapLayer::update() { if(!map) return; - - pixmap.reset(new QPixmap(map->width, map->height)); - + + pixmap = std::make_unique(map->width, map->height); + QPainter painter(pixmap.get()); //coordinate transformation for(int j = 0; j < map->height; ++j) @@ -649,7 +836,7 @@ void MinimapLayer::update() redraw(); } -MinimapViewLayer::MinimapViewLayer(MapSceneBase * s): AbstractLayer(s) +MinimapViewLayer::MinimapViewLayer(MapSceneBase * s): AbstractFixedLayer(s) { } @@ -657,9 +844,9 @@ void MinimapViewLayer::update() { if(!map) return; - - pixmap.reset(new QPixmap(map->width, map->height)); - + + pixmap = std::make_unique(map->width, map->height); + draw(); } diff --git a/mapeditor/scenelayer.h b/mapeditor/scenelayer.h index cccc130a4..ec55682a3 100644 --- a/mapeditor/scenelayer.h +++ b/mapeditor/scenelayer.h @@ -28,60 +28,111 @@ class AbstractLayer : public QObject Q_OBJECT public: AbstractLayer(MapSceneBase * s); - - virtual void update() = 0; - - void show(bool show); - void redraw(); void initialize(MapController & controller); - + void show(bool show); + virtual void update() = 0; + virtual void redraw() = 0; + protected: + int mapWidthPx() const; + int mapHeightPx() const; + int toInt(double value) const; + MapSceneBase * scene; CMap * map = nullptr; MapHandler * handler = nullptr; bool isShown = false; - + const int tileSize = 32; +}; + +class AbstractFixedLayer : public AbstractLayer +{ + Q_OBJECT +public: + AbstractFixedLayer(MapSceneBase * s); + void redraw(); + +protected: std::unique_ptr pixmap; QPixmap emptyPixmap; - + private: std::unique_ptr item; }; +class AbstractViewportLayer : public AbstractLayer +{ +public: + AbstractViewportLayer(MapSceneBase * s); + void createLayer(); + void setViewport(const QRectF & _viewPort); -class GridLayer: public AbstractLayer + void update(); + void redraw(); +protected: + virtual QGraphicsItem * draw(const QRectF & area) = 0; + void redraw(const std::vector & tiles); + void redrawWithSurroundingTiles(const std::vector & tiles); + void redraw(const std::set & objects); + void redraw(const std::vector & areas); + QRectF getObjectArea(const CGObjectInstance * object) const; +private: + void addSector(QGraphicsItem * item); + void removeSector(QGraphicsItem * item); + void redrawSectors(std::set & items); + const QList getAllSectors() const; + + std::set getContainingSectors(const std::vector & tiles) const; + std::set getIntersectingSectors(const std::vector & areas) const; + std::unique_ptr items; + const int sectorSizeInTiles = 10; + const int sectorSize = sectorSizeInTiles * tileSize; +}; + +class EmptyLayer: public AbstractFixedLayer +{ + Q_OBJECT +public: + EmptyLayer(MapSceneBase * s); + + void update() override; +}; + +class GridLayer: public AbstractViewportLayer { Q_OBJECT public: GridLayer(MapSceneBase * s); - - void update() override; + +protected: + QGraphicsItem * draw(const QRectF & section) override; }; -class PassabilityLayer: public AbstractLayer +class PassabilityLayer: public AbstractViewportLayer { Q_OBJECT public: PassabilityLayer(MapSceneBase * s); - - void update() override; + +protected: + QGraphicsItem * draw(const QRectF & section) override; }; -class SelectionTerrainLayer: public AbstractLayer +class SelectionTerrainLayer: public AbstractViewportLayer { Q_OBJECT public: SelectionTerrainLayer(MapSceneBase* s); - - void update() override; - - void draw(); - void select(const int3 & tile); - void erase(const int3 & tile); + + void select(const std::vector & tiles); + void erase(const std::vector & tiles); void clear(); - + const std::set & selection() const; +protected: + QGraphicsItem * draw(const QRectF & section) override; + signals: void selectionMade(bool anythingSelected); @@ -94,52 +145,39 @@ private: }; -class TerrainLayer: public AbstractLayer +class TerrainLayer: public AbstractViewportLayer { Q_OBJECT public: TerrainLayer(MapSceneBase * s); - - void update() override; - - void draw(bool onlyDirty = true); - void setDirty(const int3 & tile); - -private: - std::set dirty; + void redrawTerrain(const std::vector & tiles); + +protected: + QGraphicsItem * draw(const QRectF & section) override; }; -class ObjectsLayer: public AbstractLayer +class ObjectsLayer: public AbstractViewportLayer { Q_OBJECT public: ObjectsLayer(MapSceneBase * s); - - void update() override; - - void draw(bool onlyDirty = true); - - void setDirty(int x, int y); - void setDirty(const CGObjectInstance * object); + void redrawObjects(const std::set & objects); void setLockObject(const CGObjectInstance * object, bool lock); void unlockAll(); - +protected: + QGraphicsItem * draw(const QRectF & section) override; private: - std::set objDirty; std::set lockedObjects; - std::set dirty; }; -class ObjectPickerLayer: public AbstractLayer +class ObjectPickerLayer: public AbstractViewportLayer { Q_OBJECT public: ObjectPickerLayer(MapSceneBase * s); - - void update() override; bool isVisible() const; template @@ -147,14 +185,16 @@ public: { highlight([](const CGObjectInstance * o){ return dynamic_cast(o); }); } - - void highlight(std::function predicate); - + + void highlight(const std::function & predicate); + void clear(); void select(const CGObjectInstance *); void discard(); - +protected: + QGraphicsItem * draw(const QRectF & section) override; + signals: void selectionMade(const CGObjectInstance *); @@ -164,7 +204,7 @@ private: }; -class SelectionObjectsLayer: public AbstractLayer +class SelectionObjectsLayer: public AbstractViewportLayer { Q_OBJECT public: @@ -174,38 +214,37 @@ public: }; SelectionObjectsLayer(MapSceneBase* s); - - void update() override; - - void draw(); - + CGObjectInstance * selectObjectAt(int x, int y, const CGObjectInstance * ignore = nullptr) const; void selectObjects(int x1, int y1, int x2, int y2); - void selectObject(CGObjectInstance *, bool inform = true); + void selectObject(CGObjectInstance *); void deselectObject(CGObjectInstance *); bool isSelected(const CGObjectInstance *) const; std::set getSelection() const; void clear(); - void setLockObject(const CGObjectInstance * object, bool lock); + void setShift(int x, int y); + void setLockObject(CGObjectInstance * object, bool lock); void unlockAll(); - - QPoint shift; std::shared_ptr newObject; - //FIXME: magic number + QPoint shift; SelectionMode selectionMode = SelectionMode::NOTHING; +protected: + QGraphicsItem * draw(const QRectF & section) override; + signals: void selectionMade(bool anythingSelected); private: + void selectObjects(const std::set & objs); std::set selectedObjects; std::set lockedObjects; void onSelection(); }; -class MinimapLayer: public AbstractLayer +class MinimapLayer: public AbstractFixedLayer { public: MinimapLayer(MapSceneBase * s); @@ -213,7 +252,7 @@ public: void update() override; }; -class MinimapViewLayer: public AbstractLayer +class MinimapViewLayer: public AbstractFixedLayer { public: MinimapViewLayer(MapSceneBase * s);