SupportPolygon.cpp

#include "StdAfx.h"
#include "SupportPolygon.h"


#include "Link.h"


using namespace Core;
using namespace Robotics;

using namespace Microsoft;

using namespace System;
using namespace System::Diagnostics;


// DirectXのライブラリによる const/volatile 修飾子の使用の警告を抑制する
#pragma warning( disable : 4400 )


/// [ Constructor ]
SupportPolygon::SupportPolygon( System::Collections::Generic::List< Robotics::PositionVector^ >^ positionVectores )
{
    int vertexSum = positionVectores->Count;    // 頂点数
    Debug::Assert( 3 <= vertexSum, "頂点数は3以上である (さもなければ多角形となり得ない)" );


    // 頂点数分だけメモリを確保する
    Points^ vertices = gcnew Points( vertexSum );

    // 3次元の位置ベクトルから、XY平面上の位置に変換する
    for( int i = 0; i < vertexSum; i++ )
    {
        Debug::Assert( positionVectores[ i ]->Z < Link::GroundHeight, "支持多角形となり得る頂点は接地している" );

        /// @note
        /// ここでは形状の頂点の位置を確認しているが、リンクが定義している
        /// 「接地しているとみなす高さ」はリンクの位置であり、そこに相違がある。


        vertices[ i ] = Point(
            positionVectores[ i ]->X,
            positionVectores[ i ]->Y
        );
    }


    // 凸包を求める
    m_vertices = Geometry::GrahamScan( vertices );
}

/// [ Copy constructor ]
SupportPolygon::SupportPolygon( SupportPolygon^ other )
{
    // 頂点を 同じ数だけ生成する
    m_vertices = gcnew Points( other->CountOfVertex );

    // ... 全ての頂点をコピーする
    other->m_vertices->CopyTo( m_vertices, 0 );
}

/// [ Finalizer ]
SupportPolygon::!SupportPolygon()
{
    delete m_figure;
}


/// 生成する
SupportPolygon^ SupportPolygon::Create( Links^ links )
{
    // 支持多角形を構成する頂点を格納するためのリストを生成する
    Collections::Generic::List< PositionVector^ >^ composeVertex
        = gcnew Collections::Generic::List< PositionVector^ >();


    // 支持多角形を構成する頂点を集める
    for each( Link^ link in links )
    {
        if( link->IsSupportingPhase() )
        {
            // 支持相ならば、形状の頂点を取得する
            PositionVectors^ vertexsOfShape = link->GetAbsolutePositionOfVertices();

            // ... それの下面だけを、構成する頂点に追加する
            for( int i = 0; i < ( vertexsOfShape->Length / 2); i++ )
            {
                composeVertex->Add( vertexsOfShape[ i ] );
            }
        }
    }
    Debug::Assert( composeVertex->Count != 0, "支持相のリンクは存在する" );


    // 生成して返す
    return gcnew SupportPolygon( composeVertex );
}


/// 安定余裕を求める
double SupportPolygon::CalculateStabilityMargin( Robotics::PositionVector^ centerOfGravity )
{
    // 重心投影点を求める
    Point centerOfGravityProjection( centerOfGravity->X, centerOfGravity->Y );


    double result = Double::MaxValue;   // 安定余裕

    for( int i = 0; i < CountOfVertex; i++ )
    {
        // 重心投影点と 支持多角形を構成する線分との距離を求める
        double distance = Geometry::CalculateDistanceOfPointAndSegment(
            centerOfGravityProjection, Segment[ i ]
        );

        if( distance < result )
        {
            // その距離が現在の値より小さければ これを更新する
            result = distance;
        }
    }

    Debug::Assert( 0.0 <= result, "点と線分との距離は 正数である" );

    if( !Geometry::IsInsideOfPolygon( m_vertices, centerOfGravityProjection ) )
    {
        // 重心投影点が支持多角形の外部にあるならば、符合を反転する
        result = -result;
    }
    return result;
}


