/** * Returns a new stamp where all variations have been rotated in the given * \a direction. */ TileStamp TileStamp::rotated(RotateDirection direction) const { TileStamp rotated(*this); rotated.d.detach(); for (const TileStampVariation &variation : rotated.variations()) { const QRect mapRect(QPoint(), variation.map->size()); QSize rotatedSize; for (auto layer : variation.map->tileLayers()) { TileLayer *tileLayer = static_cast<TileLayer*>(layer); // Synchronize tile layer size to map size (assumes map contains all layers) if (tileLayer->rect() != mapRect) { tileLayer->resize(mapRect.size(), tileLayer->position()); tileLayer->setPosition(0, 0); } if (variation.map->orientation() == Map::Hexagonal) tileLayer->rotateHexagonal(direction, variation.map); else tileLayer->rotate(direction); rotatedSize = tileLayer->size(); } variation.map->setWidth(rotatedSize.width()); variation.map->setHeight(rotatedSize.height()); } return rotated; }
void BucketFillTool::randomFill(TileLayer &tileLayer, const QRegion ®ion) const { if (region.isEmpty() || mRandomCellPicker.isEmpty()) return; for (const QRect &rect : region.translated(-tileLayer.position()).rects()) { for (int _x = rect.left(); _x <= rect.right(); ++_x) { for (int _y = rect.top(); _y <= rect.bottom(); ++_y) { tileLayer.setCell(_x, _y, mRandomCellPicker.pick()); } } } }
/** * Returns a new stamp where all variations have been flipped in the given * \a direction. */ TileStamp TileStamp::flipped(FlipDirection direction) const { TileStamp flipped(*this); flipped.d.detach(); for (const TileStampVariation &variation : flipped.variations()) { const QRect mapRect(QPoint(), variation.map->size()); for (auto layer : variation.map->tileLayers()) { TileLayer *tileLayer = static_cast<TileLayer*>(layer); // Synchronize tile layer size to map size (assumes map contains all layers) if (tileLayer->rect() != mapRect) { tileLayer->resize(mapRect.size(), tileLayer->position()); tileLayer->setPosition(0, 0); } if (variation.map->orientation() == Map::Hexagonal) tileLayer->flipHexagonal(direction); else tileLayer->flip(direction); } if (variation.map->isStaggered()) { Map::StaggerAxis staggerAxis = variation.map->staggerAxis(); if (staggerAxis == Map::StaggerY) { if ((direction == FlipVertically && !(variation.map->height() & 1)) || direction == FlipHorizontally) variation.map->invertStaggerIndex(); } else { if ((direction == FlipHorizontally && !(variation.map->width() & 1)) || direction == FlipVertically) variation.map->invertStaggerIndex(); } } } return flipped; }
QVariant MapToVariantConverter::toVariant(const TileLayer &tileLayer, Map::LayerDataFormat format) const { QVariantMap tileLayerVariant; tileLayerVariant[QLatin1String("type")] = QLatin1String("tilelayer"); QRect bounds = tileLayer.bounds().translated(-tileLayer.position()); if (tileLayer.map()->infinite()) { tileLayerVariant[QLatin1String("width")] = bounds.width(); tileLayerVariant[QLatin1String("height")] = bounds.height(); tileLayerVariant[QLatin1String("startx")] = bounds.left(); tileLayerVariant[QLatin1String("starty")] = bounds.top(); } else { tileLayerVariant[QLatin1String("width")] = tileLayer.width(); tileLayerVariant[QLatin1String("height")] = tileLayer.height(); } addLayerAttributes(tileLayerVariant, tileLayer); switch (format) { case Map::XML: case Map::CSV: break; case Map::Base64: case Map::Base64Zlib: case Map::Base64Gzip: tileLayerVariant[QLatin1String("encoding")] = QLatin1String("base64"); if (format == Map::Base64Zlib) tileLayerVariant[QLatin1String("compression")] = QLatin1String("zlib"); else if (format == Map::Base64Gzip) tileLayerVariant[QLatin1String("compression")] = QLatin1String("gzip"); break; } if (tileLayer.map()->infinite()) { QVariantList chunkVariants; for (const QRect &rect : tileLayer.sortedChunksToWrite()) { QVariantMap chunkVariant; chunkVariant[QLatin1String("x")] = rect.x(); chunkVariant[QLatin1String("y")] = rect.y(); chunkVariant[QLatin1String("width")] = rect.width(); chunkVariant[QLatin1String("height")] = rect.height(); addTileLayerData(chunkVariant, tileLayer, format, rect); chunkVariants.append(chunkVariant); } tileLayerVariant[QLatin1String("chunks")] = chunkVariants; } else { addTileLayerData(tileLayerVariant, tileLayer, format, QRect(0, 0, tileLayer.width(), tileLayer.height())); } return tileLayerVariant; }
TileLayer *WangFiller::fillRegion(const TileLayer &back, const QRegion &fillRegion) const { Q_ASSERT(mWangSet); QRect boundingRect = fillRegion.boundingRect(); TileLayer *tileLayer = new TileLayer(QString(), boundingRect.x(), boundingRect.y(), boundingRect.width(), boundingRect.height()); QVector<WangId> wangIds(tileLayer->width() * tileLayer->height(), 0); for (const QRect &rect : fillRegion.rects()) { for (int x = rect.left(); x <= rect.right(); ++x) { int index = x - tileLayer->x() + (rect.top() - tileLayer->y()) * tileLayer->width(); wangIds[index] = wangIdFromSurroundings(back, fillRegion, QPoint(x, rect.top())); index = x - tileLayer->x() + (rect.bottom() - tileLayer->y())*tileLayer->width(); wangIds[index] = wangIdFromSurroundings(back, fillRegion, QPoint(x, rect.bottom())); } for (int y = rect.top() + 1; y < rect.bottom(); ++y) { int index = rect.left() - tileLayer->x() + (y - tileLayer->y())*tileLayer->width(); wangIds[index] = wangIdFromSurroundings(back, fillRegion, QPoint(rect.left(), y)); index = rect.right() - tileLayer->x() + (y - tileLayer->y())*tileLayer->width(); wangIds[index] = wangIdFromSurroundings(back, fillRegion, QPoint(rect.right(), y)); } } for (const QRect &rect : fillRegion.rects()) { for (int y = rect.top(); y <= rect.bottom(); ++y) { for (int x = rect.left(); x <= rect.right(); ++x) { QPoint currentPoint(x, y); int currentIndex = (currentPoint.y() - tileLayer->y()) * tileLayer->width() + (currentPoint.x() - tileLayer->x()); QList<WangTile> wangTilesList = mWangSet->findMatchingWangTiles(wangIds[currentIndex]); RandomPicker<WangTile> wangTiles; for (const WangTile &wangTile : wangTilesList) wangTiles.add(wangTile, mWangSet->wangTileProbability(wangTile)); while (!wangTiles.isEmpty()) { WangTile wangTile = wangTiles.take(); bool fill = true; if (!mWangSet->isComplete()) { QPoint adjacentPoints[8]; getSurroundingPoints(currentPoint, mStaggeredRenderer, mStaggerAxis, adjacentPoints); for (int i = 0; i < 8; ++i) { QPoint p = adjacentPoints[i]; if (!fillRegion.contains(p) || !tileLayer->cellAt(p - tileLayer->position()).isEmpty()) continue; p -= tileLayer->position(); int index = p.y() * tileLayer->width() + p.x(); WangId adjacentWangId = wangIds[index]; adjacentWangId.updateToAdjacent(wangTile.wangId(), (i + 4) % 8); if (!mWangSet->wildWangIdIsUsed(adjacentWangId)) { fill = wangTiles.isEmpty(); break; } } } if (fill) { tileLayer->setCell(currentPoint.x() - tileLayer->x(), currentPoint.y() - tileLayer->y(), wangTile.makeCell()); QPoint adjacentPoints[8]; getSurroundingPoints(currentPoint, mStaggeredRenderer, mStaggerAxis, adjacentPoints); for (int i = 0; i < 8; ++i) { QPoint p = adjacentPoints[i]; if (!fillRegion.contains(p) || !tileLayer->cellAt(p - tileLayer->position()).isEmpty()) continue; p -= tileLayer->position(); int index = p.y() * tileLayer->width() + p.x(); wangIds[index].updateToAdjacent(wangTile.wangId(), (i + 4) % 8); } break; } } } } } return tileLayer; }
void AbstractTileFillTool::randomFill(TileLayer &tileLayer, const QRegion ®ion) const { if (region.isEmpty() || mRandomCellPicker.isEmpty()) return; const auto localRegion = region.translated(-tileLayer.position()); #if QT_VERSION < 0x050800 const auto rects = localRegion.rects(); for (const QRect &rect : rects) { #else for (const QRect &rect : localRegion) { #endif for (int y = rect.top(); y <= rect.bottom(); ++y) { for (int x = rect.left(); x <= rect.right(); ++x) { tileLayer.setCell(x, y, mRandomCellPicker.pick()); } } } } void AbstractTileFillTool::wangFill(TileLayer &tileLayerToFill, const TileLayer &backgroundTileLayer, const QRegion ®ion) const { if (!mWangSet) return; WangFiller wangFiller(mWangSet, dynamic_cast<StaggeredRenderer *>(mapDocument()->renderer()), mapDocument()->map()->staggerAxis()); auto stamp = wangFiller.fillRegion(backgroundTileLayer, region); tileLayerToFill.setCells(0, 0, stamp.get()); } void AbstractTileFillTool::fillWithStamp(Map &map, const TileStamp &stamp, const QRegion &mask) { if (stamp.isEmpty()) return; const QSize size = stamp.maxSize(); if (size.isEmpty()) return; const QRect bounds = mask.boundingRect(); // Fill the entire map with random variations of the stamp for (int y = 0; y < bounds.height(); y += size.height()) { for (int x = 0; x < bounds.width(); x += size.width()) { const Map *stampMap = stamp.randomVariation().map; for (Layer *layer : stampMap->tileLayers()) { TileLayer *target = static_cast<TileLayer*>(map.findLayer(layer->name(), Layer::TileLayerType)); if (!target) { target = new TileLayer(layer->name(), bounds.topLeft(), bounds.size()); map.addLayer(target); } target->setCells(x, y, static_cast<TileLayer*>(layer)); } } } // Erase tiles outside of the masked region. This can easily be faster than // avoiding to place tiles outside of the region in the first place. for (Layer *layer : map.tileLayers()) { auto tileLayer = static_cast<TileLayer*>(layer); tileLayer->erase((QRegion(tileLayer->bounds()) - mask).translated(-tileLayer->position())); } } void AbstractTileFillTool::invalidateRandomAndMissingCache() { mRandomAndMissingCacheValid = false; }