Estoy definiendo las estructuras que almacenan los datos usados para interpolar valores...
he usado genéricos por reusar código y mantener una misma lógica para todo... pero como no me resuelve el tipo genérico hasta que lo decalro, tengo que copiar el mismo codigo una y otra vez en las clases finales...
Ver InterpolatorDataColor, InterpolatorDataVector2, InterpolatorDataFloat, ... los metodos LoadKey y SaveKey son identicos...
la pregunta clave es ¿Hay alguna manera sencilla de subir estos dos métodos a la clase padre genérica?
Bueno ahí va un tocho de código...
namespace Engine.Interpolators
{
public enum InterpolatorDataTypes { Float, Color, Vector2 }
public enum InterpolationTypes { None, Linear, Bezier, Hermite }
//------------------------------------------------------------------------------------------------------------
public class KeyData<T> where T : struct
{
public float Time;
public T Value;
public T InTan;
public T OutTan;
}
//------------------------------------------------------------------------------------------------------------
public abstract class InterpolatorData : Engine.Core.Data
{
public abstract InterpolatorDataTypes InterpolatorDataType { get; }
public InterpolationTypes InterpolationType;
protected override void OnSave( XmlWriter writer )
{
writer.WriteElementString( "InterpolationType", InterpolationType.ToString() );
}
protected override void OnLoad( XmlReader reader )
{
InterpolationType = (InterpolationTypes) Enum.Parse( typeof(InterpolationTypes), reader.ReadElementString( "InterpolationType" ) );
}
}
//------------------------------------------------------------------------------------------------------------
public abstract class InterpolatorData<T> : InterpolatorData where T : struct
{
public List<KeyData<T>> Keys = new List<KeyData<T>>( );
protected override void OnSave( XmlWriter writer )
{
base.OnSave( writer );
writer.WriteStartElement( "Keys" );
writer.WriteAttributeString( "Count", Keys.Count.ToString( CultureInfo.InvariantCulture ) );
{
for ( int index = 0; index < Keys.Count; index++ )
SaveKey( index, writer );
}
writer.WriteEndElement( );
}
protected override void OnLoad( XmlReader reader )
{
int Count;
base.OnLoad( reader );
reader.ReadToFollowing( "Keys" );
reader.GetAttribute( "Count", out Count );
reader.ReadStartElement( "Keys" );
Keys = new KeyData<T>[ Count ].ToList();
for ( int index=0; index < Count; index++ )
{
Keys[index] = new KeyData<T>( );
LoadKey( index, reader );
}
}
protected abstract void SaveKey( int index, XmlWriter writer );
protected abstract void LoadKey( int index, XmlReader reader );
}
//------------------------------------------------------------------------------------------------------------
public class InterpolatorDataColor : InterpolatorData<Microsoft.Xna.Framework.Color>
{
public override InterpolatorDataTypes InterpolatorDataType { get { return InterpolatorDataTypes.Color; } }
protected override void SaveKey(int index, XmlWriter writer)
{
writer.WriteStartElement( "Key" );
{
writer.WriteElementString( "Time", Keys[index].Time );
writer.WriteElementString( "Value", Keys[index].Value );
if ( InterpolationType == InterpolationTypes.Bezier || InterpolationType == InterpolationTypes.Hermite )
{
writer.WriteElementString( "InTan", Keys[index].InTan );
writer.WriteElementString( "OutTan", Keys[index].OutTan );
}
}
writer.WriteEndElement( );
}
protected override void LoadKey( int index, XmlReader reader )
{
reader.ReadStartElement( "Key" );
{
reader.ReadElementString( "Time", out Keys[index].Time );
reader.ReadElementString( "Value", out Keys[index].Value );
if ( InterpolationType == InterpolationTypes.Bezier || InterpolationType == InterpolationTypes.Hermite )
{
reader.ReadElementString( "InTan", out Keys[index].InTan );
reader.ReadElementString( "OutTan", out Keys[index].OutTan );
}
}
reader.ReadEndElement( );
}
}
//------------------------------------------------------------------------------------------------------------
public class InterpolatorDataFloat : InterpolatorData<Single>
{
public override InterpolatorDataTypes InterpolatorDataType { get { return InterpolatorDataTypes.Float; } }
protected override void SaveKey( int index, XmlWriter writer )
{
writer.WriteStartElement( "Key" );
{
writer.WriteElementString( "Time", Keys[index].Time );
writer.WriteElementString( "Value", Keys[index].Value );
if ( InterpolationType == InterpolationTypes.Bezier || InterpolationType == InterpolationTypes.Hermite )
{
writer.WriteElementString( "InTan", Keys[index].InTan );
writer.WriteElementString( "OutTan", Keys[index].OutTan );
}
}
writer.WriteEndElement( );
}
protected override void LoadKey( int index, XmlReader reader )
{
reader.ReadStartElement( "Key" );
{
reader.ReadElementString( "Time", out Keys[index].Time );
reader.ReadElementString( "Value", out Keys[index].Value );
if ( InterpolationType == InterpolationTypes.Bezier || InterpolationType == InterpolationTypes.Hermite )
{
reader.ReadElementString( "InTan", out Keys[index].InTan );
reader.ReadElementString( "OutTan", out Keys[index].OutTan );
}
}
reader.ReadEndElement( );
}
}
//------------------------------------------------------------------------------------------------------------
public class InterpolatorDataVector2 : InterpolatorData<Microsoft.Xna.Framework.Vector2>
{
public override InterpolatorDataTypes InterpolatorDataType { get { return InterpolatorDataTypes.Vector2; } }
protected override void SaveKey( int index, XmlWriter writer )
{
writer.WriteStartElement( "Key" );
{
writer.WriteElementString( "Time", Keys[index].Time );
writer.WriteElementString( "Value", Keys[index].Value );
if ( InterpolationType == InterpolationTypes.Bezier || InterpolationType == InterpolationTypes.Hermite )
{
writer.WriteElementString( "InTan", Keys[index].InTan );
writer.WriteElementString( "OutTan", Keys[index].OutTan );
}
}
writer.WriteEndElement( );
}
protected override void LoadKey( int index, XmlReader reader )
{
reader.ReadStartElement( "Key" );
{
reader.ReadElementString( "Time", out Keys[index].Time );
reader.ReadElementString( "Value", out Keys[index].Value );
if ( InterpolationType == InterpolationTypes.Bezier || InterpolationType == InterpolationTypes.Hermite )
{
reader.ReadElementString( "InTan", out Keys[index].InTan );
reader.ReadElementString( "OutTan", out Keys[index].OutTan );
}
}
reader.ReadEndElement( );
}
}
}
Un saludo
Desgraciadamente para poderlo hacer generico el WriteElementString y el ReadElementString tendrian que soportar genericos.
Pero podrias hacer un poco de trampa lo mismo para el Write, en vez de:
writer.WriteElementString( "Value", Keys[index].Value );
Hacer:
writer.WriteElementString( "Value", Keys[index].Value.ToString() );
Tienes el constraint a Struct y las Struct tienen el metodo ToString(), asi que deberia colar...
Para el Read no se me ocurre nada :(
De hecho hago trampas.... :)
estoy usando extensiones del xmlreader y del xmlwriter...
public static class XmlExtension
{
public static void WriteElementString( this XmlWriter writer, string element, Color value )
{
writer.WriteElementString(
element,
value.R.ToString( CultureInfo.InvariantCulture ) + ',' +
value.G.ToString( CultureInfo.InvariantCulture ) + ',' +
value.B.ToString( CultureInfo.InvariantCulture ) + ',' +
value.A.ToString( CultureInfo.InvariantCulture ) );
}
public static void WriteElementString( this XmlWriter writer, string element, Vector2 value )
{
writer.WriteElementString( element, value.X.ToString( CultureInfo.InvariantCulture ) + ',' + value.Y.ToString( CultureInfo.InvariantCulture ) );
}
public static void WriteElementString( this XmlWriter writer, string element, int value )
{
writer.WriteElementString( element, value.ToString( CultureInfo.InvariantCulture ) );
}
public static void WriteElementString( this XmlWriter writer, string element, float value )
{
writer.WriteElementString( element, value.ToString( CultureInfo.InvariantCulture ) );
}
public static void ReadElementString( this XmlReader reader, string element, out Color value )
{
string data = reader.ReadElementString( element );
byte[] values = data.Split( ',' ).Select<string, byte>( s => byte.Parse( s, CultureInfo.InvariantCulture ) ).ToArray( );
value = new Color( values[0], values[1], values[2], values[3] );
}
public static void ReadElementString( this XmlReader reader, string element, out Vector2 value )
{
string data = reader.ReadElementString( element );
float[] values = data.Split( ',' ).Select<string, float>( s => float.Parse( s, CultureInfo.InvariantCulture ) ).ToArray( );
value.X = values[0];
value.Y = values[1];
}
public static void ReadElementString( this XmlReader reader, string element, out int value )
{
string s = reader.ReadElementString( element );
value = int.Parse( s, CultureInfo.InvariantCulture );
}
public static void ReadElementString( this XmlReader reader, string element, out float value )
{
string s = reader.ReadElementString( element );
value = float.Parse( s, CultureInfo.InvariantCulture );
}
public static void GetAttribute( this XmlReader reader, string attribute, out int value )
{
string data = reader.GetAttribute( attribute );
value = int.Parse( data, CultureInfo.InvariantCulture );
}
}
}
El tema es que podria hacer metodos genericos tambien... pero no se por que no me han gustado mucho, seria algo asi:
public static void ReadElementString<T>( this XmlReader reader, string element, out T value ) where T:struct, new()
{
if (T is Color) {
Color c;
ReadElementString(reader, element, out c);
value = (T)(object)c;
}
else ....
}
Si no recuerdo mal se hacia asi... ¿hay alguna otra idea?
Al final no se ha quedado feo del todo, lo he hecho asi:
public static void ReadElementString<T>( this XmlReader reader, string element, out T value ) where T : struct
{
string Data = reader.ReadElementString( element );
Type type = typeof( T );
if ( type == typeof( Color ) ) value = (T) (object) StringToColor( Data );
else if ( type == typeof( Vector2 ) ) value = ( T ) ( object ) StringToVector2( Data );
else if ( type == typeof( int ) ) value = ( T ) ( object ) StringToInt( Data );
else if ( type == typeof( float ) ) value = ( T ) ( object ) StringToFloat( Data );
else throw new NotSupportedException( );
}
public static void WriteElementString<T>( this XmlWriter writer, string element, T value ) where T:struct
{
Type type = typeof( T );
if ( type == typeof( Color ) ) WriteElementString( writer, element, ( Color ) ( object ) value );
else if ( type == typeof( Vector2 ) ) WriteElementString( writer, element, ( Vector2 ) ( object ) value );
else if ( type == typeof( int ) ) WriteElementString( writer, element, ( int ) ( object ) value );
else if ( type == typeof( float ) ) WriteElementString( writer, element, ( float ) ( object ) value );
else throw new NotSupportedException( );
}
public class KeyData<T> where T : struct
{
public float Time;
public T Value;
public T InTan;
public T OutTan;
public void Save(XmlWriter writer, bool FullSave)
{
writer.WriteStartElement( "Key" );
{
writer.WriteElementString( "Time", Time );
writer.WriteElementString<T>( "Value", Value );
if ( FullSave )
{
writer.WriteElementString( "InTan", InTan );
writer.WriteElementString( "OutTan", OutTan );
}
}
writer.WriteEndElement( );
}
public void Load( XmlReader reader, bool FullLoad)
{
reader.ReadStartElement( "Key" );
{
reader.ReadElementString( "Time", out Time );
reader.ReadElementString<T>( "Value", out Value );
if ( FullLoad )
{
reader.ReadElementString<T>( "InTan", out InTan );
reader.ReadElementString<T>( "OutTan", out OutTan );
}
}
reader.ReadEndElement( );
}
No queda mal no :)