/// 線分を取得する [ Property ]
Robotics::Segment SupportPolygon::Segment::get( int index )
{
    Debug::Assert( index < CountOfVertex, "インデックスは 存在する頂点を示している" );


    // 最後の頂点ならば最初の頂点を、さもなくば次の頂点を指定する
    int nextIndex = ( index == ( CountOfVertex - 1 ) )? 0 : index + 1;

    // ... 2つの頂点を結んだ線分を 生成して返す
    return Robotics::Segment(
        m_vertices[ index ],
        m_vertices[ nextIndex ]
    );
}


/// PointF構造体に キャストして返す
array< System::Drawing::PointF >^ SupportPolygon::ToPointF()
{
    array< Drawing::PointF >^ result = gcnew array< Drawing::PointF >( CountOfVertex );

    for( int i = 0; i < CountOfVertex; i++ )
    {
        result[ i ] = m_vertices[ i ].ToPointF();
    }
    return result;
}


/// 図形を生成する
void SupportPolygon::CreateFigure( Microsoft::DirectX::Direct3D::Device^ device )
{
    // 図形の 頂点バッファを生成する
    m_figure = gcnew DirectX::Direct3D::VertexBuffer(
        DirectX::Direct3D::CustomVertex::PositionColored::typeid,   // 頂点データを定義した構造体
        CountOfVertex + 1,                                          // 頂点数
        device,                                                     // デバイス
        DirectX::Direct3D::Usage::Dynamic |                         // 使用法
        DirectX::Direct3D::Usage::WriteOnly,
        DirectX::Direct3D::CustomVertex::PositionColored::Format,   // 頂点フォーマット
        DirectX::Direct3D::Pool::Default
        );

    // 頂点の生成イベントに登録する
    m_figure->Created += gcnew System::EventHandler( this, &SupportPolygon::SetFigure );

    // ... そのイベントを呼び出して 設定させる
    SetFigure( m_figure, nullptr );
}

/// 図形を設定する
void SupportPolygon::SetFigure( System::Object^ sender, System::EventArgs^ )
{
    // 引数から目的とする型を取得する
    DirectX::Direct3D::VertexBuffer^ vertexBuffer
        = safe_cast< DirectX::Direct3D::VertexBuffer^ >( sender );


    // 頂点を格納する配列を生成する
    array< DirectX::Direct3D::CustomVertex::PositionColored >^ vertices
        = gcnew array< DirectX::Direct3D::CustomVertex::PositionColored >( CountOfVertex + 1 );

    for( int i = 0; i < CountOfVertex; i++ )
    {
        vertices[ i ].Position = DirectX::Vector3(  // 頂点の位置
            safe_cast< float >( m_vertices[ i ].X ),
            safe_cast< float >( m_vertices[ i ].Y ),
            0.0f
            );

        vertices[ i ].Color = Drawing::Color::Orange.ToArgb(); // 頂点の色
    }

    // 最後の頂点を 最初の頂点に重ねて ループを閉じる
    vertices[ CountOfVertex ].Position = vertices[ 0 ].Position;
    vertices[ CountOfVertex ].Color = vertices[ 0 ].Color;


    // 頂点データの範囲をロックして、頂点バッファ メモリへのリファレンスを取得する
    vertexBuffer->SetData( vertices, 0, DirectX::Direct3D::LockFlags::None );

    // [ Reference ]「Managed DirectX9」Tom Miller(Sams) P40
}


/// 描画する
void SupportPolygon::Render( Microsoft::DirectX::Direct3D::Device^ device )
{
    Debug::Assert( m_figure != nullptr, "図形は生成されている" );


    // ライトを無効にする
    device->RenderState->Lighting = false;


    // 変換行列を初期化する
    device->Transform->World = DirectX::Matrix::Identity;


    // 座標軸の頂点バッファを デバイスのデータ ストリームにバインドする
    device->SetStreamSource( 0, m_figure, 0 );
    device->VertexFormat = DirectX::Direct3D::CustomVertex::PositionColored::Format; // 頂点フォーマット

    // ... レンダリングする
    device->DrawPrimitives(
        DirectX::Direct3D::PrimitiveType::LineStrip,
        0,              // 頂点の読み出し開始位置
        CountOfVertex   // プリミティブの数
        );
}