Browse Source

Allow matching transform primitives in their generic forms (e.g. translateX to translate3d). Helper functions and constructors for creating Transform animations.

Michael 7 years ago
parent
commit
4ec999e3d8

+ 1 - 0
Include/Rocket/Core/Core.h

@@ -72,6 +72,7 @@
 #include "SystemInterface.h"
 #include "SystemInterface.h"
 #include "Texture.h"
 #include "Texture.h"
 #include "Types.h"
 #include "Types.h"
+#include "Tween.h"
 #include "Vertex.h"
 #include "Vertex.h"
 #include "XMLNodeHandler.h"
 #include "XMLNodeHandler.h"
 #include "XMLParser.h"
 #include "XMLParser.h"

+ 10 - 10
Include/Rocket/Core/Element.h

@@ -214,16 +214,14 @@ public:
 	/// @param[in] name The name of the property to fetch the value for.
 	/// @param[in] name The name of the property to fetch the value for.
 	/// @return The value of this property for this element, or NULL if this property has not been explicitly defined for this element.
 	/// @return The value of this property for this element, or NULL if this property has not been explicitly defined for this element.
 	const Property* GetLocalProperty(const String& name);		
 	const Property* GetLocalProperty(const String& name);		
-	/// Resolves one of this element's properties. If the value is a number or px, this is returned. If it's a 
-	/// percentage then it is resolved based on the second argument (the base value).
-	/// If it's an angle it is returned as degrees.
+	/// Resolves one of this element's properties. If the value is a number or px, this is returned. Angles are returned as radians.
+	/// Precentages are resolved based on the second argument (the base value).
 	/// @param[in] name The name of the property to resolve the value for.
 	/// @param[in] name The name of the property to resolve the value for.
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @return The value of this property for this element.
 	/// @return The value of this property for this element.
 	float ResolveProperty(const String& name, float base_value);
 	float ResolveProperty(const String& name, float base_value);
-	/// Resolves one of this element's non-inherited properties. If the value is a number or px, this is returned. If it's a 
-	/// percentage then it is resolved based on the second argument (the base value).
-	/// If it's an angle it is returned as degrees.
+	/// Resolves one of this element's non-inherited properties. If the value is a number or px, this is returned. Angles are returned as radians.
+	/// Precentages are resolved based on the second argument (the base value).
 	/// @param[in] name The property to resolve the value for.
 	/// @param[in] name The property to resolve the value for.
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @return The value of this property for this element.
 	/// @return The value of this property for this element.
@@ -292,10 +290,12 @@ public:
 	const Vector2f Project(const Vector2f& point) noexcept;
 	const Vector2f Project(const Vector2f& point) noexcept;
 
 
 	/// Start an animation of the given property on this element.
 	/// Start an animation of the given property on this element.
-	/// Currently, only float and Colourb property values are supported. If an animation of the same property name exists, the target value
-	/// and duration will be added as a new animation key, adding to its total duration. Then, num_iterations and alternate_direction will be ignored.
-	void Animate(const String& property_name, const Property& target_value, float duration, int num_iterations = 1, bool alternate_direction = false, Tween tween = Tween{});
-
+	/// Target values must have the same unit as the property set on this element. Keywords are not supported.
+	/// If an animation of the same property name exists, the target value and duration will be added as a new animation key, 
+	/// adding to its total duration. Then, num_iterations, alternate_direction and delay will be ignored.
+	/// @return True if a new animation or key was added.
+	bool Animate(const String& property_name, const Property& target_value, float duration, Tween tween = Tween{}, int num_iterations = 1, bool alternate_direction = true, float delay = 0.0f);
+	
 	/// Iterates over the properties defined on this element.
 	/// Iterates over the properties defined on this element.
 	/// @param[inout] index Index of the property to fetch. This is incremented to the next valid index after the fetch. Indices are not necessarily incremental.
 	/// @param[inout] index Index of the property to fetch. This is incremented to the next valid index after the fetch. Indices are not necessarily incremental.
 	/// @param[out] pseudo_classes The pseudo-classes the property is defined by.
 	/// @param[out] pseudo_classes The pseudo-classes the property is defined by.

+ 7 - 0
Include/Rocket/Core/Transform.h

