Skip to content
Permalink
Browse files

Scripting: Added TileMap.autoMap

This function enables manually triggering the AutoMap action with a
specific region and/or rules file.

Also made the rules file automatically reload when it changes due to
saving the map to a different location.
  • Loading branch information...
bjorn committed Oct 8, 2019
1 parent 551214f commit e146b0d6c653d00fbba4574e9cc858811d1ea959
Showing with 121 additions and 47 deletions.
  1. +14 −0 docs/reference/scripting.rst
  2. +45 −32 src/tiled/automappingmanager.cpp
  3. +17 −15 src/tiled/automappingmanager.h
  4. +22 −0 src/tiled/editablemap.cpp
  5. +23 −0 src/tiled/editablemap.h
@@ -642,6 +642,20 @@ Functions
new TileMap()
Constructs a new map.

.. _script-map-autoMap:

TileMap.autoMap([rulesFile : string]) : void
Applies :doc:`/manual/automapping` using the given rules file, or using the
default rules file is none is given.

*This operation can only be applied to maps loaded from a file.*

TileMap.autoMap(region : :ref:`script-region` | :ref:`script-rect` [, rulesFile : string]) : void
Applies :doc:`/manual/automapping` in the given region using the given
rules file, or using the default rules file is none is given.

*This operation can only be applied to maps loaded from a file.*

.. _script-map-setSize:

TileMap.setSize(width : int, height : int) : void
@@ -34,8 +34,6 @@

#include "qtcompat_p.h"

#include <memory>

using namespace Tiled;

AutomappingManager::AutomappingManager(QObject *parent)
@@ -49,18 +47,18 @@ AutomappingManager::AutomappingManager(QObject *parent)

AutomappingManager::~AutomappingManager()
{
cleanUp();
}

