2009-05-22 9 views
5
  • Todos los puntos son vectores, y todos los vectores son puntos.
  • Todas las direcciones son vectores, NO todos los vectores son direcciones (esto no debería significar que ambas formas de conversión no deberían permitirse).

Quiero tener los operadores anulados de una vez por todas, de preferencia, ya que son todos completamente idénticos. En C++ puedo simplemente definir la clase Vector {float x, y, z; }, y escribe typedef Point = Vector, typedef Direction = Vector; En C# no hay equivalente ("usando Point = Vector;" apesta ya que tiene que colocarlo en cada uno de los documentos que usa, y el compilador no lo aplica).La mejor forma de implementar estas 3 clases en C#: Vector, Dirección (unidad de vector), Punto

Me trataron de definir diferentes clases 3 y anular los operadores para cada uno, y luego hacer la colada tipo implícito que haría que el código se ejecute más lento, etc.

intenté definir simplemente vectorial, entonces Point: Vector y dirección: Vector, de esta manera solo escribo los operadores una vez pero luego no puedo hacer el lanzamiento de tipo implícito Punto < -> Vector o Dirección < -> Vector.

Podría simplemente definir la clase Vector y usar eso en todas partes, pero eso crearía ambigüedad en cuanto a clima una variable se supone que es una posición en el espacio (Punto), una posición relativa en el espacio (Vector) o un vector unitario (Dirección). Por ejemplo, la función:

Vector GetOrthogon(Vector a, Vector b) { 
    // .... 
} 

No se puede saber si se esperan vectores o vectores unitarios. En C++ puedes hacer eso, ¿por qué no en C#?

Nota: tener structs en lugar de clases sería ideal si es posible.

+0

Si son * exactamente * el mismo código, ¿por qué no una clase con un nombre más genérico? ¿Estás usando información en tiempo de ejecución para obtener el tipo que es? – crashmstr

Respuesta

10

Matemáticamente, los puntos son vectores. No hay puntos absolutos en el espacio. Los puntos se definen como vectores de algún origen arbitrario. Entonces, uso Vectores para ambos puntos y diferencias entre puntos.

Como una dirección es un vector unitario, tampoco es necesario hacer una distinción. Es como tratar de definir diferentes tipos estáticos para el entero 1 y otros enteros. Entonces uso vectores para ambas direcciones y diferencias entre puntos.

Por lo tanto, defina un tipo de Vector único. Te facilitará la vida porque tendrás menos clases y operadores/funciones sobrecargados para escribir y probar, y será matemáticamente "más puro" (si eso te importa).

1

Si los vectores son puntos y los puntos son vectores, entonces tal vez deberían ser de la misma clase (o el mismo tipo de estructura).

O, si usted quiere que ...

  • tienen las mismas implementaciones de los mismos métodos y operadores
  • tienen diferentes nombres y diferentes tipos

... a continuación, definir los métodos y operadores en una clase base con un constructor protegido, y definen Punto y Vector como subclases de hoja de esta clase base para que hereden su funcionalidad.

Si no puede usar la herencia, etc. para hacer exactamente lo que desea, copie y pegue las definiciones de operador en las varias clases, porque a) No es un problema de mantenimiento porque es poco probable que cambien b) Usted dice que tiene una razón válida, es decir, "estoy haciendo un raytracer, así que la velocidad es crítica".

Se podría decir que no importa, ya que todos son vectores, pero en C++ se puede tener fácilmente la distinción, ¿por qué no C?

También puede distinguir en C#: C# admite la definición de distintos tipos, etc.Dos cosas que puede hacer en C++ que no puede hacer en C# son plantillas de pato y funciones globales, que pueden ser útiles aquí, pero que no son esenciales, si está dispuesto a utilizar la herencia o a copiar-y- pegar el funcional común.

+0

Sí, pero entonces no sabría lo que una cierta propiedad podría significar. Por ejemplo: clase Plane {Vector Position; Vector Normal; Desplazamiento del vector; }. ¿Es normal un vector normalizado? ¿Se supone que Displace es un punto o un vector, y si es un vector, está normalizado? Podrías decir que no importa ya que todos son vectores, pero en C++ fácilmente podrías tener la distinción, ¿por qué no C? – manixrock

+0

Editado para responder a su comentario – ChrisW

8

En un nivel purista, yo diría que un Vector y una Point son no la misma, mediante el álgebra:

  • Point + Vector =>Point (traducción)
  • Vector + Vector =>Vector (adición)
  • Point + Point (no definido)

Tendría 2 estructuras inmutables con operadores de conversión implícitos (donde son lógicamente equivalentes) o explícitos (de lo contrario). Lo que probablemente no tienen una estructura Direction, aunque ... que podría tener una propiedad en un DirectionVector que se adapta a la unidad, pero eso es todo ...


He apagó una primitiva Vector y Point para mostrar las interacciones; No he rellenado todos Point (ya que es más sencillo):

public struct Point { 
    public Point(double x, double y) : this() { X = x; Y = y; } 
    public double X { get; private set; } 
    public double Y { get; private set; } 

    // and more ;-p 
} 

public struct Vector : IEquatable<Vector> { 
    public Vector(double x, double y) : this() { X = x; Y = y; } 
    public Vector AsUnit() { 
     if (X == 0 && Y == 0) throw new InvalidOperationException(); 
     if (X == 0) return new Vector(0, X > 0 ? 1 : -1); 
     if (Y == 0) return new Vector(Y > 0 ? 1 : -1, 0); 
     double sqr = Math.Sqrt((X * X) + (Y * Y)); 
     return new Vector(X/sqr, Y/sqr); 
    } 
    public double X { get; private set; } 
    public double Y { get; private set; } 