@@ -36,6 +36,7 @@ namespace Core {
 
 
 class ViewState;
 class ViewState;
 namespace Transforms { struct Primitive; }
 namespace Transforms { struct Primitive; }
+class Property;
 
 
 /**
 /**
 	The Transform class holds the information parsed from an element's
 	The Transform class holds the information parsed from an element's
@@ -56,6 +57,12 @@ public:
 	/// Default constructor, initializes an identity transform
 	/// Default constructor, initializes an identity transform
 	Transform();
 	Transform();
 
 
+	/// Construct transform with a list of primitives
+	Transform(std::vector<Transforms::Primitive> primitives);
+
+	/// Helper function to create a Property with TransformRef from list of primitives
+	static Property MakeProperty(std::vector<Transforms::Primitive> primitives);
+
 	/// Copy constructor
 	/// Copy constructor
 	Transform(const Transform& other);
 	Transform(const Transform& other);
 
 

+ 40 - 3
Include/Rocket/Core/TransformPrimitive.h

@@ -81,8 +81,14 @@ struct ResolvedPrimitive
 		for (size_t i = 0; i < N; ++i)
 		for (size_t i = 0; i < N; ++i)
 			this->values[i] = values[i].ResolveAbsoluteUnit(base_units[i]);
 			this->values[i] = values[i].ResolveAbsoluteUnit(base_units[i]);
 	}
 	}
+	ResolvedPrimitive(std::array<NumericValue, N> values, std::array<Property::Unit, N> base_units) noexcept
+	{
+		for (size_t i = 0; i < N; ++i)
+			this->values[i] = values[i].ResolveAbsoluteUnit(base_units[i]);
+	}
+	ResolvedPrimitive(std::array<float, N> values) noexcept : values(values) { }
 	
 	
-	float values[N];
+	std::array<float, N> values;
 };
 };
 
 
 template< size_t N >
 template< size_t N >
@@ -90,10 +96,12 @@ struct UnresolvedPrimitive
 {
 {
 	UnresolvedPrimitive(const NumericValue* values) noexcept
 	UnresolvedPrimitive(const NumericValue* values) noexcept
 	{
 	{
-		memcpy(this->values, values, sizeof(this->values));
+		for (size_t i = 0; i < N; ++i)
+			this->values[i] = values[i];
 	}
 	}
+	UnresolvedPrimitive(std::array<NumericValue, N> values) noexcept : values(values) { }
 
 
-	NumericValue values[N];
+	std::array<NumericValue, N> values;
 };
 };
 
 
 
 
@@ -114,91 +122,114 @@ struct Matrix3D : public ResolvedPrimitive< 16 >
 struct TranslateX : public UnresolvedPrimitive< 1 >
 struct TranslateX : public UnresolvedPrimitive< 1 >
 {
 {
 	TranslateX(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
 	TranslateX(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
+	TranslateX(float x, Property::Unit unit) noexcept : UnresolvedPrimitive({ NumericValue{x, unit} }) { }
 };
 };
 
 
 struct TranslateY : public UnresolvedPrimitive< 1 >
 struct TranslateY : public UnresolvedPrimitive< 1 >
 {
 {
 	TranslateY(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
 	TranslateY(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
+	TranslateY(float y, Property::Unit unit) noexcept : UnresolvedPrimitive({ NumericValue(y, unit) }) { }
 };
 };
 
 
 struct TranslateZ : public UnresolvedPrimitive< 1 >
 struct TranslateZ : public UnresolvedPrimitive< 1 >
 {
 {
 	TranslateZ(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
 	TranslateZ(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
+	TranslateZ(float z, Property::Unit unit) noexcept : UnresolvedPrimitive({ NumericValue(z, unit) }) { }
 };
 };
 
 
 struct Translate2D : public UnresolvedPrimitive< 2 >
 struct Translate2D : public UnresolvedPrimitive< 2 >
 {
 {
 	Translate2D(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
 	Translate2D(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
+	Translate2D(float x, float y, Property::Unit units) noexcept : UnresolvedPrimitive({ NumericValue(x, units), NumericValue(y, units) }) { }
 };
 };
 
 
 struct Translate3D : public UnresolvedPrimitive< 3 >
 struct Translate3D : public UnresolvedPrimitive< 3 >
 {
 {
 	Translate3D(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
 	Translate3D(const NumericValue* values) noexcept : UnresolvedPrimitive(values) { }
+	Translate3D(NumericValue x, NumericValue y, NumericValue z) noexcept : UnresolvedPrimitive({ x, y, z }) { }
+	Translate3D(float x, float y, float z, Property::Unit units) noexcept : UnresolvedPrimitive({ NumericValue(x, units), NumericValue(y, units), NumericValue(z, units) }) { }
 };
 };
 
 
 struct ScaleX : public ResolvedPrimitive< 1 >
 struct ScaleX : public ResolvedPrimitive< 1 >
 {
 {
 	ScaleX(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
 	ScaleX(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
+	ScaleX(float value) noexcept : ResolvedPrimitive({ value }) { }
 };
 };
 
 
 struct ScaleY : public ResolvedPrimitive< 1 >
 struct ScaleY : public ResolvedPrimitive< 1 >
 {
 {
 	ScaleY(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
 	ScaleY(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
+	ScaleY(float value) noexcept : ResolvedPrimitive({ value }) { }
 };
 };
 
 
 struct ScaleZ : public ResolvedPrimitive< 1 >
 struct ScaleZ : public ResolvedPrimitive< 1 >
 {
 {
 	ScaleZ(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
 	ScaleZ(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
+	ScaleZ(float value) noexcept : ResolvedPrimitive({ value }) { }
 };
 };
 
 
 struct Scale2D : public ResolvedPrimitive< 2 >
 struct Scale2D : public ResolvedPrimitive< 2 >
 {
 {
 	Scale2D(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
 	Scale2D(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
+	Scale2D(float xy) noexcept : ResolvedPrimitive({ xy, xy }) { }
+	Scale2D(float x, float y) noexcept : ResolvedPrimitive({ x, y }) { }
 };
 };
 
 
 struct Scale3D : public ResolvedPrimitive< 3 >
 struct Scale3D : public ResolvedPrimitive< 3 >
 {
 {
 	Scale3D(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
 	Scale3D(const NumericValue* values) noexcept : ResolvedPrimitive(values) { }
+	Scale3D(float xyz) noexcept : ResolvedPrimitive({ xyz, xyz, xyz }) { }
+	Scale3D(float x, float y, float z) noexcept : ResolvedPrimitive({ x, y, z }) { }
 };
 };
 
 
 struct RotateX : public ResolvedPrimitive< 1 >
 struct RotateX : public ResolvedPrimitive< 1 >
 {
 {
 	RotateX(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
 	RotateX(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
+	RotateX(float angle, Property::Unit unit = Property::DEG) noexcept : ResolvedPrimitive({ NumericValue{ angle, unit } }, { Property::RAD }) { }
 };
 };
 
 
 struct RotateY : public ResolvedPrimitive< 1 >
 struct RotateY : public ResolvedPrimitive< 1 >
 {
 {
 	RotateY(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) {}
 	RotateY(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) {}
+	RotateY(float angle, Property::Unit unit = Property::DEG) noexcept : ResolvedPrimitive({ NumericValue{ angle, unit } }, { Property::RAD }) { }
 };
 };
 
 
 struct RotateZ : public ResolvedPrimitive< 1 >
 struct RotateZ : public ResolvedPrimitive< 1 >
 {
 {
 	RotateZ(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
 	RotateZ(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
+	RotateZ(float angle, Property::Unit unit = Property::DEG) noexcept : ResolvedPrimitive({ NumericValue{ angle, unit } }, { Property::RAD }) { }
 };
 };
 
 
 struct Rotate2D : public ResolvedPrimitive< 1 >
 struct Rotate2D : public ResolvedPrimitive< 1 >
 {
 {
 	Rotate2D(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
 	Rotate2D(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
+	Rotate2D(float angle, Property::Unit unit = Property::DEG) noexcept : ResolvedPrimitive({ NumericValue{ angle, unit } }, { Property::RAD }) { }
 };
 };
 
 
 struct Rotate3D : public ResolvedPrimitive< 4 >
 struct Rotate3D : public ResolvedPrimitive< 4 >
 {
 {
 	Rotate3D(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::NUMBER, Property::NUMBER, Property::NUMBER, Property::RAD }) { }
 	Rotate3D(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::NUMBER, Property::NUMBER, Property::NUMBER, Property::RAD }) { }
+	Rotate3D(float x, float y, float z, float angle_rad, Property::Unit angle_unit = Property::DEG) noexcept
+		: ResolvedPrimitive({ NumericValue{x, Property::NUMBER}, NumericValue{x, Property::NUMBER}, NumericValue{x, Property::NUMBER}, NumericValue{x, angle_unit} },
+			{ Property::NUMBER, Property::NUMBER, Property::NUMBER, Property::RAD }) { }
 };
 };
 
 
 struct SkewX : public ResolvedPrimitive< 1 >
 struct SkewX : public ResolvedPrimitive< 1 >
 {
 {
 	SkewX(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
 	SkewX(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
+	SkewX(float angle, Property::Unit unit = Property::DEG) noexcept : ResolvedPrimitive({ NumericValue{ angle, unit } }, { Property::RAD }) { }
 };
 };
 
 
 struct SkewY : public ResolvedPrimitive< 1 >
 struct SkewY : public ResolvedPrimitive< 1 >
 {
 {
 	SkewY(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
 	SkewY(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD }) { }
+	SkewY(float angle, Property::Unit unit = Property::DEG) noexcept : ResolvedPrimitive({ NumericValue{ angle, unit } }, { Property::RAD }) { }
 };
 };
 
 
 struct Skew2D : public ResolvedPrimitive< 2 >
 struct Skew2D : public ResolvedPrimitive< 2 >
 {
 {
 	Skew2D(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD, Property::RAD }) { }
 	Skew2D(const NumericValue* values) noexcept : ResolvedPrimitive(values, { Property::RAD, Property::RAD }) { }
+	Skew2D(float x, float y, Property::Unit unit = Property::DEG) noexcept : ResolvedPrimitive({NumericValue{ x, unit }, { NumericValue{ y, unit }}}, {Property::RAD, Property::RAD}) { }
 };
 };
 
 
 struct Perspective : public UnresolvedPrimitive< 1 >
 struct Perspective : public UnresolvedPrimitive< 1 >
@@ -230,12 +261,18 @@ struct Primitive
 {
 {
 	PrimitiveVariant primitive;
 	PrimitiveVariant primitive;
 
 
+	template<typename PrimitiveType>
+	Primitive(PrimitiveType primitive) : primitive(primitive) {}
+
 	bool ResolveTransform(Matrix4f& m, Element& e) const noexcept;
 	bool ResolveTransform(Matrix4f& m, Element& e) const noexcept;
 	bool ResolvePerspective(float &p, Element& e) const noexcept;
 	bool ResolvePerspective(float &p, Element& e) const noexcept;
 	
 	
 	// Promote units to basic types which can be interpolated, that is, convert 'length -> pixel' for unresolved primitives.
 	// Promote units to basic types which can be interpolated, that is, convert 'length -> pixel' for unresolved primitives.
 	bool ResolveUnits(Element& e) noexcept;
 	bool ResolveUnits(Element& e) noexcept;
 
 
+
+	static bool TryConvertToMatchingGenericType(Primitive& p0, Primitive& p1) noexcept;
+
 	bool InterpolateWith(const Primitive& other, float alpha) noexcept;
 	bool InterpolateWith(const Primitive& other, float alpha) noexcept;
 	void SetIdentity() noexcept;
 	void SetIdentity() noexcept;
 };
 };

+ 32 - 20
Include/Rocket/Core/Tween.h

@@ -28,8 +28,6 @@
 #ifndef ROCKETCORETWEEN_H
 #ifndef ROCKETCORETWEEN_H
 #define ROCKETCORETWEEN_H
 #define ROCKETCORETWEEN_H
 
 
-#include <functional>
-
 namespace Rocket {
 namespace Rocket {
 namespace Core {
 namespace Core {
 
 
@@ -37,23 +35,31 @@ namespace Core {
 class Tween {
 class Tween {
 public:
 public:
 	enum Type { None, Back, Bounce, Circular, Cubic, Elastic, Exponential, Linear, Quadratic, Quartic, Quintic, Sine, Callback };
 	enum Type { None, Back, Bounce, Circular, Cubic, Elastic, Exponential, Linear, Quadratic, Quartic, Quintic, Sine, Callback };
-	enum Direction { In, Out, InOut };
+	enum Direction { In = 1, Out = 2, InOut = 3 };
+	typedef float(*CallbackFnc)(float);
 
 
-	Tween(Type type = Linear, Direction direction = In) : type(type), direction(direction) {}
-	Tween(std::function<float(float)> callback, Direction direction = In) : type(Callback), direction(direction), callback(callback) {}
+	Tween(Type type = Linear, Direction direction = In) {
+		if (direction & In) type_in = type;
+		if (direction & Out) type_out = type;
+	}
+	Tween(Type type_in, Type type_out) : type_in(type_in), type_out(type_out) {}
+	Tween(CallbackFnc callback, Direction direction = In) : callback(callback) {
+		if (direction & In) type_in = Callback;
+		if (direction & Out) type_out = Callback;
+	}
 
 
 	float operator()(float t) const
 	float operator()(float t) const
 	{
 	{
-		if (type == None)
-			return t;
-
-		switch (direction)
+		if (type_in != None && type_out == None)
 		{
 		{
-		case In:
 			return in(t);
 			return in(t);
-		case Out:
+		}
+		if (type_in == None && type_out != None)
+		{
 			return out(t);
 			return out(t);
-		case InOut:
+		}
+		if (type_in != None && type_out != None)
+		{
 			return in_out(t);
 			return in_out(t);
 		}
 		}
 		return t;
 		return t;
@@ -119,7 +125,9 @@ public:
 
 
 
 
 private:
 private:
-	float in(float t) const
+
+
+	float tween(Type type, float t) const
 	{
 	{
 		switch (type)
 		switch (type)
 		{
 		{
@@ -146,29 +154,33 @@ private:
 		case Sine:
 		case Sine:
 			return sine(t);
 			return sine(t);
 		case Callback:
 		case Callback:
-			if(callback(t))
-				return callback(t);
+			if (callback)
+				return (*callback)(t);
 			break;
 			break;
 		}
 		}
 		return t;
 		return t;
 	}
 	}
+	float in(float t) const
+	{
+		return tween(type_in, t);
+	}
 	float out(float t) const
 	float out(float t) const
 	{
 	{
-		return 1.0f - in(1.0f - t);
+		return 1.0f - tween(type_out, 1.0f - t);
 	}
 	}
 	float in_out(float t) const
 	float in_out(float t) const
 	{
 	{
 		if (t < 0.5f)
 		if (t < 0.5f)
-			return in(2.0f*t)*0.5f;
+			return tween(type_in, 2.0f*t)*0.5f;
 		else
 		else
 			return 0.5f + out(2.0f*t - 1.0f)*0.5f;
 			return 0.5f + out(2.0f*t - 1.0f)*0.5f;
 	}
 	}
 
 
 	inline static float square(float t) { return t * t; }
 	inline static float square(float t) { return t * t; }
 
 
-	Type type;
-	Direction direction;
-	std::function<float(float)> callback;
+	Type type_in = None;
+	Type type_out = None;
+	CallbackFnc callback = nullptr;
 };
 };
 
 
 
 

+ 26 - 20
Include/Rocket/Core/Variant.h

@@ -84,11 +84,36 @@ public:
 
 
 	/// Gets the current internal representation type.
 	/// Gets the current internal representation type.
 	/// @return The type of data stored in the variant internally.
 	/// @return The type of data stored in the variant internally.
-	Type GetType() const;
+	inline Type GetType() const;
 
 
 	/// Shares another variant's data with this variant.
 	/// Shares another variant's data with this variant.
 	/// @param[in] copy Variant to share data.
 	/// @param[in] copy Variant to share data.
 	void Set(const Variant& copy);
 	void Set(const Variant& copy);
+
+	/// Clear and set a new value to this variant.
+	/// @param[in] t New value to set.
+	template<typename T>
+	void Reset(const T& t);
+
+	/// Templatised data accessor. TypeConverters will be used to attempt to convert from the
+	/// internal representation to the requested representation.
+	/// @return Data in the requested type.
+	template< typename T >
+	T Get() const;
+
+	/// Templatised data accessor. TypeConverters will be used to attempt to convert from the
+	/// internal representation to the requested representation.
+	/// @param[out] value Data in the requested type.
+	/// @return True if the value was converted and returned, false if no data was stored in the variant.
+	template< typename T >
+	bool GetInto(T& value) const;
+
+	/// Assigns another variant's internal data to this variant.
+	/// @param[in] copy Variant to share data.
+	Variant& operator=(const Variant& copy);
+
+private:
+
 	/// Sets a byte value on this variant.
 	/// Sets a byte value on this variant.
 	/// @param[in] value New value to set.
 	/// @param[in] value New value to set.
 	void Set(const byte value);
 	void Set(const byte value);
@@ -134,25 +159,6 @@ public:
 	/// Sets a script object value on this variant.
 	/// Sets a script object value on this variant.
 	/// @param[in] value New value to set.
 	/// @param[in] value New value to set.
 	void Set(ScriptInterface* value);
 	void Set(ScriptInterface* value);
-
-	/// Templatised data accessor. TypeConverters will be used to attempt to convert from the
-	/// internal representation to the requested representation.
-	/// @return Data in the requested type.
-	template< typename T >
-	T Get() const;
-
-	/// Templatised data accessor. TypeConverters will be used to attempt to convert from the
-	/// internal representation to the requested representation.
-	/// @param[out] value Data in the requested type.
-	/// @return True if the value was converted and returned, false if no data was stored in the variant.
-	template< typename T >
-	bool GetInto(T& value) const;
-
-	/// Assigns another variant's internal data to this variant.
-	/// @param[in] copy Variant to share data.
-	Variant& operator=(const Variant& copy);
-
-private:
 	
 	
 #ifdef ROCKET_ARCH_64
 #ifdef ROCKET_ARCH_64
 		static const int LOCAL_DATA_SIZE = 40; // Required for Strings
 		static const int LOCAL_DATA_SIZE = 40; // Required for Strings

+ 14 - 0
Include/Rocket/Core/Variant.inl

@@ -25,6 +25,12 @@
  *
  *
  */
  */
 
 
+
+inline Variant::Type Variant::GetType() const
+{
+	return type;
+}
+
 // Constructs a variant with internal data.
 // Constructs a variant with internal data.
 template< typename T >
 template< typename T >
 Variant::Variant(const T& t) : type(NONE)
 Variant::Variant(const T& t) : type(NONE)
@@ -32,6 +38,14 @@ Variant::Variant(const T& t) : type(NONE)
 	Set( t );
 	Set( t );
 }
 }
 
 
