Skip to content

Commit

Permalink
AutoMapping: Load rule maps on-demand
Browse files Browse the repository at this point in the history
This optimizes AutoMapping operations by not loading rule maps which do
not apply to the current map based on file name filters.

Also, re-parsing of the rules.txt file(s) now only happens when any of
such files were changed, not when rule maps have changed.
bjorn committed Jan 30, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 4cf1972 commit 339b504
Showing 5 changed files with 67 additions and 57 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

* Added support for SVG 1.2 / CSS blending modes to layers (#3932)
* AutoMapping: Don't match rules based on empty input indexes
* AutoMapping: Optimized reloading of rule maps and load rule maps on-demand
* Raised minimum supported Qt version from 5.12 to 5.15

### Tiled 1.11.2 (28 Jan 2025)
3 changes: 1 addition & 2 deletions src/tiled/automapper.cpp
Original file line number Diff line number Diff line change
@@ -143,10 +143,9 @@ AutoMappingContext::AutoMappingContext(MapDocument *mapDocument)
}


AutoMapper::AutoMapper(std::unique_ptr<Map> rulesMap, const QRegularExpression &mapNameFilter)
AutoMapper::AutoMapper(std::unique_ptr<Map> rulesMap)
: mRulesMap(std::move(rulesMap))
, mRulesMapRenderer(MapRenderer::create(mRulesMap.get()))
, mMapNameFilter(mapNameFilter)
{
setupRuleMapProperties();

10 changes: 1 addition & 9 deletions src/tiled/automapper.h
Original file line number Diff line number Diff line change
@@ -31,7 +31,6 @@
#include <QList>
#include <QMap>
#include <QRegion>
#include <QRegularExpression>
#include <QSet>
#include <QString>
#include <QVector>
@@ -333,11 +332,10 @@ class TILED_EDITOR_EXPORT AutoMapper
* @param rulesMap The map containing the AutoMapping rules. The
* AutoMapper takes ownership of this map.
*/
AutoMapper(std::unique_ptr<Map> rulesMap, const QRegularExpression &mapNameFilter = {});
explicit AutoMapper(std::unique_ptr<Map> rulesMap);
~AutoMapper();

QString rulesMapFileName() const;
const QRegularExpression &mapNameFilter() const;

/**
* Checks if the passed \a ruleLayerName is used as input layer in this
@@ -467,7 +465,6 @@ class TILED_EDITOR_EXPORT AutoMapper
*/
const std::unique_ptr<Map> mRulesMap;
const std::unique_ptr<MapRenderer> mRulesMapRenderer;
const QRegularExpression mMapNameFilter;

RuleMapSetup mRuleMapSetup;

@@ -490,9 +487,4 @@ class TILED_EDITOR_EXPORT AutoMapper
const TileLayer dummy; // used in case input layers are missing
};

inline const QRegularExpression &AutoMapper::mapNameFilter() const
{
return mMapNameFilter;
}

} // namespace Tiled
94 changes: 52 additions & 42 deletions src/tiled/automappingmanager.cpp
Original file line number Diff line number Diff line change
@@ -109,21 +109,6 @@ void AutomappingManager::autoMapInternal(const QRegion &where,

const bool automatic = touchedLayer != nullptr;

if (!mLoaded) {
if (mRulesFile.isEmpty()) {
mError = tr("No AutoMapping rules provided. Save the map or refer to a rule file in the project properties.");
emit errorsOccurred(automatic);
return;
}

if (loadFile(mRulesFile)) {
mLoaded = true;
} else {
emit errorsOccurred(automatic);
return;
}
}

// Even if no AutoMapper instance will be executed, we still want to report
// any warnings or errors that might have been reported while interpreting
// the rule maps.
@@ -135,14 +120,29 @@ void AutomappingManager::autoMapInternal(const QRegion &where,
emit errorsOccurred(automatic);
});

if (!mLoaded) {
if (mRulesFile.isEmpty()) {
mError = tr("No AutoMapping rules provided. Save the map or refer to a rule file in the project properties.");
return;
}

if (!loadFile(mRulesFile))
return;

mLoaded = true;
}

// Determine the list of AutoMappers that is relevant for this map
const QString mapFileName = QFileInfo(mMapDocument->fileName()).fileName();

