2011-04-06 15 views
7

He estado haciendo muchas cosas de vectores y escribí mi propia plantilla para eso.C++ Vector Math y OpenGL compatible

Mis requisitos son un montón de matemáticas vectoriales (suma, resta, escala, cruce prod y punto prod) también tengo que ser capaz de pasar mi vector como un flotador [] para que openGL pueda hacer uso de él.

He estado usando esto muy felizmente por algún tiempo, hoy un conferencista lo vio y gimió. Había dos cosas que odiaba particularmente (una de las cuales entiendo), mi uso de la herencia porque no parecía seguir un estilo is a. Y mi casting (T*)this, por supuesto que no tenía mucho en el camino de una solución.

Primero: La herencia, tengo que ser capaz de hacer uso de vec2 a modo de vec4 he diseñado mis vectores de este tipo.

template<typename T> 
Vector2D 
{ 
public: 
getX(), getY(), setX(), setY() .... 
}; 

template<typename T> 
Vector3D : public Vector2D<T> 
{ 
public: 
getZ(), setZ() ... 
} 

template<typename T> 
Vector4D : public Vector3D<T> 
{ 
public: 
getW(), setW() ... 
} 

¿Por qué es esto malo? y tbh no puedo ver cómo mejorarlo. Necesito (quiero) poder definir el tipo y tener getters y setters de algún tipo. Si reacomodé ella como

template<typename T, int _size> 
VectorT 

me gustaría perder mi .getX(), .setX() cosas y tenga que reemplazarlo con algo como .at() o []. tbh Prefiero la legibilidad de .getX(), aunque haría más fáciles las definiciones del operador.

Segundo: Puedo ver por qué esto es malo, pero para que sea por lo que puede pasar estos vectores en el método de OpenGL que esperan una red de flotadores que he sobrecargado el operador splat

// Defined in Vector2D<T> 
operator*() { return (T*)this; } 

Como Lo entiendo, no hay garantía de que el compilador ponga las variables miembro x, y, z, w al comienzo de la clase, y si no tiene cuidado, podría terminar pasando la tabla v en su lugar. Sin embargo, debo admitir que hasta ahora no he tenido problemas.

La única forma que puedo ver a este respecto es mantener una matriz que se devuelva. Lo cual supongo que sería más fácil si los cambiara de la misma forma en que trato con los vectores en primer lugar.

+0

mensaje más código. ¿Dónde se define 'operator *'? ¿Dónde están los métodos getX() y setX()? –

Respuesta

6

En su lugar, le conviene considerar el uso de GLM. Hace todo lo que ha descrito (aunque me ha faltado la documentación), incluida la integración con OpenGL.

+0

¿Por qué le falta la documentación? Estoy de acuerdo con: no hay función de búsqueda y adivinando en qué espacio de nombres está. – Marnix

+1

