Skip to content

Commit

Permalink
AutoMapping: Introduced AutoMappingContext
Browse files Browse the repository at this point in the history
This context stores temporary data specific to applying the active
AutoMappers to a target map. Previously this data was stored inside each
AutoMapper and hence the rule maps had to be reloaded to be applied to a
different map. Now this reloading is no longer necessary.

At the same time, the context stores any new tilesets, layers and
objects that may need to be added to the target map. They are no longer
added directly by the AutoMapper, which avoids the need to remove unused
tilesets and empty layers in a cleanup pass at the end.

This change also makes progress towards using the AutoMapper as part of
the preview or to be able to merge undo steps (see issue #2166), though
for now that is still not possible since tile layers in the target map
are still changed in-place.

Finally I figured out how to make QtConcurrent::blockingMapped compile
on Qt 5, making AutoMapping with Qt 5 almost twice as fast (matching the
Qt 6 speed). The functor was simplified by re-using the lambda
internally.
  • Loading branch information
bjorn committed Apr 5, 2022
1 parent 519cc60 commit d75e85e
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 346 deletions.
322 changes: 120 additions & 202 deletions src/tiled/automapper.cpp

Large diffs are not rendered by default.

147 changes: 50 additions & 97 deletions src/tiled/automapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#pragma once

#include "addremovemapobject.h"
#include "tilededitor_global.h"
#include "tilelayer.h"
#include "tileset.h"
Expand Down Expand Up @@ -107,10 +108,9 @@ struct RuleMapSetup
*/
std::vector<OutputSet> mOutputSets;

/**
* All input layer names.
*/
QSet<QString> mInputLayerNames;
QSet<QString> mOutputTileLayerNames;
QSet<QString> mOutputObjectGroupNames;
};

struct RuleInputLayer
Expand Down Expand Up @@ -148,6 +148,30 @@ struct Rule
struct CompileContext;
struct ApplyContext;

/**
* A single context is used for running all active AutoMapper instances on a
* specific target map.
*/
struct TILED_EDITOR_EXPORT AutoMappingContext
{
AutoMappingContext(MapDocument *mapDocument);

MapDocument *targetDocument;
Map *targetMap;

QVector<SharedTileset> newTilesets;
QVector<Layer*> newLayers;
QVector<AddMapObjects::Entry> newMapObjects;
QHash<Layer*, Properties> changedProperties;

QHash<QString, const TileLayer*> inputLayers;
QHash<QString, TileLayer*> outputTileLayers;
QHash<QString, ObjectGroup*> outputObjectGroups;

QList<const TileLayer*> touchedTileLayers; // only used when initially non-empty

const TileLayer dummy; // used in case input layers are missing
};

/**
* This class does all the work for the automapping feature.
Expand Down Expand Up @@ -220,8 +244,7 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
* AutoMapper takes ownership of this map.
* @param rulesMapFileName: The filepath to the rule map.
*/
AutoMapper(MapDocument *mapDocument,
std::unique_ptr<Map> rulesMap,
AutoMapper(std::unique_ptr<Map> rulesMap,
const QString &rulesMapFileName);
~AutoMapper() override;

Expand All @@ -233,33 +256,23 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
*/
bool ruleLayerNameUsed(const QString &ruleLayerName) const;

/**
* Returns a map of name to target layer, which could be touched
* considering the output layers of the rule map.
*/
const QHash<QString, TileLayer *> &outputTileLayers() const;

/**
* This needs to be called directly before the autoMap call.
* It sets up some data structures which change rapidly, so it is quite
* painful to keep these data structures up to date all time. (indices of
* layers of the working map)
*/
void prepareAutoMap();
void prepareAutoMap(AutoMappingContext &context);

/**
* Here is done all the automapping.
*
* When an \a appliedRegion is provided, it is set to the region where
* rule outputs have been applied.
*/
void autoMap(const QRegion &where, QList<const TileLayer *> *touchedTileLayers, QRegion *appliedRegion);

/**
* This cleans all data structures, which are setup via prepareAutoMap,
* so the auto mapper becomes ready for its next automatic mapping.
*/
void finalizeAutoMap();
void autoMap(const QRegion &where,
QRegion *appliedRegion,
AutoMappingContext &context);

/**
* Contains any errors which occurred while interpreting the rules map.
Expand All @@ -279,26 +292,25 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
bool setupRuleMapProperties();
void setupInputLayerProperties(InputLayer &inputLayer);

/**
* Searches the rules layer for regions and stores these in \a rules.
* @return returns true when anything is ok, false when errors occurred.
*/
bool setupRuleList();

/**
* Sets up the layers in the rules map, which are used for automapping.
* The layers are detected and put in the internal data structures.
* @return returns true when everything is ok, false when errors occurred.
*/
bool setupRuleMapLayers();

void setupWorkMapLayers();
void setupTilesets();
void compileRule(Rule &rule) const;
/**
* Searches the rules layer for regions and stores these in \a rules.
* @return returns true when anything is ok, false when errors occurred.
*/
bool setupRuleList();

void setupWorkMapLayers(AutoMappingContext &context) const;
void compileRule(Rule &rule, const AutoMappingContext &context) const;
bool compileInputSet(RuleInputSet &index,
const InputSet &inputSet,
const QRegion &inputRegion,
CompileContext &context) const;
const QRegion &inputRegion, CompileContext &compileContext,
const AutoMappingContext &context) const;

