4

アプリケーションのジオメトリを扱う部分の設計に問題があります。特に、クラスの階層と交差のための個別のメソッドが必要です。

問題

階層は次のようになります。

  • ジオメトリ
    • メッシュ
    • パラメトリック
      • 球体

そして、交差メソッドは次のようになります。

namespace intersections
{
  bool intersection( const Box &, const Box &);
  bool intersection( const Box &, const Sphere &);
}

これは非常に簡単です。std::vector問題が発生するのは、すべてのジオメトリを 1 つの構造(または KD ツリーなど) にまとめて格納したい場合です。

そのためには、を使用する必要がありますstd::vector<Geometry*>。ただし、このベクトルから読み取るとGeometry*オブジェクトが取得されるため、適切な交差関数を呼び出す方法がありません。

問題の例:

std::vector<Geometry*> arrGeometry;

// add the elements
arrGeometry.push( new Box() );
arrGeometry.push( new Sphere() );

// ... some more code

// try to intersect them?
Geometry* g1 = arrGeometry[0];
Geometry* g2 = arrGeometry[1];
bool intersecting = intersections::intersection( g1, g2 ); //< As expected, this does
                                                          // not work

ジオメトリ オブジェクト内にアルゴリズムを実装した場合、問題はビジターと非常に奇妙な関数呼び出しのバウンスによって解決される可能性があります。

ただし、交差アルゴリズムは Geometry クラスの外に置きたいと思います。理由は次のとおりです。

  • どちらが所有権を持つべきかの決定を避けるため (例えば、ボックスと球の間の交差をどこに実装しますBoxSphere?)

  • ジオメトリ オブジェクトが乱雑になるのを避けるために、ジオメトリに対してできることはすべてです。これは非常に多くのことです (ほんの数例を挙げると、ボクセル化、交点の計算、建設的なジオメトリ オペレータの適用など)。したがって、ロジックをデータから分離することは、ここでは非常に望ましいことです。

一方、特定のジオメトリを抽象化できるものがあるため、テンプレートの代わりに階層が必要です... (たとえば、std::vector、または KD ツリー、または... に格納するため)。

これをどのように解決しますか?これに適したデザインパターンはありますか?私はいくつかのライブラリを見てみましたが、私はすでにそうであったことにもっと混乱しました...

最も簡単な方法 (時々使用される) は、RTTI (またはそれを偽造する) とダウンキャストを使用することですが、それは正確には維持できません... (新しいジオメトリを追加すると、すべてのコードで多くの switch ステートメントを変更する必要があります)。

何かご意見は?

よろしくお願いします。

4

3 に答える 3

2

別の解決策を考えています。速度が心配な場合、これはO(1)の複雑さですが、モードジオメトリタイプのc++コードを自動生成するためのプログラムが必要になる場合があります。交差関数の配列を作成できます(擬似コード、手元にコンパイラはありません):

enum GeometryType
{
    TypeMesh,
    TypeParamBox,
    TypeParamSphere,
    MaxType,
};

bool intersection( const Mesh*, const Mesh* );
bool intersection( const Mesh*, const Box* );
bool intersection( const Mesh*, const Sphere* );

bool intersection( const Box*, const Mesh* );
bool intersection( const Box*, const Box* );
bool intersection( const Box*, const Sphere* );

bool intersection( const Sphere*, const Mesh* );
bool intersection( const Sphere*, const Box* );
bool intersection( const Sphere*, const Sphere* );

template<class T1,class T2>
bool t_intersection( const Geometry* first, const Geometry* second )
{
    return intersection( static_cast<const T1*>( first ), static_cast<const T1*>( second ) );
}

typedef bool (*uni_intersect)( const Geometry*, const Geometry* );

const uni_intersect IntersectionArray[] = // 2D array all possible combinations
{
    t_intersection<Mesh,Mesh>,
    t_intersection<Mesh,Box>,
    t_intersection<Mesh,Sphere>,

    t_intersection<Box,Mesh>,
    t_intersection<Box,Box>,
    t_intersection<Box,Sphere>,

    t_intersection<Sphere,Mesh>,
    t_intersection<Sphere,Box>,
    t_intersection<Sphere,Sphere>,
};

bool main_intersection( const Geometry* first, const Geometry* second )
{
    const unsigned index = (unsigned)(first->Type) * (unsigned)MaxType + (unsigned)(second->Type);
    return IntersectionArray[ index ]( first, second );
}

または代替:

const uni_intersect IntersectionArray[] = // 2D array all possible combinations
{
    t_intersection<Mesh,Mesh>,
    t_intersection<Mesh,Box>,
    t_intersection<Mesh,Sphere>,

    nullptr, // skip mirrored functions
    t_intersection<Box,Box>,
    t_intersection<Box,Sphere>,

    nullptr,
    nullptr,
    t_intersection<Sphere,Sphere>,
};

bool main_intersection( const Geometry* first, const Geometry* second )
{
    const unsigned Type1 = unsigned(first->Type);
    const unsigned Type2 = unsigned(second->Type);
    unsigned index;
    if( Type1 < Type2 )
        index = Type1 * (unsigned)MaxType + Type2;
    else
        index = Type1 + Type2 * (unsigned)MaxType;

    return IntersectionArray[ index ]( first, second );
}
于 2012-10-01T12:31:53.857 に答える
1

この問題はこれに似ています

class Geometry
{
public:
    enum eType
    {
        TypeMesh,
        TypeParamBox,
        TypeParamSphere,
    };

    Geometry( eType t ): Type( t ) { }
    ~Geometry() { }

    const eType Type;
};