+// Clear and set new value
+template< typename T >
+void Variant::Reset(const T& t)
+{
+	Clear();
+	Set(t);
+}
+
 // Templatised data accessor.
 // Templatised data accessor.
 template< typename T >
 template< typename T >
 bool Variant::GetInto(T& value) const
 bool Variant::GetInto(T& value) const

+ 1 - 2
Samples/basic/animation/data/animation.rml

@@ -23,7 +23,7 @@
 		#start_game 
 		#start_game 
 		{
 		{
 			opacity: 0.5;
 			opacity: 0.5;
-			transform: rotate(10) translateX(100) scale(1.2);
+			transform: rotate(-350) translateX(100) scale(1.2);
 			transform-origin: 30% 80% 0;
 			transform-origin: 30% 80% 0;
 		}
 		}
 
 
@@ -39,6 +39,5 @@
 	<button id="options">Options</button><br />
 	<button id="options">Options</button><br />
 	<button id="help">Help</button><br />
 	<button id="help">Help</button><br />
 	<button id="exit" onclick="exit">Exit</button>
 	<button id="exit" onclick="exit">Exit</button>
-	<div style="position: absolute; right: 15.4px; bottom: 20.5px; height: 30px; width: 20px; border: 3px #000; background-color: #c66;">A</div>
 </body>
 </body>
 </rml>
 </rml>