/**
* This copies all tiles from TileLayer \a srcLayer to TileLayer
Expand All @@ -311,7 +323,7 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
* so the maybe existing tile in dst will not be overwritten.
*/
void copyTileRegion(const TileLayer *srcLayer, QRect rect, TileLayer *dstLayer,
int dstX, int dstY);
int dstX, int dstY, const AutoMappingContext &context);

/**
* This copies all objects from the \a src_lr ObjectGroup to the \a dst_lr
Expand All @@ -321,7 +333,8 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
* destination object group.
*/
void copyObjectRegion(const ObjectGroup *srcLayer, const QRectF &rect,
ObjectGroup *dstLayer, int dstX, int dstY);
ObjectGroup *dstLayer, int dstX, int dstY,
AutoMappingContext &context);


/**
Expand All @@ -333,7 +346,7 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
*/
void copyMapRegion(const QRegion &region, QPoint Offset,
const OutputSet &ruleOutput,
ApplyContext &context);
AutoMappingContext &context);

/**
* This goes through all the positions in \a matchRegion and checks if the
Expand All @@ -344,69 +357,26 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
void matchRule(const Rule &rule,
const QRegion &matchRegion,
GetCell getCell,
const std::function<void (QPoint)> &matched) const;
const std::function<void (QPoint)> &matched,
const AutoMappingContext &context) const;

/**
* Applies the given \a rule at each of the given \a positions.
*
* Might skip some of the positions to satisfy the NoOverlappingRules
* option.
*/
void applyRule(const Rule &rule, QPoint pos, ApplyContext &context);

/**
* Cleans up the data structures filled by setupTilesets(),
* so the next rule can be processed.
*/
void cleanTilesets();

/**
* Cleans up the added tile layers setup by setupMissingLayers(),
* so we have a minimal addition of tile layers by the automapping.
*/
void cleanEmptyLayers();
void applyRule(const Rule &rule, QPoint pos, ApplyContext &applyContext,
AutoMappingContext &context);

void addWarning(const QString &text,
std::function<void()> callback = std::function<void()>());

/**
* where to work in
*/
MapDocument *mTargetDocument;

/**
* the same as mMapDocument->map()
*/
Map *mTargetMap;

/**
* map containing the rules, usually different than mTargetMap
*/
std::unique_ptr<Map> mRulesMap;

/**
* Contains the tilesets that have been added to mTargetMap.
*
* Any tilesets used by the rules map, which are not in the mTargetMap, are
* added before applying the rules. Afterwards, we remove the added
* tilesets that didn't end up getting used.
*
* @see setupTilesets()
* @see cleanTilesets()
*/
QVector<SharedTileset> mAddedTilesets;

/**
* Contains the layers that have been added to mTargetMap.
*
* Any output layers that were not found in mTargetMap are added. If they
* remain empty after applying the rules, they are removed again.
*
* @see setupMissingLayers()
* @see cleanEmptyLayers()
*/
QVector<Layer*> mAddedLayers;

RuleMapSetup mRuleMapSetup;

/**
Expand All @@ -422,18 +392,6 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject

Options mOptions;

/*
* These map layer names to the target layers in mTargetMap. To ensure no
* roaming pointers are used, this mapping is updated right before each
* automapping.
*
* @see setupWorkMapLayers()
*/
QHash<QString, const TileLayer*> mSetLayers;
QHash<QString, TileLayer*> mOutputTileLayers;
QHash<QString, ObjectGroup*> mOutputObjectGroups;

const TileLayer mDummy; // used in case input layers are missing
QString mError;
QString mWarning;
};
Expand All @@ -443,9 +401,4 @@ inline QString AutoMapper::rulesMapFileName() const
return mRulesMapFileName;
}

inline const QHash<QString, TileLayer*> &AutoMapper::outputTileLayers() const
{
return mOutputTileLayers;
}

} // namespace Tiled
Loading

0 comments on commit d75e85e

Please sign in to comment.