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" #include "RandomGenerator.h"
AtmosphereDefinition::AtmosphereDefinition(DefinitionNode* parent): AtmosphereDefinition::AtmosphereDefinition(DefinitionNode* parent):
DefinitionNode(parent, "atmosphere") DefinitionNode(parent, "atmosphere", "atmosphere")
{ {
} }

View file

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

View file

@ -6,7 +6,7 @@
#include "PackStream.h" #include "PackStream.h"
CloudLayerDefinition::CloudLayerDefinition(DefinitionNode* parent): CloudLayerDefinition::CloudLayerDefinition(DefinitionNode* parent):
DefinitionNode(parent, "layer") DefinitionNode(parent, "layer", "cloudlayer")
{ {
type = CIRRUS; type = CIRRUS;
altitude = 0.5; 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 "Logs.h"
#include "PackStream.h" #include "PackStream.h"
#include "DefinitionDiff.h"
DefinitionNode::DefinitionNode(DefinitionNode* parent, const std::string &name): DefinitionNode::DefinitionNode(DefinitionNode* parent, const std::string &name, const std::string &type_name):
parent(parent), name(name) parent(parent), type_name(type_name), name(name)
{ {
if (parent) if (parent)
{ {
@ -71,6 +72,20 @@ std::string DefinitionNode::toString(int indent) const
return result; 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 void DefinitionNode::save(PackStream* stream) const
{ {
int children_count = (int)children.size(); int children_count = (int)children.size();
@ -128,8 +143,15 @@ void DefinitionNode::load(PackStream* stream)
void DefinitionNode::copy(DefinitionNode* destination) const void DefinitionNode::copy(DefinitionNode* destination) const
{ {
destination->setName(name); if (destination->getTypeName() == getTypeName())
// can't copy children as we don't know their types... {
destination->setName(name);
// TODO Copy children ?
}
else
{
Logs::error() << "Can't copy from " << getTypeName() << " to " << destination->getTypeName() << std::endl;
}
} }
void DefinitionNode::validate() void DefinitionNode::validate()

View file

@ -12,7 +12,7 @@ namespace definition {
class DEFINITIONSHARED_EXPORT DefinitionNode class DEFINITIONSHARED_EXPORT DefinitionNode
{ {
public: public:
DefinitionNode(DefinitionNode* parent, const std::string &name); DefinitionNode(DefinitionNode* parent, const std::string &name, const std::string &type_name = "");
virtual ~DefinitionNode(); virtual ~DefinitionNode();
virtual void save(PackStream* stream) const; virtual void save(PackStream* stream) const;
@ -24,6 +24,8 @@ public:
inline const std::string &getName() const {return name;} inline const std::string &getName() const {return name;}
virtual void setName(const std::string &name); virtual void setName(const std::string &name);
inline const std::string &getTypeName() const {return type_name;}
virtual Scenery* getScenery(); virtual Scenery* getScenery();
inline const DefinitionNode* getParent() const {return parent;} inline const DefinitionNode* getParent() const {return parent;}
@ -35,6 +37,17 @@ public:
*/ */
virtual std::string toString(int indent = 0) const; 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: protected:
void addChild(DefinitionNode* child); void addChild(DefinitionNode* child);
void removeChild(DefinitionNode* child); void removeChild(DefinitionNode* child);
@ -51,6 +64,7 @@ protected:
private: private:
DefinitionNode* parent; DefinitionNode* parent;
DefinitionNode* root; DefinitionNode* root;
std::string type_name;
std::string name; std::string name;
std::vector<DefinitionNode*> children; 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 "FloatNode.h"
#include "PackStream.h" #include "PackStream.h"
#include "FloatDiff.h"
#include "Logs.h"
#include <sstream> #include <sstream>
#include <cassert>
FloatNode::FloatNode(DefinitionNode* parent, const std::string &name, double value): 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 void FloatNode::copy(DefinitionNode *destination) const
{ {
// TODO type check if (destination->getTypeName() == getTypeName())
{
((FloatNode *)destination)->value = value; ((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 save(PackStream* stream) const override;
virtual void load(PackStream* stream) override; virtual void load(PackStream* stream) override;
virtual void copy(DefinitionNode* destination) const 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: private:
double value; double value;
}; };

View file

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

View file

@ -7,7 +7,7 @@
#include "PaintedGridBrush.h" #include "PaintedGridBrush.h"
PaintedGrid::PaintedGrid(DefinitionNode *parent): PaintedGrid::PaintedGrid(DefinitionNode *parent):
DefinitionNode(parent, "grid") DefinitionNode(parent, "grid", "grid")
{ {
merged_data = new PaintedGridData; merged_data = new PaintedGridData;
brush_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; static const int DATA_VERSION = 1;
Scenery::Scenery(): Scenery::Scenery():
DefinitionNode(NULL, "scenery") DefinitionNode(NULL, "scenery", "scenery")
{ {
addChild(atmosphere = new AtmosphereDefinition(this)); addChild(atmosphere = new AtmosphereDefinition(this));
addChild(camera = new CameraDefinition); addChild(camera = new CameraDefinition);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
#include "BaseTestCase.h" #include "BaseTestCase.h"
#include "FloatNode.h" #include "FloatNode.h"
#include "FloatDiff.h"
#include "PackStream.h" #include "PackStream.h"
TEST(FloatNode, toString) TEST(FloatNode, toString)
@ -28,3 +29,83 @@ TEST(FloatNode, saveLoadAndSkip)
EXPECT_DOUBLE_EQ(4.3, testb2.getValue()); 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());
}