@Marnix: El manual (http://glm.g-truc.net/glm-0.9.1.pdf) no contiene suficiente información sobre cómo usar todas las características, solo las básicas (por ejemplo, no mencionar el uso de las versiones con plantilla de los vectores o matrices). La documentación de la API (http://glm.g-truc.net/api-0.9.1/index.html) parece haber adoptado un enfoque minimalista para describir la función de los argumentos (por ejemplo, qué hace 'm' en http: //glm.g-truc.net/api-0.9.1/a00237.html#ga48168ff70412019857ceb28b3b2b1f5e?) – andand

+1

De hecho, recomendaría [vmmlib] (http://vmmlib.sourceforge.net/) o [Eigen] (http: //eigen.tuxfamily.org/index.php?title=Main_Page) sobre GLM, personalmente. Ambos funcionan fácilmente con OpenGL, y son fáciles de usar y están bien documentados. – greyfade

0

Como dijiste, estás abusando de la naturaleza "is-a" de la herencia. El problema puede surgir si usted escribió una función como esta

float dotProduct (vector2D a, vector2D b);

Puede pasar un vector 3D y obtener un resultado escalar cuando realmente el producto escalar de un vector 2d y un vector 3d no está definido y esto es en realidad un error que puede llevar a un comportamiento extraño. De acuerdo, esto no es gran cosa, pero estás descartando algunas de las comprobaciones de tipo, y si vas a lidiar con el dolor de la tipado estático, también obtendrás sus beneficios cuando surjan fallas como esa.

El mantenimiento de la matriz es definitivamente una solución superior, no desea confiar en un comportamiento indefinido, porque nunca se sabe cuándo lo va a atornillar por completo.

+0

Ah, no pensé en lo que pasaría si pasaras en Vector2D en lugar de 3 (o viceversa), gracias. –

0

probablemente haría algo como esto:

template<typename T> 
class VectorT{ 
protected: 
    T* m_data; 
    int m_size; 

public: 
    VectorT(unsigned int size) 
    : m_size(size) 
    { 
    m_data=new T[size]; 
    } 

    virtual ~VectorT() 
    { 
    delete[] m_data; 
    } 

    T* operator*() { return m_data; } 
    T& operator[](int ii) { return m_data[ii]; } 
} 

template<typename T> 
class Vector3 : public VectorT<T> 
{ 
public: 
    Vector3() : VectorT(3) {} 
    T getX() { return m_data[0]; } 
    T getY() { return m_data[1]; } 
    T getZ() { return m_data[2]; } 

    Vector3 crossP(const Vector3& vv) { ... } 
} 
2

se puede escuchar a su maestro, y el uso de la especialización parcial (advertencia: no probado):

template<typename T, int size> 
class Vector; 

template< typename T > 
class Vector< T, 2 > 
{ 
    public : 
    Vector() : data() {} 

    T GetX() const { return data[0]; }; 
    T GetY() const { return data[1]; }; 

    void SetX(const T v) const { data[0]=v; }; 
    void SetY(const T v) const { data[1]=v; }; 

    private : 
    T data[2]; 
}; 

template< typename T > 
class Vector< T, 3 > 
{ 
    public : 
    Vector() : data() {} 

    T GetX() const { return data[0]; }; 
    T GetY() const { return data[1]; }; 
    T GetZ() const { return data[2]; }; 

    void SetX(const T v) const { data[0]=v; }; 
    void SetY(const T v) const { data[1]=v; }; 
    void SetZ(const T v) const { data[2]=v; }; 

    private : 
    T data[3]; 
}; 
+1

Probablemente desee usar size_t (o algún otro tipo de entero sin signo en lugar de un int firmado) para el tipo de tamaño para protegerse de los argumentos de la plantilla de tamaño negativo. – andand

1

¿Qué tal esto:

template<class T, int _dim> 
class Vector 
{ 
    T v[_dim]; 
    operator*(){return v;} 

friend T inner_product(Vector<T, _dim> const &v1, Vector<T, _dim> const &v2); 
}; 

template<class T, int _dim> 
T inner_product(Vector<T, _dim> const &v1, Vector<T, _dim> const &v2) 
{ 
    T p = 0.; 
    for(int i; i < _dim; i++) 
     p += v1.v[i] * v2.v[i]; 
    return p; 
} 

template<class T> 
class Vector2 : Vector<T, 2> 
{ 
    float getX() const {return v[0];} 
    float getS() const {return v[0];} 

    float getY() const {return v[1];} 
    float getT() const {return v[1];} 
} 

template<class T> 
class Vector3 : Vector<T, 3>, Vector2<T> 
{ 
    float getZ() const {return v[2];} 
    float getR() const {return v[2];} 
} 

template<class T> 
class Vector4 : Vector<T, 4>, Vector3<T> 
{ 
    float getW() const {return v[3];} 
    float getQ() const {return v[3];} 
} 

Tenga en cuenta que hacer inner_product un amigo, al no ser parte de la clase, le permite usarlo para una ll tipos derivados!