class Mesh : public Geometry
{
public:
    Mesh(): Geometry( TypeMesh ) { }
};


class Parametric : public Geometry
{
public:
    Parametric( eType t ): Geometry( TypeMesh ) { }
};


class Box : public Parametric
{
public:
    Box(): Parametric( TypeParamBox ) { }
};


class Sphere : public Parametric
{
public:
    Sphere(): Parametric( TypeParamSphere ) { }
};


namespace intersections
{
    bool intersection( const Geometry* first, const Geometry* second );
    template <typename T>
    bool t_intersection( const T* first, const Geometry* second );

    bool intersection( const Box*, const Box* );
    bool intersection( const Box*, const Sphere* );
    bool intersection( const Sphere*, const Box* );
    bool intersection( const Sphere*, const Sphere* );
}


void foo_test()
{
    std::vector<Geometry*> arrGeometry;

    // add the elements
    arrGeometry.push_back( new Box() );
    arrGeometry.push_back( new Sphere() );

    // ... some more code

    // try to intersect them?
    Geometry* g1 = arrGeometry[0];
    Geometry* g2 = arrGeometry[1];
    bool intersecting = intersections::intersection( g1, g2 );
}


bool intersections::intersection( const Geometry* first, const Geometry* second )
{
    switch( first->Type )
    {
    case Geometry::TypeParamBox: return t_intersection( static_cast<const Box*>( first ), second );
    case Geometry::TypeParamSphere: return t_intersection( static_cast<const Sphere*>( first ), second );
    default: return false;
    }
}


template <typename T>
bool intersections::t_intersection( const T* first, const Geometry* second )
{
    switch( second->Type )
    {
    case Geometry::TypeParamBox: return intersection( first, static_cast<const Box*>( second ) );
    case Geometry::TypeParamSphere: return intersection( first, static_cast<const Sphere*>( second ) );
    default: return false;
    }
}

または、継承を使用しない場合:

struct Mesh{};

struct Box{};

struct Sphere{};


enum GeometryType
{
    TypeMesh,
    TypeParamBox,
    TypeParamSphere
};


struct Geometry
{
    GeometryType Type;

    union
    {
        Mesh* pMesh;
        Box* pBox;
        Sphere* pSphere;
    } Ptr;
};



namespace intersections
{
    bool intersection( const Geometry* first, const Geometry* second );
    template <typename T>
    bool t_intersection( const T* first, const Geometry* second );

    bool intersection( const Box*, const Box* );
    bool intersection( const Box*, const Sphere* );
    bool intersection( const Sphere*, const Box* );
    bool intersection( const Sphere*, const Sphere* );
}



void foo_test()
{
    std::vector<Geometry*> arrGeometry;

    // add the elements
//  arrGeometry.push_back( new Box() );
//  arrGeometry.push_back( new Sphere() );

    // ... some more code

    // try to intersect them?
    Geometry* g1 = arrGeometry[0];
    Geometry* g2 = arrGeometry[1];
    bool intersecting = intersections::intersection( g1, g2 );
}


bool intersections::intersection( const Geometry* first, const Geometry* second )
{
    switch( first->Type )
    {
    case TypeParamBox: return t_intersection( first->Ptr.pBox, second );
    case TypeParamSphere: return t_intersection( first->Ptr.pSphere, second );
    default: return false;
    }
}


template <typename T>
bool intersections::t_intersection( const T* first, const Geometry* second )
{
    switch( second->Type )
    {
    case TypeParamBox: return intersection( first, second->Ptr.pBox );
    case TypeParamSphere: return intersection( first, second->Ptr.pSphere );
    default: return false;
    }
}


注: 1 つのジオメトリのタイプがわかっている場合は、テンプレート化された関数を使用できます。

Box* pBox;
Geometry* pUnknownGeometry;
bool intersecting = intersections::t_intersection( pBox, pUnknownGeometry );


注2:次のように同一の関数を書くことができます:

bool intersection( const Box* first, const Sphere* second );

bool intersection( const Sphere* first, const Box* second )
{
    return intersection( second, first );
}


編集:

問題を二重発送に減らす場合は、この手法を使用できます。

交差の場合、階層を次のように扱います。

  • ジオメトリ
    • メッシュ
    • 球体

その他のアクションの場合:

  • ジオメトリ
    • メッシュ
    • パラメトリック
      • 球体
于 2012-09-28T16:49:16.060 に答える
0

交差点アルゴリズムを、取得するジオメトリのセットを処理できるかどうかを決定できるクラスとして表現し、元のインターフェイスを持つ交差点プロキシを提供するのはどうでしょうか。これは次のようになります。

class IIntersection
{
   public:
     bool intersection( const Geometry &, const Geometry &);
     bool accept( const Geometry &, const Geometry &);
}

class IntersectionBoxSphere : public IIntersection
{
public:
   bool intersection( const Geometry &, const Geometry &);
   bool accept( const Geometry & a, const Geometry &b)
   {
      return ((dynamic_cast<Box>(a) != NULL || dynamic_cast<Box>(b) != NULL)
              (dynamic_cast<Sphere>(a) != NULL || dynamic_cast<Sphere>(b) != NULL))
   }
}

class IntersectionBoxbox  : public IIntersection
...
    /**collect some where all algorithms*/
    IIntersection* myintersections[2];
    myintersections[0] = new IntersectionBoxSphere()
    myintersections[1] = new IntersectionBoxbox()
...
/**decide here which to use */
bool intersection( const Geometry &a, const Geometry &b)
{
    for ( i ... )
     if (myintersections[i]->appect(a,b))
       return myintersections[i]->intersection(a,b)
}
于 2012-09-28T15:43:55.660 に答える