+ 38 - 32
Samples/basic/animation/src/main.cpp

@@ -38,7 +38,6 @@
 
 
 // Animations TODO:
 // Animations TODO:
 //  - Proper interpolation of full transform matrices (split into translate/rotate/skew/scale).
 //  - Proper interpolation of full transform matrices (split into translate/rotate/skew/scale).
-//  - Support interpolation of primitive derivatives without going to full matrices.
 //  - Better error reporting when submitting invalid animations, check validity on add. Remove animation if invalid.
 //  - Better error reporting when submitting invalid animations, check validity on add. Remove animation if invalid.
 //  - RCSS support? Both @keyframes and transition, maybe.
 //  - RCSS support? Both @keyframes and transition, maybe.
 //  - Profiling
 //  - Profiling
@@ -54,37 +53,44 @@ public:
 		document = context->LoadDocument("basic/animation/data/animation.rml");
 		document = context->LoadDocument("basic/animation/data/animation.rml");
 		if (document != NULL)
 		if (document != NULL)
 		{
 		{
-			document->GetElementById("title")->SetInnerRML(title);
-			document->SetProperty("left", Property(position.x, Property::PX));
-			document->SetProperty("top", Property(position.y, Property::PX));
-			document->Animate("opacity", Property(0.6f, Property::NUMBER), 0.5f, -1, true);
-
-			auto el = document->GetElementById("high_score");
-			el->Animate("margin-left", Property(0.f, Property::PX), 0.3f, 10, true);
-			el->Animate("margin-left", Property(100.f, Property::PX), 0.6f);
-
-			//el = document->GetElementById("exit");
-			//el->Animate("margin-left", Property(100.f, Property::PX), 8.0f, -1, true);
-
-			el = document->GetElementById("start_game");
-			auto t0 = TransformRef{ new Transform };
-			auto v0 = Transforms::NumericValue(3.f, Property::NUMBER);
-			t0->AddPrimitive({ Transforms::Scale2D{ &v0 } });
-			//auto t1 = TransformRef{ new Transform };
-			//auto v1 = Transforms::NumericValue(370.f, Property::DEG);
-			//t1->AddPrimitive({ Transforms::Rotate2D{ &v1 } });
-			PropertyDictionary pd;
-			StyleSheetSpecification::ParsePropertyDeclaration(pd, "transform", "rotate(370deg)");
-			auto p = pd.GetProperty("transform");
-			el->Animate("transform", *p, 1.8f, -1, true, Tween{ Tween::Elastic, Tween::In });
-			el->Animate("transform", Property(t0, Property::TRANSFORM), 1.3f, -1, true, Tween{ Tween::Cubic, Tween::In });
-
-			el = document->GetElementById("help");
-			el->Animate("image-color", Property(Colourb(128, 255, 255, 255), Property::COLOUR), 0.3f, -1, false);
-			el->Animate("image-color", Property(Colourb(128, 128, 255, 255), Property::COLOUR), 0.3f);
-			el->Animate("image-color", Property(Colourb(0, 128, 128, 255), Property::COLOUR), 0.3f);
-			el->Animate("image-color", Property(Colourb(64, 128, 255, 0), Property::COLOUR), 0.9f);
-			el->Animate("image-color", Property(Colourb(255, 255, 255, 255), Property::COLOUR), 0.3f);
+			{
+				document->GetElementById("title")->SetInnerRML(title);
+				document->SetProperty("left", Property(position.x, Property::PX));
+				document->SetProperty("top", Property(position.y, Property::PX));
+				//document->Animate("opacity", Property(0.6f, Property::NUMBER), 0.5f, -1, true);
+			}
+
+
+			{
+				auto el = document->GetElementById("start_game");
+				PropertyDictionary pd;
+				StyleSheetSpecification::ParsePropertyDeclaration(pd, "transform", "rotate(10)");
+				auto p = pd.GetProperty("transform");
+				el->Animate("transform", *p, 1.8f, Tween{ Tween::Elastic, Tween::InOut }, -1, true);
+
+				auto pp = Transform::MakeProperty({ Transforms::Scale2D{3.f} });
+				el->Animate("transform", pp, 1.3f, Tween{ Tween::Elastic, Tween::InOut }, -1, true);
+			}
+
+			//{
+			//	auto el = document->GetElementById("high_score");
+			//	el->Animate("margin-left", Property(0.f, Property::PX), 0.3f, 10, true);
+			//	el->Animate("margin-left", Property(100.f, Property::PX), 0.6f);
+			//}
+
+			{
+				auto el = document->GetElementById("exit");
+				el->Animate("margin-left", Property(100.f, Property::PX), 1.0f, Tween{}, -1, true);
+			}
+
+			//{
+			//	auto el = document->GetElementById("help");
+			//	el->Animate("image-color", Property(Colourb(128, 255, 255, 255), Property::COLOUR), 0.3f, -1, false);
+			//	el->Animate("image-color", Property(Colourb(128, 128, 255, 255), Property::COLOUR), 0.3f);
+			//	el->Animate("image-color", Property(Colourb(0, 128, 128, 255), Property::COLOUR), 0.3f);
+			//	el->Animate("image-color", Property(Colourb(64, 128, 255, 0), Property::COLOUR), 0.9f);
+			//	el->Animate("image-color", Property(Colourb(255, 255, 255, 255), Property::COLOUR), 0.3f);
+			//}
 
 
 			document->Show();
 			document->Show();
 		}
 		}

