void ImageMovementTool::mouseMoved(const QPointF &pos, Qt::KeyboardModifiers modifiers) { AbstractImageTool::mouseMoved(pos, modifiers); if (mMousePressed) { if (ImageLayer *layer = currentImageLayer()) { QPointF diff = pos - mMouseStart; bool snapToGrid = Preferences::instance()->snapToGrid(); bool snapToFineGrid = Preferences::instance()->snapToFineGrid(); if (modifiers & Qt::ControlModifier) { snapToGrid = !snapToGrid; snapToFineGrid = false; } if (snapToGrid || snapToFineGrid) { MapRenderer *renderer = mapDocument()->renderer(); int scale = snapToFineGrid ? Preferences::instance()->gridFine() : 1; const QPointF alignScreenPos = renderer->pixelToScreenCoords(mLayerStart); const QPointF newAlignPixelPos = alignScreenPos + diff; // Snap the position to the grid QPointF newTileCoords = (renderer->screenToTileCoords(newAlignPixelPos) * scale).toPoint(); newTileCoords /= scale; diff = renderer->tileToScreenCoords(newTileCoords) - alignScreenPos; } layer->setPosition(mLayerStart + diff.toPoint()); mapDocument()->emitImageLayerChanged(layer); } } }
/** * Creates and removes handle instances as necessary to adapt to a new object * selection. */ void EditPolygonTool::updateHandles() { const QSet<MapObjectItem*> &selection = mapScene()->selectedObjectItems(); // First destroy the handles for objects that are no longer selected QMutableMapIterator<MapObjectItem*, QList<PointHandle*> > i(mHandles); while (i.hasNext()) { i.next(); if (!selection.contains(i.key())) { const auto &handles = i.value(); for (PointHandle *handle : handles) { if (handle->isSelected()) mSelectedHandles.remove(handle); delete handle; } i.remove(); } } MapRenderer *renderer = mapDocument()->renderer(); for (MapObjectItem *item : selection) { const MapObject *object = item->mapObject(); if (!object->cell().isEmpty()) continue; QPolygonF polygon = object->polygon(); polygon.translate(object->position()); QList<PointHandle*> pointHandles = mHandles.value(item); // Create missing handles while (pointHandles.size() < polygon.size()) { PointHandle *handle = new PointHandle(item, pointHandles.size()); pointHandles.append(handle); mapScene()->addItem(handle); } // Remove superfluous handles while (pointHandles.size() > polygon.size()) { PointHandle *handle = pointHandles.takeLast(); if (handle->isSelected()) mSelectedHandles.remove(handle); delete handle; } // Update the position of all handles for (int i = 0; i < pointHandles.size(); ++i) { const QPointF &point = polygon.at(i); const QPointF handlePos = renderer->pixelToScreenCoords(point); const QPointF internalHandlePos = handlePos - item->pos(); pointHandles.at(i)->setPos(item->mapToScene(internalHandlePos)); } mHandles.insert(item, pointHandles); } }
void EditPolygonTool::updateMovingItems(const QPointF &pos, Qt::KeyboardModifiers modifiers) { MapRenderer *renderer = mapDocument()->renderer(); QPointF diff = pos - mStart; SnapHelper snapHelper(renderer, modifiers); if (snapHelper.snaps()) { const QPointF alignScreenPos = renderer->pixelToScreenCoords(mAlignPosition); const QPointF newAlignScreenPos = alignScreenPos + diff; QPointF newAlignPixelPos = renderer->screenToPixelCoords(newAlignScreenPos); snapHelper.snap(newAlignPixelPos); diff = renderer->pixelToScreenCoords(newAlignPixelPos) - alignScreenPos; } const auto &selectedHandles = mSelectedHandles; int i = 0; for (PointHandle *handle : selectedHandles) { // update handle position const QPointF newScreenPos = mOldHandlePositions.at(i) + diff; handle->setPos(newScreenPos); // calculate new pixel position of polygon node const MapObjectItem *item = handle->mapObjectItem(); const QPointF newInternalPos = item->mapFromScene(newScreenPos); const QPointF newScenePos = item->pos() + newInternalPos; const QPointF newPixelPos = renderer->screenToPixelCoords(newScenePos); // update the polygon MapObject *mapObject = item->mapObject(); QPolygonF polygon = mapObject->polygon(); polygon[handle->pointIndex()] = newPixelPos - mapObject->position(); mapDocument()->mapObjectModel()->setObjectPolygon(mapObject, polygon); ++i; } }
void MapObjectItem::syncWithMapObject() { const QColor color = mObject->effectiveColor(); // Update the whole object when the name, polygon or color has changed if (mPolygon != mObject->polygon() || mColor != color) { mPolygon = mObject->polygon(); mColor = color; update(); } QString toolTip = mObject->name(); const QString &type = mObject->type(); if (!type.isEmpty()) toolTip += QLatin1String(" (") + type + QLatin1String(")"); setToolTip(toolTip); MapRenderer *renderer = mMapDocument->renderer(); const QPointF pixelPos = renderer->pixelToScreenCoords(mObject->position()); QRectF bounds = renderer->boundingRect(mObject); bounds.translate(-pixelPos); if (renderer->flags().testFlag(ShowTileCollisionShapes)) expandBoundsToCoverTileCollisionObjects(bounds); setPos(pixelPos); setRotation(mObject->rotation()); if (ObjectGroup *objectGroup = mObject->objectGroup()) { if (objectGroup->drawOrder() == ObjectGroup::TopDownOrder) setZValue(pixelPos.y()); if (mIsHoveredIndicator) { auto totalOffset = objectGroup->totalOffset(); setTransform(QTransform::fromTranslate(totalOffset.x(), totalOffset.y())); } } if (mBoundingRect != bounds) { // Notify the graphics scene about the geometry change in advance prepareGeometryChange(); mBoundingRect = bounds; } setVisible(mObject->isVisible()); }
void ExportAsImageDialog::accept() { const QString fileName = mUi->fileNameEdit->text(); if (fileName.isEmpty()) return; if (QFile::exists(fileName)) { const QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Export as Image"), tr("%1 already exists.\n" "Do you want to replace it?") .arg(QFileInfo(fileName).fileName()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (button != QMessageBox::Yes) return; } const bool visibleLayersOnly = mUi->visibleLayersOnly->isChecked(); const bool useCurrentScale = mUi->currentZoomLevel->isChecked(); const bool drawTileGrid = mUi->drawTileGrid->isChecked(); const bool includeBackgroundColor = mUi->includeBackgroundColor->isChecked(); MapRenderer *renderer = mMapDocument->renderer(); // Remember the current render flags const Tiled::RenderFlags renderFlags = renderer->flags(); renderer->setFlag(ShowTileObjectOutlines, false); QSize mapSize = renderer->mapSize(); QMargins margins = mMapDocument->map()->computeLayerOffsetMargins(); mapSize.setWidth(mapSize.width() + margins.left() + margins.right()); mapSize.setHeight(mapSize.height() + margins.top() + margins.bottom()); if (useCurrentScale) mapSize *= mCurrentScale; QImage image; try { image = QImage(mapSize, QImage::Format_ARGB32_Premultiplied); if (includeBackgroundColor) { if (mMapDocument->map()->backgroundColor().isValid()) image.fill(mMapDocument->map()->backgroundColor()); else image.fill(Qt::gray); } else { image.fill(Qt::transparent); } } catch (const std::bad_alloc &) { QMessageBox::critical(this, tr("Out of Memory"), tr("Could not allocate sufficient memory for the image. " "Try reducing the zoom level or using a 64-bit version of Tiled.")); return; } if (image.isNull()) { const size_t gigabyte = 1073741824; const size_t memory = size_t(mapSize.width()) * size_t(mapSize.height()) * 4; const double gigabytes = (double) memory / gigabyte; QMessageBox::critical(this, tr("Image too Big"), tr("The resulting image would be %1 x %2 pixels and take %3 GB of memory. " "Tiled is unable to create such an image. Try reducing the zoom level.") .arg(mapSize.width()) .arg(mapSize.height()) .arg(gigabytes, 0, 'f', 2)); return; } QPainter painter(&image); if (useCurrentScale) { if (smoothTransform(mCurrentScale)) painter.setRenderHints(QPainter::SmoothPixmapTransform); painter.setTransform(QTransform::fromScale(mCurrentScale, mCurrentScale)); renderer->setPainterScale(mCurrentScale); } else { renderer->setPainterScale(1); } painter.translate(margins.left(), margins.top()); foreach (const Layer *layer, mMapDocument->map()->layers()) { if (visibleLayersOnly && !layer->isVisible()) continue; painter.setOpacity(layer->opacity()); painter.translate(layer->offset()); const TileLayer *tileLayer = dynamic_cast<const TileLayer*>(layer); const ObjectGroup *objGroup = dynamic_cast<const ObjectGroup*>(layer); const ImageLayer *imageLayer = dynamic_cast<const ImageLayer*>(layer); if (tileLayer) { renderer->drawTileLayer(&painter, tileLayer); } else if (objGroup) { QList<MapObject*> objects = objGroup->objects(); if (objGroup->drawOrder() == ObjectGroup::TopDownOrder) qStableSort(objects.begin(), objects.end(), objectLessThan); foreach (const MapObject *object, objects) { if (object->isVisible()) { if (object->rotation() != qreal(0)) { QPointF origin = renderer->pixelToScreenCoords(object->position()); painter.save(); painter.translate(origin); painter.rotate(object->rotation()); painter.translate(-origin); } const QColor color = MapObjectItem::objectColor(object); renderer->drawMapObject(&painter, object, color); if (object->rotation() != qreal(0)) painter.restore(); } } } else if (imageLayer) { renderer->drawImageLayer(&painter, imageLayer); } painter.translate(-layer->offset()); }