void AutomappingManager::autoMap()
{
if (!mMapDocument)
return;

Map *map = mMapDocument->map();
QRegion region = mMapDocument->selectedArea();

if (region.isEmpty()) {
Map *map = mMapDocument->map();

if (map->infinite()) {
LayerIterator iterator(map);

@@ -80,12 +78,23 @@ void AutomappingManager::autoMap()
autoMapInternal(region, nullptr);
}

void AutomappingManager::autoMapRegion(const QRegion &region)
{
autoMapInternal(region, nullptr);
}

void AutomappingManager::onRegionEdited(const QRegion &where, Layer *touchedLayer)
{
if (Preferences::instance()->automappingDrawing())
autoMapInternal(where, touchedLayer);
}

void AutomappingManager::onMapFileNameChanged()
{
if (!mRulesFileOverride)
refreshRulesFile();
}

void AutomappingManager::autoMapInternal(const QRegion &where,
Layer *touchedLayer)
{
@@ -97,8 +106,7 @@ void AutomappingManager::autoMapInternal(const QRegion &where,
const bool automatic = touchedLayer != nullptr;

if (!mLoaded) {
const QString rulesFile = rulesFileName();
if (loadFile(rulesFile)) {
if (loadFile(mRulesFile)) {
mLoaded = true;
} else {
emit errorsOccurred(automatic);
@@ -107,26 +115,23 @@ void AutomappingManager::autoMapInternal(const QRegion &where,
}

QVector<AutoMapper*> passedAutoMappers;
if (touchedLayer) {
for (AutoMapper *a : qAsConst(mAutoMappers)) {
if (a->ruleLayerNameUsed(touchedLayer->name()))
passedAutoMappers.append(a);
}
} else {
passedAutoMappers = mAutoMappers;
for (auto &a : qAsConst(mAutoMappers)) {
if (!touchedLayer || a->ruleLayerNameUsed(touchedLayer->name()))
passedAutoMappers.append(a.get());
}

if (!passedAutoMappers.isEmpty()) {
// use a copy of the region, so each automapper can manipulate it and the
// following automappers do see the impact
QRegion region(where);

QUndoStack *undoStack = mMapDocument->undoStack();
undoStack->beginMacro(tr("Apply AutoMap rules"));
AutoMapperWrapper *aw = new AutoMapperWrapper(mMapDocument, passedAutoMappers, &region);
AutoMapperWrapper *aw = new AutoMapperWrapper(mMapDocument, std::move(passedAutoMappers), &region);
undoStack->push(aw);
undoStack->endMacro();
}
for (AutoMapper *automapper : qAsConst(mAutoMappers)) {
for (auto &automapper : qAsConst(mAutoMappers)) {
mWarning += automapper->warningString();
mError += automapper->errorString();
}
@@ -210,7 +215,7 @@ bool AutomappingManager::loadFile(const QString &filePath)
mWarning += autoMapper->warningString();
const QString error = autoMapper->errorString();
if (error.isEmpty()) {
mAutoMappers.append(autoMapper.release());
mAutoMappers.push_back(std::move(autoMapper));
mWatcher.addPath(rulePath);
} else {
mError += error;
@@ -224,30 +229,45 @@ bool AutomappingManager::loadFile(const QString &filePath)
return ret;
}

void AutomappingManager::setMapDocument(MapDocument *mapDocument)
/**
* The rules file is determind based on the map location, but can be overridden
* by passing \a rulesFile.
*/
void AutomappingManager::setMapDocument(MapDocument *mapDocument, const QString &rulesFile)
{
QString oldRules;
if (mMapDocument) {
oldRules = rulesFileName();
if (mMapDocument)
mMapDocument->disconnect(this);
}

mMapDocument = mapDocument;

QString newRules;
if (mMapDocument) {
connect(mMapDocument, &MapDocument::fileNameChanged,
this, &AutomappingManager::onMapFileNameChanged);
connect(mMapDocument, &MapDocument::regionEdited,
this, &AutomappingManager::onRegionEdited);
newRules = rulesFileName();
}

if (newRules != oldRules)
refreshRulesFile(rulesFile);
}

void AutomappingManager::refreshRulesFile(const QString &ruleFileOverride)
{
mRulesFileOverride = !ruleFileOverride.isEmpty();
QString rulesFile = ruleFileOverride;

if (rulesFile.isEmpty() && mMapDocument) {
const QString mapPath = QFileInfo(mMapDocument->fileName()).path();
rulesFile = mapPath + QLatin1String("/rules.txt");
}

if (mRulesFile != rulesFile) {
mRulesFile = rulesFile;
cleanUp();
}
}

void AutomappingManager::cleanUp()
{
qDeleteAll(mAutoMappers);
mAutoMappers.clear();
mLoaded = false;
if (!mWatcher.files().isEmpty())
@@ -258,10 +278,3 @@ void AutomappingManager::onFileChanged()
{
cleanUp();
}

QString AutomappingManager::rulesFileName() const
{
const QString mapPath = QFileInfo(mMapDocument->fileName()).path();
const QString rulesFileName = mapPath + QLatin1String("/rules.txt");
return rulesFileName;
}
@@ -23,9 +23,11 @@
#include <QObject>
#include <QRegion>
#include <QString>
#include <QVector>
#include <QFileSystemWatcher>

#include <memory>
#include <vector>

namespace Tiled {

class Layer;
@@ -43,19 +45,22 @@ class AutomappingManager : public QObject
Q_DISABLE_COPY(AutomappingManager)

public:
/**
* Constructor.
*/
AutomappingManager(QObject *parent = nullptr);

~AutomappingManager();

void setMapDocument(MapDocument *mapDocument);
void setMapDocument(MapDocument *mapDocument, const QString &rulesFile = QString());

QString errorString() const { return mError; }

QString warningString() const { return mWarning; }

/**
* This triggers an automapping on the current map document. Starts with
* the currently selected area, or the entire map if there is no selection.
*/
void autoMap();
void autoMapRegion(const QRegion &region);

signals:
/**
* This signal is emitted after automapping was done and an error occurred.
@@ -67,17 +72,13 @@ class AutomappingManager : public QObject
*/
void warningsOccurred(bool automatic);

public slots:
/**
* This triggers an automapping on the current map document. Starts with
* the currently selected area, or the entire map if there is no selection.
*/
void autoMap();

private:
void onRegionEdited(const QRegion &where, Layer *touchedLayer);
void onMapFileNameChanged();
void onFileChanged();

void refreshRulesFile(const QString &ruleFileOverride = QString());

/**
* This function parses a rules file.
* For each path which is a rule, (file extension is tmx) an AutoMapper
@@ -113,7 +114,7 @@ public slots:
* For each new file of rules a new AutoMapper is setup. In this vector we
* can store all of the AutoMappers in order.
*/
QVector<AutoMapper*> mAutoMappers;
std::vector<std::unique_ptr<AutoMapper>> mAutoMappers;

/**
* This tells you if the rules for the current map document were already
@@ -135,7 +136,8 @@ public slots:

QFileSystemWatcher mWatcher;

QString rulesFileName() const;
QString mRulesFile;
bool mRulesFileOverride = false;
};

} // namespace Tiled
@@ -22,6 +22,7 @@

#include "addremovelayer.h"
#include "addremovetileset.h"
#include "automappingmanager.h"
#include "changeevents.h"
#include "changemapproperty.h"
#include "changeselectedarea.h"
@@ -345,6 +346,27 @@ void EditableMap::resize(QSize size, QPoint offset, bool removeObjects)
mapDocument()->resizeMap(size, offset, removeObjects);
}

void EditableMap::autoMap(const RegionValueType &region, const QString &rulesFile)
{
if (checkReadOnly())
return;
if (!mapDocument()) {
ScriptManager::instance().throwError(QLatin1String("AutoMapping is currently not supported for detached maps"));
return;
}

if (!mAutomappingManager)
mAutomappingManager = new AutomappingManager(this);

AutomappingManager &manager = *mAutomappingManager;
manager.setMapDocument(mapDocument(), rulesFile);

if (region.region().isEmpty())
manager.autoMap();
else
manager.autoMapRegion(region.region());
}

void EditableMap::setSize(int width, int height)
{
if (auto doc = mapDocument()) {
@@ -22,11 +22,13 @@

#include "editableasset.h"
#include "mapdocument.h"
#include "regionvaluetype.h"

namespace Tiled {

class MapObject;

class AutomappingManager;
class EditableLayer;
class EditableMapObject;
class EditableSelectedArea;
@@ -147,6 +149,11 @@ class EditableMap : public EditableAsset
QPoint offset = QPoint(),
bool removeObjects = false);

Q_INVOKABLE void autoMap(const QString &rulesFile = QString());
Q_INVOKABLE void autoMap(const QRect &region, const QString &rulesFile = QString());
Q_INVOKABLE void autoMap(const QRectF &region, const QString &rulesFile = QString());
Q_INVOKABLE void autoMap(const Tiled::RegionValueType &region, const QString &rulesFile = QString());

void setWidth(int width);
void setHeight(int height);
Q_INVOKABLE void setSize(int width, int height);
@@ -193,6 +200,7 @@ class EditableMap : public EditableAsset
bool mReadOnly;

EditableSelectedArea *mSelectedArea;
AutomappingManager *mAutomappingManager;
};


@@ -276,6 +284,21 @@ inline EditableSelectedArea *EditableMap::selectedArea()
return mSelectedArea;
}

inline void EditableMap::autoMap(const QString &rulesFile)
{
autoMap(RegionValueType(), rulesFile);
}

inline void EditableMap::autoMap(const QRect &region, const QString &rulesFile)
{
autoMap(RegionValueType(region), rulesFile);
}

inline void EditableMap::autoMap(const QRectF &region, const QString &rulesFile)
{
autoMap(region.toRect(), rulesFile);
}

inline void EditableMap::setWidth(int width)
{
setSize(width, height());

0 comments on commit e146b0d

Please sign in to comment.
You can’t perform that action at this time.