    public static explicit operator Point(Vector vector) { 
     return new Point(vector.X, vector.Y); 
    } 
    public static explicit operator Vector(Point point) { 
     return new Vector(point.X, point.Y); 
    } 
    public override string ToString() { 
     return "(" + X.ToString() + "," + Y.ToString() + ")"; 
    } 
    public override int GetHashCode() { 
     return 17 * X.GetHashCode() + Y.GetHashCode(); 
    } 
    public override bool Equals(object obj) { 
     return obj == null ? false : Equals((Vector)obj); 
    } 
    public bool Equals(Vector vector) { 
     return X == vector.X && Y == vector.Y; 
    } 
    public static bool operator ==(Vector a, Vector b) { 
     return a.X == b.X && a.Y == b.Y; 
    } 
    public static bool operator !=(Vector a, Vector b) { 
     return a.X != b.X || a.Y != b.Y; 
    } 
    public static Point operator +(Point point, Vector vector) { 
     return new Point(point.X + vector.X, point.Y + vector.Y); 
    } 
    public static Point operator -(Point point, Vector vector) { 
     return new Point(point.X - vector.X, point.Y - vector.Y); 
    } 
    public static Vector operator +(Vector a, Vector b) { 
     return new Vector(a.X + b.X, a.Y + b.Y); 
    } 
    public static Vector operator -(Vector a, Vector b) { 
     return new Vector(a.X - b.X, a.Y - b.Y); 
    } 
} 
+0

Tiene razón (sobre todo) en que no tiene sentido agregar 2 puntos, pero todas las otras operaciones que puede hacer en Vector y Vector también deberían poder hacerlo en Vector & Point o Punto y Vector. Podrías decir convertirlos a Vector y eso funcionaría pero sería mucho más lento. Estoy haciendo un raytracer así que la velocidad es crítica. – manixrock

6

Ya hay Point y Vector implementaciones en .Net> = 3.0. Desafortunadamente están en System.Windows.Media.Media3D ya que son parte de la API 3D de WPF.

Estas clases existentes tienen algunas propiedades útiles que los hacen dignos de estudio (algunos ya han mencionado otros):

  • Point [+|-] Vector = Point
  • Vector [+|-] Vector = Vector
  • Point - Point = Vector
  • Vector tiene métodos estáticos para realizar dot y productos cruzados
  • Vector.Normalise() y vector.Negate() hacer lo que dicen en la lata

¿Quizás podría examinar (e incluso ampliar) estos para su propio uso?

+0

Eso es más o menos lo que quiero hacer, excepto que no quiero usar las funciones Quiero usar operadores, entonces puedo hacerlo: Vector proj = dir * (dir * normal); Vector reflejado = dir - (n + n); – manixrock

3

Recomiendo contra tener tanto un Vector y un tipo Point. Aunque matemáticamente son un poco diferentes, esa distinción generalmente no proporciona ninguna ventaja en un contexto de programación. Además, puede considerar un punto como un vector desde el origen hasta la ubicación del punto.

Recomiendo usar tipos de valores en lugar de tipos de referencia, ya que normalmente usa vectores como lo haría con números reales, por lo que querría la misma semántica que un flotante (paso por valor). Si elige tipos de valor, recuerde que a menudo es mejor hacerlos inmutables.

También desaconsejo usar un tipo Direction, y prefiero usar vectores normalizados. En lugar de sus tres tipos de Vector, Point y Direction, solo Vector debería ser suficiente (si es lo suficientemente genérico).

En cuanto a la referencia de implementación, puede echar un vistazo a XNA's Vector3 structure.

1

Sugiero definirlo como tal:

public abstract class VectorBase { 
    public int X { get; private set; } 
    public int Y { get; private set; } 

    public VectorBase(int x, int y) { 
     this.X = x; 
     this.Y = y; 
    } 

    public VectorBase(VectorBase copy) : this(copy.X, copy.Y) { 
     // creates a vector with the same x, y 
    } 

    public static Vector operator +(VectorBase left, VectorBase right) { 
     return new Vector(left.X + right.X, left.Y + right.Y); 
    } 

    public static Vector operator -(VectorBase left, VectorBase right) { 
     return new Vector(left.X - right.X, left.Y - right.Y); 
    } 
} 

public class Vector : VectorBase { 
    public Vector(VectorBase v) : base(v) { } 
    public Vector(int x, int y) : base(x, y) { } 
} 

public class Point : VectorBase { 
    public Point(VectorBase v) : base(v) { } 
    public Point(int x, int y) : base(x, y) { } 

    public static implicit operator Vector(Point p) { 
     return new Vector(p); 
    } 

    public static implicit operator Point(Vector v) { 
     return new Point(v); 
    } 
} 

public class Direction : VectorBase { 
    public Direction(VectorBase v) : base(v) { } 
    public Direction(int x, int y) : base(x, y) { } 

    public static implicit operator Vector(Direction d) { 
     return new Vector(d); 
    } 

    public static implicit operator Direction(Vector v) { 
     return new Direction(v); 
    } 

    // implementation of *, any other stuff you need 
} 

A tener en cuenta:

  • Esta aplicación es immutable.
  • Cualquier tipo - Vector, Point o Direction se puede sumar/restar de cualquier otro dando como resultado Vector.
  • A Direction se puede multiplicar por un vector.
  • Son implícitamente intercambiables; puede cambiar fácilmente eso a explícitamente intercambiable cambiando el implicit operator a explicit operator s.