2009-10-08 19 views
7

Estoy buscando una forma de implementar la funcionalidad Swizzle encontrada en HLSL en C#, para aquellos que no están familiarizados con esto, se usa para acceder fácilmente a elementos vectoriales.HLSL Swizzle - en C#

Vector4 v1, v2; 
// (t,x,y,z) or (alpha,r,g,b) 
v1 = new Vector4 (1,2,0,0); 
v2 = new Vector4 (0,0,3,4); 

// v1 (Green, Z) = v2 (Y, Blue) 
v1.gz = v2.yb; 
// Result : v1 = (1,2,3,4) 

Se podrían crear MUCHAS propiedades (una para cada combinación posible). Tengo la sensación de que podría ser posible hacerlo a través de Linq, pero en realidad no tengo mucha experiencia con él.

No sé si XNA tiene algo así como el tipo, pero no quiero ir por ese camino, ya que esto es todo lo que usaría, es decir, si lo tiene.

Gracias.

+0

Por favor, vea la edición en mi respuesta, he incluido una solución de trabajo real a este problema, pero sí requiere de C# 4.0 –

Respuesta

8

En C# 3.5 y anteriores, su mejor opción es simplemente usar un montón de propiedades.

En C# 4.0, sin embargo, puede usar la tipificación dynamic y la subclase DynamicObject para obtener la funcionalidad que está buscando. Esta puede o no ser una mejor opción, no sé mucho sobre esta funcionalidad.

EDIT:

estaba tan intrigado por la pregunta, que fui e implementado una solución de C# 4.0 a esto. El código sigue, pero también puede download a full solution if that's more your thing [github]. Como siempre, puedes usar/romper/hacer este código como quieras, solo que no me culpes si borra tu disco duro.

Datos 3: Esta es la última edición Voy a hacer aquí, pero probablemente voy a jugar con esto más, y voy a mantener la versión ligado al día para cualquier persona que viene por aquí más tarde.

EDIT 2:

Antes de que te deje al código, hay ejemplos de lo que será y no funcionará en Program.Main() en el propio código.

Y ahora, en el código.

namespace Swizzle 
{ 
    /// <summary> 
    /// This implements the Vector4 class as described in the question, based on our totally generic 
    /// Vector class. 
    /// </summary> 
    class Vector4 : Vector<int> 
    { 
     public Vector4(int val0, int val1, int val2, int val3) 
      : base(new Dictionary<char, int> { {'t', 0}, {'x', 1}, {'y', 2}, {'z', 3}, 
               {'a', 0}, {'r', 1}, {'g', 2}, {'b', 3}}, 
        new int[] { val0, val1, val2, val3 }) 
     { } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      dynamic v1, v2, v3; 

      v1 = new Vector4(1, 2, 3, 4); 

      v2 = v1.rgb; 
      // Prints: v2.r: 2 
      Console.WriteLine("v2.r: {0}", v2.r); 
      // Prints: red: 2 
      int red = v2.r; 
      Console.WriteLine("red: {0}", red); 
      // Prints: v2 has 3 elements. 
      Console.WriteLine("v2 has {0} elements.", v2.Length); 

      v3 = new Vector4(5, 6, 7, 8); 
      v3.ar = v2.gb; // yes, the names are preserved! v3 = (3, 4, 7, 8) 

      v2.r = 5; 
      //v2.a = 5; // fails: v2 has no 'a' element, only 'r', 'g', and 'b' 

      // Something fun that will also work 
      Console.WriteLine("v3.gr: {0}", v3.gr); 
      v3.rg = v3.gr; // switch green and red 
      Console.WriteLine("v3.gr: {0}", v3.gr); 

      Console.WriteLine("\r\nPress any key to continue."); 
      Console.ReadKey(true); 
     } 
    } 

    class Vector<T> : DynamicObject 
    { 
     private T[] m_values; 
     private Dictionary<char, int> m_positions; 

     public Vector(Dictionary<char, int> positions, params T[] values) 
     { 
      this.m_positions = positions; 
      this.m_values = values; 
     } 

     public T this[int index] { 
      get { return this.m_values[index]; } 
     } 

     public int Length 
     { 
      get { return this.m_values.Length; } 
     } 

     public override string ToString() 
     { 
      List<string> elements = new List<string>(this.Length); 

      for (int i = 0; i < this.Length; i++) 
      { 
       elements.Add(m_values[i].ToString()); 
      } 

      return string.Join(", ", elements.ToArray()); 
     } 

     public override bool TryGetMember(GetMemberBinder binder, out object result) 
     { 
      if (binder.Name == "Length") { 
       result = this.Length; 
       return true; 
      } 

      if (binder.Name.Length == 1 && this.m_positions.ContainsKey(binder.Name[0])) 
      { 
       result = m_values[this.m_positions[binder.Name[0]]]; 
       return true; 
      } 

      Dictionary<char, int> positions = new Dictionary<char, int>(binder.Name.Length); 
      List<T> values = new List<T>(binder.Name.Length); 
      int i = 0; 
      foreach (char c in binder.Name) 
      { 
       if (!this.m_positions.ContainsKey(c)) 
        return base.TryGetMember(binder, out result); 

       values.Add(m_values[m_positions[c]]); 
       positions.Add(c, i); 

       i++; 
      } 

      result = new Vector<T>(positions, values.ToArray()); 
      return true; 
     } 

     public override bool TrySetMember(SetMemberBinder binder, object value) 
     { 
      // sanity checking. 
      foreach (char c in binder.Name) 
      { 
       if (!this.m_positions.ContainsKey(c)) 
        return base.TrySetMember(binder, value); 
      } 

      Vector<T> vectorValue = value as Vector<T>; 

      if (vectorValue == null && binder.Name.Length == 1 && value is T) 
      { 
       m_values[m_positions[binder.Name[0]]] = (T)value; 
       return true; 
      } 
      else if (vectorValue == null) 
       throw new ArgumentException("You may only set properties of a Vector to another Vector of the same type."); 
      if (vectorValue.Length != binder.Name.Length) 
       throw new ArgumentOutOfRangeException("The length of the Vector given does not match the length of the Vector to assign it to."); 

      int i = 0; 
      foreach (char c in binder.Name) 
      { 
       m_values[m_positions[c]] = vectorValue[i]; 
       i++; 
      } 

      return true; 
     } 
    } 
} 
+0

Siempre se puede generalizar aún más y hacer un 'vector ', pero yo estaba feliz de tener los fundamentos funcionando bien. Esto en realidad parece bastante útil, por lo que es útil para mencionarlo. –

+0

Saludos, Definitivamente estaré investigando eso (todavía no he tenido nada que ver con los tipos dinámicos). La otra cosa que es posible usando la implementación de HLSL es asignar vectores de diferentes tamaños (vec2 = vec4.xy) etc. Creo que con un poco de reproducción también debería ser posible. –

+0

Mi código devuelve una clase Vector que es un subconjunto del vector original cuando accede a él. Editaré en algunos ejemplos. –