在数学库之间无缝转换

Posted

技术标签:

【中文标题】在数学库之间无缝转换【英文标题】:Seamlessly cast between math libraries 【发布时间】:2012-02-22 22:10:09 【问题描述】:

我在一个游戏项目中使用了两个数学库。一个是GLM 库,另一个是Box2D 的数学部分。有时两者之间的转换是必要的,像这样:

b2Vec2 vec1(1.0f, 1.0f);
glm::vec2 vec2(vec1.x, vec1.y);

我想知道是否有更好的方法可以在不编辑任何库的情况下更无缝地完成它?

【问题讨论】:

【参考方案1】:

我认为在不修改库的情况下无法隐式进行转换。

但是,为了简化您的转换代码,您可以实现简单的转换函数,例如:

inline glm::vec2 make_glmVec2(const b2Vec2 &v) 
    return glm::vec2(v.x, v.y);

inline glm::vec3 make_glmVec3(const b2Vec3 &v) 
    return glm::vec3(v.x, v.y, v.z);

如果这两个库的类型之间(几乎)直接对应,您甚至可以为所有转换函数使用更简单的名称,例如 toGlm 并为您需要的所有类型简单地重载它:

inline glm::vec2 toGlm(const b2Vec2 &v) 
    return glm::vec2(v.x, v.y);

inline glm::vec3 toGlm(const b2Vec3 &v) 
    return glm::vec3(v.x, v.y, v.z);


编辑

我尝试实现一个“代理”类,它可以作为来自两个库的两个类之间的桥梁。代理类包含构造函数和强制转换运算符,它们允许您在这些类之间进行创建和创建。不幸的是,你需要显式调用构造函数,否则编译器甚至不会考虑使用这个类:

//Library 1:
class Vec1 
public:
  int x;
  int y;
  Vec1(int _x, int _y) : x(_x), y(_y) 
;

//Library 2:   
class Vec2 
public:
  int e1;
  int e2;
  Vec2(int _x, int _y) : e1(_x), e2(_y) 
;

//Your code

class VecProxy 
public:
  int pxX;
  int pxY;

  VecProxy(const Vec1& v1) : pxX(v1.x), pxY(v1.y) 
  VecProxy(const Vec2& v2) : pxX(v2.e1), pxY(v2.e2) 

  operator Vec1() return Vec1(pxX, pxY); 
  operator Vec2() return Vec2(pxX, pxY); 

;

int main() 
  Vec1 v1(2,3);
  Vec2 v2=VecProxy(v1);
  Vec1 v3=VecProxy(v2);

请注意,您可以使用相同的名称,无论您向哪个方向投射,这可能比我上面之前的建议稍微好一点。我不认为你可以隐式调用构造函数。

【讨论】:

在我看来,编写一个从一种类型到另一种类型的转换函数并重载它以进行反之转换要容易得多。【参考方案2】:

理论上,您可以将隐式转换运算符添加到一个或两个库中(尽管隐式转换不一定是个好主意)。但是你说你不能编辑任何一个库,所以排除了。

因此,一种替代方法是引入您自己的向量类,并为其提供必要的转换运算符。然后,当您在代码中需要向量时,始终将其存储为自定义类的对象,并在需要使用库时(隐式)进行转换。

但同样,隐式转换可能带来的麻烦可能超过表面上的好处(有关此内容的更多详细信息,请参阅More Effective C++ 的第 5 条)。

【讨论】:

【参考方案3】:

我知道这有点老了,但我最近在我当前的项目中遇到了同样的情况。我想分享我的解决方案,这对我很有效。这些只是我最常使用的类型(我对类型进行了类型定义以便更容易(阅读:懒惰)在我的项目中输入)。这可能会被扩展/调整,但希望这将为一起使用这两个库的任何人提供另一种选择。

typedef b2Vec2 b2vec2;
typedef b2Mat22 b2mat2;
typedef glm::vec2 vec2;
typedef glm::mat2 mat2;

//Conversion types
namespace Convert

///////////////////////////////////////
//Use case examples
//
// Turn the glm vector2 into a box2d type
// vec2 a = vec2( 1, 2 );
// b2vec2 b = Convert::v2(a).to_box2d;
//
// Access the box2d b2vec2 like a glm type
// b2vec2 c = b2vec2( 1, 2 );
// Convert::v2(c).to_glm = vec2(3,4);
//
// Turn the const glm vector2 into a box2d type
// const vec2 d = vec2( 1,2 );
// b2vec2 e = Convert::c_v2(d).to_box2d;


//Don't use these directly. See the use case examples
union _Vec2 
    _Vec2( vec2* _v ):glm(_v)
    _Vec2( b2vec2* _v ):box2d(_v)
    vec2* glm;
    b2vec2* box2d;
;
union _CVec2 
    _CVec2( const vec2* _v ):glm(_v)
    _CVec2( const b2vec2* _v ):box2d(_v)
    const vec2* glm;
    const b2vec2* box2d;
;

union _Mat2 
    _Mat2( mat2* _m ):glm(_m)
    _Mat2( b2mat2* _m ):box2d(_m)
    mat2* glm;
    b2mat2* box2d;
;

//Convert between vec2 and b2vec2
struct v2 

private:
    _Vec2 data;

public:

    vec2& to_glm;
    b2vec2& to_box2d;

    v2( vec2& _v )
    :data( &_v )
    ,to_glm( *data.glm )
    ,to_box2d( *data.box2d )

    v2( b2vec2& _v )
    :data( &_v )
    ,to_glm( *data.glm )
    ,to_box2d( *data.box2d )
;

//Convert between const vec2 and const b2vec2
struct c_v2 

private:
    _CVec2 data;

public:

    const vec2& to_glm;
    const b2vec2& to_box2d;

    c_v2( const vec2& _v )
    :data( &_v )
    ,to_glm( *data.glm )
    ,to_box2d( *data.box2d )

    c_v2( const b2vec2& _v )
    :data( &_v )
    ,to_glm( *data.glm )
    ,to_box2d( *data.box2d )
;

//Convert between mat2 and b2mat22
struct m2 

private:
    _Mat2 data;

public:

    mat2& to_glm;
    b2mat2& to_box2d;

    m2( mat2& _m )
    :data( &_m )
    ,to_glm( *data.glm )
    ,to_box2d( *data.box2d )

    m2( b2mat2& _m )
    :data( &_m )
    ,to_glm( *data.glm )
    ,to_box2d( *data.box2d )
;

【讨论】:

以上是关于在数学库之间无缝转换的主要内容,如果未能解决你的问题,请参考以下文章

3D数学基础:四元数与欧拉角之间的转换

#8 Python数学方法

数学转换 sRGB 和 Adob​​eRGB

C++调用matlab数学函数问题

Python数学模块毕业

将数学字符串转换为 int