QVector<const AutoMapper*> autoMappers;
autoMappers.reserve(mActiveAutoMappers.size());
for (auto autoMapper : mActiveAutoMappers) {
const auto &mapNameFilter = autoMapper->mapNameFilter();
autoMappers.reserve(mRuleMapReferences.size());

for (auto &ruleMap : std::as_const(mRuleMapReferences)) {
const auto &mapNameFilter = ruleMap.mapNameFilter;
if (!mapNameFilter.isValid() || mapNameFilter.match(mapFileName).hasMatch())
autoMappers.append(autoMapper);
if (const AutoMapper *autoMapper = findAutoMapper(ruleMap.filePath))
autoMappers.append(autoMapper);
}

if (autoMappers.isEmpty())
@@ -164,6 +164,24 @@ void AutomappingManager::autoMapInternal(const QRegion &where,
mMapDocument->undoStack()->push(aw);
}

/**
* Returns the AutoMapper instance for the given rules file, loading it if
* necessary. Returns nullptr if the file could not be loaded.
*/
const AutoMapper *AutomappingManager::findAutoMapper(const QString &filePath)
{
auto it = mLoadedAutoMappers.find(filePath);
if (it != mLoadedAutoMappers.end())
return it->second.get();

auto autoMapper = loadRuleMap(filePath);
if (!autoMapper)
return nullptr;

auto result = mLoadedAutoMappers.emplace(filePath, std::move(autoMapper));
return result.first->second.get();
}

/**
* This function parses a rules file or loads a rules map file.
*
@@ -183,7 +201,8 @@ bool AutomappingManager::loadFile(const QString &filePath)
return loadRulesFile(filePath);
}

return loadRuleMap(filePath);
mRuleMapReferences.append(RuleMapReference { filePath, mMapNameFilter });
return true;
}

bool AutomappingManager::loadRulesFile(const QString &filePath)
@@ -251,41 +270,30 @@ bool AutomappingManager::loadRulesFile(const QString &filePath)
return ret;
}

bool AutomappingManager::loadRuleMap(const QString &filePath)
std::unique_ptr<AutoMapper> AutomappingManager::loadRuleMap(const QString &filePath)
{
auto it = mLoadedAutoMappers.find(filePath);
if (it != mLoadedAutoMappers.end()) {
mActiveAutoMappers.push_back(it->second.get());
return true;
}

QString errorString;
std::unique_ptr<Map> rules { readMap(filePath, &errorString) };

if (!rules) {
auto rulesMap = readMap(filePath, &errorString);
if (!rulesMap) {
QString error = tr("Opening rules map '%1' failed: %2")
.arg(filePath, errorString);
ERROR(error);

mError += error;
mError += QLatin1Char('\n');
return false;
return {};
}

std::unique_ptr<AutoMapper> autoMapper { new AutoMapper(std::move(rules), mMapNameFilter) };
mWatcher.addPath(filePath);

auto autoMapper = std::make_unique<AutoMapper>(std::move(rulesMap));

mWarning += autoMapper->warningString();
const QString error = autoMapper->errorString();
if (error.isEmpty()) {
auto autoMapperPtr = autoMapper.get();
mLoadedAutoMappers.insert(std::make_pair(filePath, std::move(autoMapper)));
mActiveAutoMappers.push_back(autoMapperPtr);
mWatcher.addPath(filePath);
} else {
if (!error.isEmpty())
mError += error;
}

return true;
return autoMapper;
}

/**
@@ -346,7 +354,7 @@ void AutomappingManager::refreshRulesFile(const QString &ruleFileOverride)

void AutomappingManager::cleanUp()
{
mActiveAutoMappers.clear();
mRuleMapReferences.clear();
mLoaded = false;
}

@@ -358,7 +366,9 @@ void AutomappingManager::onFileChanged(const QString &path)
// File will be re-added when it is still relevant
mWatcher.removePath(path);

cleanUp();
// Re-parse the rules file(s) when any of them changed
if (path.endsWith(QLatin1String(".txt"), Qt::CaseInsensitive))
cleanUp();
}

#include "moc_automappingmanager.cpp"
16 changes: 12 additions & 4 deletions src/tiled/automappingmanager.h
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@
#include <QString>

#include <memory>
#include <vector>
#include <unordered_map>

namespace Tiled {

@@ -39,6 +39,12 @@ class TileLayer;
class AutoMapper;
class MapDocument;

struct RuleMapReference
{
QString filePath;
QRegularExpression mapNameFilter;
};

/**
* This class is a superior class to the AutoMapper and AutoMapperWrapper class.
* It uses these classes to do the whole automapping process.
@@ -84,9 +90,11 @@ class AutomappingManager : public QObject
void onMapFileNameChanged();
void onFileChanged(const QString &path);

const AutoMapper *findAutoMapper(const QString &filePath);

bool loadFile(const QString &filePath);
bool loadRulesFile(const QString &filePath);
bool loadRuleMap(const QString &filePath);
std::unique_ptr<AutoMapper> loadRuleMap(const QString &filePath);

/**
* Applies automapping to the region \a where.
@@ -113,13 +121,13 @@ class AutomappingManager : public QObject
std::unordered_map<QString, std::unique_ptr<AutoMapper>> mLoadedAutoMappers;

/**
* The active list of AutoMapper instances, in the order they were
* The active list of rule map references, in the order they were
* encountered in the rules file.
*
* Some loaded rule maps might not be active, and some might be active
* multiple times.
*/
std::vector<const AutoMapper*> mActiveAutoMappers;
QVector<RuleMapReference> mRuleMapReferences;

/**
* This tells you if the rules for the current map document were already

0 comments on commit 339b504

Please sign in to comment.