+ 1 - 1
Source/Controls/ElementForm.cpp

@@ -81,7 +81,7 @@ void ElementForm::Submit(const Rocket::Core::String& name, const Rocket::Core::S
 		// If the item already exists, append to it.
 		// If the item already exists, append to it.
 		Rocket::Core::Variant* value = values.Get(control_name);
 		Rocket::Core::Variant* value = values.Get(control_name);
 		if (value != NULL)
 		if (value != NULL)
-			value->Set(value->Get< Rocket::Core::String >() + ", " + control_value);
+			value->Reset(value->Get< Rocket::Core::String >() + ", " + control_value);
 		else
 		else
 			values.Set< Rocket::Core::String >(control_name, control_value);					
 			values.Set< Rocket::Core::String >(control_name, control_value);					
 	}
 	}

+ 23 - 6
Source/Core/Element.cpp

@@ -889,8 +889,11 @@ const Vector2f Element::Project(const Vector2f& point) noexcept
 	}
 	}
 }
 }
 
 
-void Element::Animate(const String & property_name, const Property & target_value, float duration, int num_iterations, bool alternate_direction, Tween tween)
+bool Element::Animate(const String & property_name, const Property & target_value, float duration, Tween tween, int num_iterations, bool alternate_direction, float delay)
 {
 {
+	if (delay < 0.0f)
+		return false;
+
 	ElementAnimation* animation = nullptr;
 	ElementAnimation* animation = nullptr;
 
 
 	for (auto& existing_animation : animations)
 	for (auto& existing_animation : animations)
@@ -906,13 +909,20 @@ void Element::Animate(const String & property_name, const Property & target_valu
 
 
 	if (!animation)
 	if (!animation)
 	{
 	{
-		float time = Clock::GetElapsedTime();
+		float start_time = Clock::GetElapsedTime() + delay;
 		const Property* property = GetProperty(property_name);
 		const Property* property = GetProperty(property_name);
-		if (!property) return;
+		if (!property) 
+			return false;
 
 
-		ElementAnimation new_animation{ property_name, *property, time, duration, num_iterations, alternate_direction, tween };
-		animations.push_back(new_animation);
+		animations.push_back(
+			ElementAnimation{ property_name, *property, start_time, duration, num_iterations, alternate_direction }
+		);
 		animation = &animations.back();
 		animation = &animations.back();
+		if(!animation->IsValid())
+		{
+			animations.pop_back();
+			animation = nullptr;
+		}
 	}
 	}
 	else
 	else
 	{
 	{
@@ -920,7 +930,14 @@ void Element::Animate(const String & property_name, const Property & target_valu
 		animation->SetDuration(target_time);
 		animation->SetDuration(target_time);
 	}
 	}
 
 
-	animation->AddKey(target_time, target_value, *this);
+	bool result = false;
+
+	if(animation)
+	{
+		result = animation->AddKey(target_time, target_value, *this, tween);
+	}
+	
+	return result;
 }
 }
 
 
 // Iterates over the properties defined on this element.
 // Iterates over the properties defined on this element.

+ 74 - 20
Source/Core/ElementAnimation.cpp

@@ -145,8 +145,12 @@ static PrepareTransformResult PrepareTransformPair(Transform& t0, Transform& t1,
 		{
 		{
 			if (prims0[i].primitive.index() != prims1[i].primitive.index())
 			if (prims0[i].primitive.index() != prims1[i].primitive.index())
 			{
 			{
-				same_primitives = false;
-				break;
+				// They are not the same, but see if we can convert them to their more generic form
+				if(!Primitive::TryConvertToMatchingGenericType(prims0[i], prims1[i]))
+				{
+					same_primitives = false;
+					break;
+				}
 			}
 			}
 		}
 		}
 		if (same_primitives)
 		if (same_primitives)
@@ -171,24 +175,38 @@ static PrepareTransformResult PrepareTransformPair(Transform& t0, Transform& t1,
 		std::vector<size_t> matching_indices; // Indices into 'big' for matching types
 		std::vector<size_t> matching_indices; // Indices into 'big' for matching types
 		matching_indices.reserve(small.size() + 1);
 		matching_indices.reserve(small.size() + 1);
 
 
-		size_t big_index = 0;
+		size_t i_big = 0;
 		bool match_success = true;
 		bool match_success = true;
+		bool changed_big = false;
 
 
 		// Iterate through the small set to see if its types fit into the big set
 		// Iterate through the small set to see if its types fit into the big set
-		for (size_t i = 0; i < small.size(); i++)
+		for (size_t i_small = 0; i_small < small.size(); i_small++)
 		{
 		{
-			auto small_type = small[i].primitive.index();
 			match_success = false;
 			match_success = false;
+			auto small_type = small[i_small].primitive.index();
 
 
-			for (; big_index < big.size(); big_index++)
+			for (; i_big < big.size(); i_big++)
 			{
 			{
-				auto big_type = big[big_index].primitive.index();
+				auto big_type = big[i_big].primitive.index();
 
 
 				if (small_type == big_type)
 				if (small_type == big_type)
 				{
 				{
-					matching_indices.push_back(big_index);
+					// Exact match
+					match_success = true;
+				}
+				else if (Primitive::TryConvertToMatchingGenericType(small[i_small], big[i_big]))
+				{
+					// They matched in their more generic form, one or both primitives converted
+					match_success = true;
+					if (big[i_big].primitive.index() != big_type)
+						changed_big = true;
+				}
+
+				if (match_success)
+				{
+					matching_indices.push_back(i_big);
 					match_success = true;
 					match_success = true;
-					big_index += 1;
+					i_big += 1;
 					break;
 					break;
 				}
 				}
 			}
 			}
@@ -217,6 +235,11 @@ static PrepareTransformResult PrepareTransformPair(Transform& t0, Transform& t1,
 				i0 = match_index + 1;
 				i0 = match_index + 1;
 			}
 			}
 
 
+			// The small set has always been changed if we get here, but the big set is only changed
+			// if one or more of its primitives were converted to a general form.
+			if (changed_big)
+				return PrepareTransformResult::ChangedT0andT1;
+
 			return (prims0_smallest ? PrepareTransformResult::ChangedT0 : PrepareTransformResult::ChangedT1);
 			return (prims0_smallest ? PrepareTransformResult::ChangedT0 : PrepareTransformResult::ChangedT1);
 		}
 		}
 	}
 	}
@@ -263,17 +286,49 @@ static bool PrepareTransforms(std::vector<AnimationKey>& keys, Element& element)
 }
 }
 
 
 
 
