Start of undo/redo system (added DefinitionDiff)

This commit is contained in:
Michaël Lemaire 2015-08-16 23:01:56 +02:00
parent d78bd2553c
commit 1eef1ef429
21 changed files with 257 additions and 21 deletions

View file

@ -4,7 +4,7 @@
#include "RandomGenerator.h"
AtmosphereDefinition::AtmosphereDefinition(DefinitionNode* parent):
DefinitionNode(parent, "atmosphere")
DefinitionNode(parent, "atmosphere", "atmosphere")
{
}

View file

@ -5,7 +5,7 @@
#include "BoundingBox.h"
CameraDefinition::CameraDefinition(DefinitionNode *parent):
DefinitionNode(parent, "camera")
DefinitionNode(parent, "camera", "camera")
{
location.x = 0.0;
location.y = 0.0;

View file

@ -6,7 +6,7 @@
#include "PackStream.h"
CloudLayerDefinition::CloudLayerDefinition(DefinitionNode* parent):
DefinitionNode(parent, "layer")
DefinitionNode(parent, "layer", "cloudlayer")
{
type = CIRRUS;
altitude = 0.5;

View file

@ -0,0 +1,8 @@
#include "DefinitionDiff.h"
#include "DefinitionNode.h"
DefinitionDiff::DefinitionDiff(const std::string &type_name):
type_name(type_name)
{
}

View file

@ -0,0 +1,28 @@
#ifndef DEFINITIONDIFF_H
#define DEFINITIONDIFF_H
#include "definition_global.h"
namespace paysages {
namespace definition {
/**
* Base class for diffs produced by the definition tree.
*
* Diffs are used to undo/redo changes.
*/
class DEFINITIONSHARED_EXPORT DefinitionDiff
{
public:
DefinitionDiff(const std::string &type_name);
inline const std::string &getTypeName() const {return type_name;}
private:
std::string type_name;
};
}
}
#endif // DEFINITIONDIFF_H

View file

@ -2,9 +2,10 @@
#include "Logs.h"
#include "PackStream.h"
#include "DefinitionDiff.h"
DefinitionNode::DefinitionNode(DefinitionNode* parent, const std::string &name):
parent(parent), name(name)
DefinitionNode::DefinitionNode(DefinitionNode* parent, const std::string &name, const std::string &type_name):
parent(parent), type_name(type_name), name(name)
{
if (parent)
{
@ -71,6 +72,20 @@ std::string DefinitionNode::toString(int indent) const
return result;
}
bool DefinitionNode::applyDiff(const DefinitionDiff *diff, bool)
{
// Only do type check, subclasses will do the rest
if (diff->getTypeName() == type_name)
{
return true;
}
else
{
Logs::error() << "Can't apply " << diff->getTypeName() << " diff to " << getName() << " " << type_name << " node" << std::endl;
return false;
}
}
void DefinitionNode::save(PackStream* stream) const
{
int children_count = (int)children.size();
@ -128,8 +143,15 @@ void DefinitionNode::load(PackStream* stream)
void DefinitionNode::copy(DefinitionNode* destination) const
{
if (destination->getTypeName() == getTypeName())
{
destination->setName(name);
// can't copy children as we don't know their types...
// TODO Copy children ?
}
else
{
Logs::error() << "Can't copy from " << getTypeName() << " to " << destination->getTypeName() << std::endl;
}
}
void DefinitionNode::validate()

View file

@ -12,7 +12,7 @@ namespace definition {
class DEFINITIONSHARED_EXPORT DefinitionNode
{
public:
DefinitionNode(DefinitionNode* parent, const std::string &name);
DefinitionNode(DefinitionNode* parent, const std::string &name, const std::string &type_name = "");
virtual ~DefinitionNode();
virtual void save(PackStream* stream) const;
@ -24,6 +24,8 @@ public:
inline const std::string &getName() const {return name;}
virtual void setName(const std::string &name);
inline const std::string &getTypeName() const {return type_name;}
virtual Scenery* getScenery();
inline const DefinitionNode* getParent() const {return parent;}
@ -35,6 +37,17 @@ public:
*/
virtual std::string toString(int indent = 0) const;
/**
* Apply a diff to the internal value of this node.
*
* All internal node modifications should be done using this method, to be reversible.
*
* If *backward* is true, the diff will be reversed, instead of applied.
*
* Return true if the diff could be applied.
*/
virtual bool applyDiff(const DefinitionDiff *diff, bool backward=false);
protected:
void addChild(DefinitionNode* child);
void removeChild(DefinitionNode* child);
@ -51,6 +64,7 @@ protected:
private:
DefinitionNode* parent;
DefinitionNode* root;
std::string type_name;
std::string name;
std::vector<DefinitionNode*> children;
};

View file

@ -0,0 +1,6 @@
#include "FloatDiff.h"
FloatDiff::FloatDiff(double oldvalue, double newvalue):
DefinitionDiff("float"), oldvalue(oldvalue), newvalue(newvalue)
{
}

View file

@ -0,0 +1,30 @@
#ifndef FLOATDIFF_H
#define FLOATDIFF_H
#include "definition_global.h"
#include "DefinitionDiff.h"
namespace paysages {
namespace definition {
/**
* Diff for a FloatNode.
*/
class DEFINITIONSHARED_EXPORT FloatDiff: public DefinitionDiff
{
public:
FloatDiff(double oldvalue, double newvalue);
inline double getOldValue() const {return oldvalue;}
inline double getNewValue() const {return newvalue;}
private:
double oldvalue;
double newvalue;
};
}
}
#endif // FLOATDIFF_H

View file

@ -1,10 +1,13 @@
#include "FloatNode.h"
#include "PackStream.h"
#include "FloatDiff.h"
#include "Logs.h"
#include <sstream>
#include <cassert>
FloatNode::FloatNode(DefinitionNode* parent, const std::string &name, double value):
DefinitionNode(parent, name), value(value)
DefinitionNode(parent, name, "float"), value(value)
{
}
@ -29,7 +32,42 @@ void FloatNode::load(PackStream *stream)
void FloatNode::copy(DefinitionNode *destination) const
{
// TODO type check
if (destination->getTypeName() == getTypeName())
{
((FloatNode *)destination)->value = value;
}
else
{
Logs::error() << "Can't copy from " << getTypeName() << " to " << destination->getTypeName() << std::endl;
}
}
const FloatDiff *FloatNode::produceDiff(double new_value) const
{
return new FloatDiff(value, new_value);
}
bool FloatNode::applyDiff(const DefinitionDiff *diff, bool backward)
{
if (!DefinitionNode::applyDiff(diff, backward))
{
return false;
}
assert(diff->getTypeName() == "float");
const FloatDiff *float_diff = (const FloatDiff *)diff;
double previous = backward ? float_diff->getNewValue() : float_diff->getOldValue();
double next = backward ? float_diff->getOldValue() : float_diff->getNewValue();
if (value == previous)
{
value = next;
return true;
}
else
{
Logs::error() << "Can't apply float diff " << previous << " => " << next << " to " << getName() << std::endl;
return false;
}
}

View file

@ -22,6 +22,9 @@ public:
virtual void save(PackStream* stream) const override;
virtual void load(PackStream* stream) override;
virtual void copy(DefinitionNode* destination) const override;
const FloatDiff *produceDiff(double new_value) const;
virtual bool applyDiff(const DefinitionDiff *diff, bool backward=false) override;
private:
double value;
};

View file

@ -4,7 +4,7 @@
#include "Logs.h"
Layers::Layers(DefinitionNode* parent, const std::string &name, LayerConstructor layer_constructor):
DefinitionNode(parent, name), layer_constructor(layer_constructor)
DefinitionNode(parent, name, "layers" + name), layer_constructor(layer_constructor)
{
max_layer_count = 100;
null_layer = layer_constructor(this);

View file

@ -7,7 +7,7 @@
#include "PaintedGridBrush.h"
PaintedGrid::PaintedGrid(DefinitionNode *parent):
DefinitionNode(parent, "grid")
DefinitionNode(parent, "grid", "grid")
{
merged_data = new PaintedGridData;
brush_data = new PaintedGridData;

View file

@ -15,7 +15,7 @@ static const double APP_HEADER = 19866544632.125;
static const int DATA_VERSION = 1;
Scenery::Scenery():
DefinitionNode(NULL, "scenery")
DefinitionNode(NULL, "scenery", "scenery")
{
addChild(atmosphere = new AtmosphereDefinition(this));
addChild(camera = new CameraDefinition);

View file

@ -6,7 +6,7 @@
#include "FloatNode.h"
TerrainDefinition::TerrainDefinition(DefinitionNode* parent):
DefinitionNode(parent, "terrain")
DefinitionNode(parent, "terrain", "terrain")
{
height = 1.0;
scaling = 1.0;

View file

@ -8,7 +8,7 @@
#include "TerrainDefinition.h"
TextureLayerDefinition::TextureLayerDefinition(DefinitionNode* parent):
DefinitionNode(parent, "texture")
DefinitionNode(parent, "texture", "texturelayer")
{
terrain_zone = new Zone;
_displacement_noise = new NoiseGenerator;

View file

@ -6,7 +6,7 @@
#include "SurfaceMaterial.h"
WaterDefinition::WaterDefinition(DefinitionNode* parent):
DefinitionNode(parent, "water")
DefinitionNode(parent, "water", "water")
{
material = new SurfaceMaterial;
depth_color = new Color;

View file

@ -6,7 +6,7 @@
#include "Vector3.h"
Zone::Zone(DefinitionNode *parent):
DefinitionNode(parent, "zone")
DefinitionNode(parent, "zone", "zone")
{
value_by_height = new Curve;
absolute_height = 1;

View file

@ -31,7 +31,9 @@ SOURCES += \
PaintedGridBrush.cpp \
PaintedGridData.cpp \
DefinitionNode.cpp \
FloatNode.cpp
FloatNode.cpp \
DefinitionDiff.cpp \
FloatDiff.cpp
HEADERS +=\
definition_global.h \
@ -52,7 +54,9 @@ HEADERS +=\
PaintedGridBrush.h \
PaintedGridData.h \
DefinitionNode.h \
FloatNode.h
FloatNode.h \
DefinitionDiff.h \
FloatDiff.h
unix:!symbian {
maemo5 {

View file

@ -14,7 +14,9 @@
namespace paysages {
namespace definition {
class DefinitionNode;
class DefinitionDiff;
class FloatNode;
class FloatDiff;
class Scenery;
class CameraDefinition;
class SurfaceMaterial;

View file

@ -1,6 +1,7 @@
#include "BaseTestCase.h"
#include "FloatNode.h"
#include "FloatDiff.h"
#include "PackStream.h"
TEST(FloatNode, toString)
@ -28,3 +29,83 @@ TEST(FloatNode, saveLoadAndSkip)
EXPECT_DOUBLE_EQ(4.3, testb2.getValue());
}
TEST(FloatNode, copy)
{
FloatNode base(NULL, "test", 2.1);
FloatNode other(NULL, "test", 4.3);
DefinitionNode badother(NULL, "test");
base.copy(&other);
EXPECT_DOUBLE_EQ(2.1, base.getValue());
EXPECT_DOUBLE_EQ(2.1, other.getValue());
badother.copy(&base);
EXPECT_DOUBLE_EQ(2.1, base.getValue());
base.copy(&badother);
// can't check anything, just useful in valgrind
}
TEST(FloatNode, produceDiff)
{
FloatNode node(NULL, "test", 8.3);
const FloatDiff *diff = node.produceDiff(-4.1);
EXPECT_EQ("float", diff->getTypeName());
EXPECT_DOUBLE_EQ(8.3, diff->getOldValue());
EXPECT_DOUBLE_EQ(-4.1, diff->getNewValue());
bool result = node.applyDiff(diff);
EXPECT_TRUE(result);
EXPECT_DOUBLE_EQ(-4.1, node.getValue());
delete diff;
}
TEST(FloatNode, applyDiff)
{
FloatNode node(NULL, "test", 1.2);
FloatDiff diff(1.2, 2.4);
DefinitionDiff odiff("badtype");
bool result;
EXPECT_DOUBLE_EQ(1.2, node.getValue());
// apply
result = node.applyDiff(&diff);
EXPECT_TRUE(result);
EXPECT_DOUBLE_EQ(2.4, node.getValue());
// can't apply twice
result = node.applyDiff(&diff);
EXPECT_FALSE(result);
EXPECT_DOUBLE_EQ(2.4, node.getValue());
// reverse
result = node.applyDiff(&diff, true);
EXPECT_TRUE(result);
EXPECT_DOUBLE_EQ(1.2, node.getValue());
// can't reverse twice
result = node.applyDiff(&diff, true);
EXPECT_FALSE(result);
EXPECT_DOUBLE_EQ(1.2, node.getValue());
// bad type
result = node.applyDiff(&odiff);
EXPECT_FALSE(result);
EXPECT_DOUBLE_EQ(1.2, node.getValue());
// apply again
result = node.applyDiff(&diff);
EXPECT_TRUE(result);
EXPECT_DOUBLE_EQ(2.4, node.getValue());
}