+static bool TryMakeUnitValid(Variant& value)
+{
+	bool result = true;
 
 
-bool ElementAnimation::AddKey(float time, const Property & property, Element& element)
+	switch (value.GetType())
+	{
+	case Variant::FLOAT:
+	case Variant::COLOURB:
+	case Variant::TRANSFORMREF:
+		break;
+	default:
+	{
+		// Try to convert types to float so they can be interpolated
+		float f = 0.0f;
+		result = value.GetInto(f);
+		if (result) value.Reset(f);
+		break;
+	}
+	}
+	return result;
+}
+
+ElementAnimation::ElementAnimation(const String& property_name, const Property& current_value, float start_world_time, float duration, int num_iterations, bool alternate_direction)
+	: property_name(property_name), property_unit(current_value.unit), property_specificity(current_value.specificity),
+	duration(duration), num_iterations(num_iterations), alternate_direction(alternate_direction),
+	keys({ AnimationKey{0.0f, current_value.value, Tween{}} }),
+	last_update_world_time(start_world_time), time_since_iteration_start(0.0f), current_iteration(0), reverse_direction(false), animation_complete(false)
 {
 {
-	if (property.unit != property_unit)
-		return false;
+	valid = TryMakeUnitValid(keys.back().value);
+}
 
 
-	keys.push_back({ time, property.value });
+
+bool ElementAnimation::AddKey(float time, const Property & property, Element& element, Tween tween)
+{
+	if (property.unit != property_unit || !valid)
+		return false;
 
 
 	bool result = true;
 	bool result = true;
+	keys.push_back({ time, property.value, tween });
+
+	result = TryMakeUnitValid(keys.back().value);
 
 
-	if (property.unit == Property::TRANSFORM)
+	if (result && property.unit == Property::TRANSFORM)
 	{
 	{
 		for (auto& primitive : property.value.Get<TransformRef>()->GetPrimitives())
 		for (auto& primitive : property.value.Get<TransformRef>()->GetPrimitives())
 		{
 		{
@@ -291,19 +346,18 @@ bool ElementAnimation::AddKey(float time, const Property & property, Element& el
 	return result;
 	return result;
 }
 }
 
 
-Property ElementAnimation::UpdateAndGetProperty(float time)
+Property ElementAnimation::UpdateAndGetProperty(float world_time)
 {
 {
 	Property result;
 	Property result;
 
 
-
 	//Log::Message(Log::LT_INFO, "Animation it = %d,  t_it = %f, rev = %d,  dt = %f", current_iteration, time_since_iteration_start, (int)reverse_direction, time - last_update_time);
 	//Log::Message(Log::LT_INFO, "Animation it = %d,  t_it = %f, rev = %d,  dt = %f", current_iteration, time_since_iteration_start, (int)reverse_direction, time - last_update_time);
 
 
-	if (animation_complete || time - last_update_time <= 0.0f)
+	if (animation_complete || !valid || world_time - last_update_world_time <= 0.0f)
 		return result;
 		return result;
 
 
-	const float dt = time - last_update_time;
+	const float dt = world_time - last_update_world_time;
 
 
-	last_update_time = time;
+	last_update_world_time = world_time;
 	time_since_iteration_start += dt;
 	time_since_iteration_start += dt;
 
 
 	if (time_since_iteration_start >= duration)
 	if (time_since_iteration_start >= duration)
@@ -361,7 +415,7 @@ Property ElementAnimation::UpdateAndGetProperty(float time)
 			alpha = (t - t0) / (t1 - t0);
 			alpha = (t - t0) / (t1 - t0);
 	}
 	}
 
 
-	alpha = tween(alpha);
+	alpha = keys[key1].tween(alpha);
 
 
 	result.unit = property_unit;
 	result.unit = property_unit;
 	result.specificity = property_specificity;
 	result.specificity = property_specificity;

+ 7 - 10
Source/Core/ElementAnimation.h

@@ -39,6 +39,7 @@ namespace Core {
 struct AnimationKey {
 struct AnimationKey {
 	float time;
 	float time;
 	Variant value;
 	Variant value;
+	Tween tween;  // Tweening between the previous and this key. Ignored for the first animation key.
 };
 };
 
 
 
 
@@ -52,27 +53,22 @@ private:
 	float duration;           // for a single iteration
 	float duration;           // for a single iteration
 	int num_iterations;       // -1 for infinity
 	int num_iterations;       // -1 for infinity
 	bool alternate_direction; // between iterations
 	bool alternate_direction; // between iterations
-	Tween tween;              // tweening for a single iteration
 
 
 	std::vector<AnimationKey> keys;
 	std::vector<AnimationKey> keys;
 
 
-	float last_update_time;
+	float last_update_world_time;
 	float time_since_iteration_start;
 	float time_since_iteration_start;
 	int current_iteration;
 	int current_iteration;
-	bool reverse_direction;  // if true, run time backwards
+	bool reverse_direction;
 
 
 	bool animation_complete;
 	bool animation_complete;
+	bool valid;
 
 
 public:
 public:
 
 
-	ElementAnimation(const String& property_name, const Property& current_value, float time, float duration, int num_iterations, bool alternate_direction, Tween tween) 
-		: property_name(property_name), property_unit(current_value.unit), property_specificity(current_value.specificity), tween(tween),
-		duration(duration), num_iterations(num_iterations), alternate_direction(alternate_direction), 
-		keys({ AnimationKey{0.0f, current_value.value} }),
-		last_update_time(time), time_since_iteration_start(0.0f), current_iteration(0), reverse_direction(false), animation_complete(false) 
-	{}
+	ElementAnimation(const String& property_name, const Property& current_value, float start_world_time, float duration, int num_iterations, bool alternate_direction);
 
 
-	bool AddKey(float time, const Property& property, Element& element);
+	bool AddKey(float time, const Property& property, Element& element, Tween tween);
 
 
 	Property UpdateAndGetProperty(float time);
 	Property UpdateAndGetProperty(float time);
 
 
@@ -80,6 +76,7 @@ public:
 	float GetDuration() const { return duration; }
 	float GetDuration() const { return duration; }
 	void SetDuration(float duration) { this->duration = duration; }
 	void SetDuration(float duration) { this->duration = duration; }
 	bool IsComplete() const { return animation_complete; }
 	bool IsComplete() const { return animation_complete; }
+	bool IsValid() const { return valid; }
 };
 };
 
 
 
 

+ 2 - 2
Source/Core/ElementStyle.h

@@ -107,14 +107,14 @@ public:
 	const Property* GetLocalProperty(const String& name);
 	const Property* GetLocalProperty(const String& name);
 	/// Resolves one of this element's properties. If the value is a number or px, this is returned. If it's a 
 	/// Resolves one of this element's properties. If the value is a number or px, this is returned. If it's a 
 	/// percentage then it is resolved based on the second argument (the base value).
 	/// percentage then it is resolved based on the second argument (the base value).
-	/// If it's an angle, it is returned as degrees.
+	/// If it's an angle, it is returned as radians.
 	/// @param[in] property Property to resolve the value for.
 	/// @param[in] property Property to resolve the value for.
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @return The value of this property for this element.
 	/// @return The value of this property for this element.
 	float ResolveProperty(const Property *property, float base_value);
 	float ResolveProperty(const Property *property, float base_value);
 	/// Resolves one of this element's properties. If the value is a number or px, this is returned. If it's a 
 	/// Resolves one of this element's properties. If the value is a number or px, this is returned. If it's a 
 	/// percentage then it is resolved based on the second argument (the base value).
 	/// percentage then it is resolved based on the second argument (the base value).
-	/// If it's an angle, it is returned as degrees.
+	/// If it's an angle, it is returned as radians.
 	/// @param[in] name The name of the property to resolve the value for.
 	/// @param[in] name The name of the property to resolve the value for.
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @return The value of this property for this element.
 	/// @return The value of this property for this element.

+ 2 - 4
Source/Core/Event.cpp

@@ -138,10 +138,8 @@ void Event::ProjectMouse(Element* element)
 		);
 		);
 		Vector2f mouse = element->Project(old_mouse);
 		Vector2f mouse = element->Project(old_mouse);
 
 
-		//mouse_x->Set(int(mouse.x + 0.5f));
-		//mouse_y->Set(int(mouse.y + 0.5f));
-		mouse_x->Set(mouse.x);
-		mouse_y->Set(mouse.y);
+		mouse_x->Reset(mouse.x);
+		mouse_y->Reset(mouse.y);
 	}
 	}
 	else
 	else
 	{
 	{

+ 1 - 1
Source/Core/PropertyDefinition.cpp

@@ -73,7 +73,7 @@ PropertyDefinition& PropertyDefinition::AddParser(const String& parser_name, con
 		String unparsed_value = default_value.value.Get< String >();
 		String unparsed_value = default_value.value.Get< String >();
 		if (!new_parser.parser->ParseValue(default_value, unparsed_value, new_parser.parameters))
 		if (!new_parser.parser->ParseValue(default_value, unparsed_value, new_parser.parameters))
 		{
 		{
-			default_value.value.Set(unparsed_value);
+			default_value.value.Reset(unparsed_value);
 			default_value.unit = Property::UNKNOWN;
 			default_value.unit = Property::UNKNOWN;
 		}
 		}
 	}
 	}

+ 11 - 0
Source/Core/Transform.cpp

@@ -29,6 +29,7 @@
 #include "../../Include/Rocket/Core/Transform.h"
 #include "../../Include/Rocket/Core/Transform.h"
 #include "../../Include/Rocket/Core/TransformPrimitive.h"
 #include "../../Include/Rocket/Core/TransformPrimitive.h"
 #include "../../Include/Rocket/Core/ViewState.h"
 #include "../../Include/Rocket/Core/ViewState.h"
+#include "../../Include/Rocket/Core/Property.h"
 
 
 namespace Rocket {
 namespace Rocket {
 namespace Core {
 namespace Core {
@@ -39,6 +40,16 @@ Transform::Transform()
 {
 {
 }
 }
 
 
+Transform::Transform(std::vector<Transforms::Primitive> primitives) 
+	: primitives(primitives)
+{
+}
+
+Property Transform::MakeProperty(std::vector<Transforms::Primitive> primitives)
+{
+	return Property{ TransformRef{new Transform{primitives}}, Property::TRANSFORM };
+}
+
 Transform::Transform(const Transform& other)
 Transform::Transform(const Transform& other)
 {
 {
 	primitives = other.primitives;
 	primitives = other.primitives;

+ 65 - 1
Source/Core/TransformPrimitive.cpp

@@ -28,6 +28,7 @@
 #include "precompiled.h"
 #include "precompiled.h"
 #include "../../Include/Rocket/Core/TransformPrimitive.h"
 #include "../../Include/Rocket/Core/TransformPrimitive.h"
 #include <iostream>
 #include <iostream>
+#include <unordered_map>
 
 
 namespace Rocket {
 namespace Rocket {
 namespace Core {
 namespace Core {
@@ -348,9 +349,15 @@ struct InterpolateVisitor
 		for (size_t i = 0; i < N; i++)
 		for (size_t i = 0; i < N; i++)
 			p0.values[i].number = p0.values[i].number*(1.0f - alpha) + p1.values[i].number * alpha;
 			p0.values[i].number = p0.values[i].number*(1.0f - alpha) + p1.values[i].number * alpha;
 	}
 	}
-	//void Interpolate(Matrix3D& p0, Matrix3D& p1)
+	void Interpolate(Rotate3D& p0, const Rotate3D& p1)
+	{
+		// Assumes that the underlying direction vectors are normalized and equivalent (else, need to do full matrix interpolation)
+		p0.values[4] = p0.values[4] * (1.0f - alpha) + p1.values[4] * alpha;
+	}
+	//void Interpolate(Matrix3D& p0, const Matrix3D& p1)
 	//{
 	//{
 	//	// Special interpolation for full matrices TODO
 	//	// Special interpolation for full matrices TODO
+	//  // Also, Matrix2d, Perspective, and conditionally Rotate3d get interpolated in this way
 	//}
 	//}
 
 
 	template <typename T>
 	template <typename T>
@@ -361,6 +368,7 @@ struct InterpolateVisitor
 	}
 	}
 };
 };
 
 
+
 bool Primitive::InterpolateWith(const Primitive & other, float alpha) noexcept
 bool Primitive::InterpolateWith(const Primitive & other, float alpha) noexcept
 {
 {
 	if (primitive.index() != other.primitive.index())
 	if (primitive.index() != other.primitive.index())
@@ -372,6 +380,62 @@ bool Primitive::InterpolateWith(const Primitive & other, float alpha) noexcept
 }
 }
 
 
 
 
+
+enum class GenericType { None, Scale3D, Translate3D };
+
+struct GetGenericTypeVisitor
+{
+	GenericType common_type = GenericType::None;
+
+	GenericType operator()(const TranslateX& p) { return GenericType::Translate3D; }
+	GenericType operator()(const TranslateY& p) { return GenericType::Translate3D; }
+	GenericType operator()(const TranslateZ& p) { return GenericType::Translate3D; }
+	GenericType operator()(const Translate2D& p) { return GenericType::Translate3D; }
+	GenericType operator()(const ScaleX& p) { return GenericType::Scale3D; }
+	GenericType operator()(const ScaleY& p) { return GenericType::Scale3D; }
+	GenericType operator()(const ScaleZ& p) { return GenericType::Scale3D; }
+	GenericType operator()(const Scale2D& p) { return GenericType::Scale3D; }
+
+	template <typename T>
+	GenericType operator()(const T& p) { return GenericType::None; }
+};
+
+
+struct ConvertToGenericTypeVisitor
+{
+	PrimitiveVariant operator()(const TranslateX& p) { return Translate3D{ p.values[0], {0.0f, Property::PX}, {0.0f, Property::PX} }; }
+	PrimitiveVariant operator()(const TranslateY& p) { return Translate3D{ {0.0f, Property::PX}, p.values[0], {0.0f, Property::PX} }; }
+	PrimitiveVariant operator()(const TranslateZ& p) { return Translate3D{ {0.0f, Property::PX}, {0.0f, Property::PX}, p.values[0] }; }
+	PrimitiveVariant operator()(const Translate2D& p) { return Translate3D{ p.values[0], p.values[1], {0.0f, Property::PX} }; }
+	PrimitiveVariant operator()(const ScaleX& p) { return Scale3D{ p.values[0], 1.0f, 1.0f }; }
+	PrimitiveVariant operator()(const ScaleY& p) { return Scale3D{  1.0f, p.values[0], 1.0f }; }
+	PrimitiveVariant operator()(const ScaleZ& p) { return Scale3D{  1.0f, 1.0f, p.values[0] }; }
+	PrimitiveVariant operator()(const Scale2D& p) { return Scale3D{ p.values[0], p.values[1], 1.0f }; }
+
+	template <typename T>
+	PrimitiveVariant operator()(const T& p) { ROCKET_ERROR; return p; }
+};
+
+
+
+bool Primitive::TryConvertToMatchingGenericType(Primitive & p0, Primitive & p1) noexcept
+{
+	if (p0.primitive.index() == p1.primitive.index())
+		return true;
+
+	GenericType c0 = std::visit(GetGenericTypeVisitor{}, p0.primitive);
+	GenericType c1 = std::visit(GetGenericTypeVisitor{}, p1.primitive);
+
+	if (c0 == c1 && c0 != GenericType::None)
+	{
+		p0.primitive = std::visit(ConvertToGenericTypeVisitor{}, p0.primitive);
+		p1.primitive = std::visit(ConvertToGenericTypeVisitor{}, p1.primitive);
+		return true;
+	}
+
+	return false;
+}
+
 struct ResolveUnitsVisitor
 struct ResolveUnitsVisitor
 {
 {
 	Element& e;
 	Element& e;

+ 1 - 4
Source/Core/Variant.cpp

@@ -76,10 +76,7 @@ void Variant::Clear()
 	type = NONE;
 	type = NONE;
 }
 }
 
 
-Variant::Type Variant::GetType() const
-{
-	return type;
-}
+
 
 
 //////////////////////////////////////////////////
 //////////////////////////////////////////////////
 // Set methods
 // Set methods