Link.cpp

#include "StdAfx.h"
#include "Link.h"


#include "ConnectionRelation.h"

#include "Joint.h"
#include "Shape.h"


using namespace Core;
using namespace Robotics;

using namespace Microsoft;

using namespace System;
using namespace System::Diagnostics;


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


/// XMLから生成する [ Constructor ]
Link::Link( Core::Joint^ joint, System::Xml::XmlElement^ element ) :
    m_joint( joint )
{
    m_posture = gcnew RotationMatrix();
    m_centerOfGravity = gcnew PositionVector();

    m_shape = Core::Shape::Create( element );   // 形状
    Name = element->GetAttribute( "name" );     // 名称


    // 関節の 絶対位置の変更イベントに登録する
    m_joint->AbsolutePositionChanged += gcnew System::EventHandler( this, &Link::JointPositionChanged );
}

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


/// 名称を取得する [ Property ]
System::String^ Link::Name::get()
{
    Debug::Assert( m_joint->Name == m_shape->Name, "関節と形状の名称は等しい" );

    // 関節の名称を返す
    return m_joint->Name;
}

/// 名称を設定する [ Property ]
void Link::Name::set( System::String^ value )
{
    Debug::Assert( m_joint != nullptr, "関節は生成されている" );

    m_joint->Name = value;  // 関節の名称
    m_shape->Name = value;  // 形状の名称
}


/// 絶対位置を取得する [ Property ]
Robotics::PositionVector^ Link::AbsolutePosition::get()
{
    /// @note
    /// これが返す位置ベクトルに変更を加えても、この属性は変更されない。


    // 関節の絶対位置を取得する
    PositionVector^ result = gcnew PositionVector( m_joint->AbsolutePosition );

    // ... 形状の相対位置を加算する
    return result + ( Posture * m_shape->RelativePosition );
}

/// 絶対位置を設定する [ Property ]
void Link::AbsolutePosition::set( Robotics::PositionVector^ value )
{
    /// @note
    /// 絶対位置は姿勢の影響を受けるため、それも同時に設定する場合は
    /// 姿勢を先に設定すること。

    Debug::Assert( !IsStandard(), "基準リンクの絶対位置は変更されない" );
    Debug::Assert( -GroundHeight <= value->Z, "位置は地面より上に設定される" );


    // 形状の相対位置を減算する
    value -= Posture->TransposedMatrix() * m_shape->RelativePosition;

    // ... 関節の絶対位置を設定する
    m_joint->AbsolutePosition = value;
}


/// 姿勢を設定する [ Property ]
void Link::Posture::set( Robotics::RotationMatrix^ value )
{
    Debug::Assert( !IsStandard(), "基準リンクの姿勢は変更されない" );


    if( m_posture != value )
    {
        // 現在の値と異なるならば、設定する
        m_posture = value;

        // ... 姿勢の変更イベントを発生する
        PostureChanged( this, nullptr );


        // 絶対位置も変更されるので、それの変更イベントを発生する
        AbsolutePositionChanged( this, nullptr );
    }
}


/// 軸方向(ワールド座標系) を取得する [ Property ]
AxisVector^ Link::AxialDirection::get()
{
    // 関節の軸方向(ローカル座標系)を ワールド座標系に変換する
    return m_posture * m_joint->AxialDirection;

    // [ Reference ]「ヒューマノイドロボット」梶田秀司(オーム社) P61
}


/// 質量を設定する [ Property ]
void Link::Mass::set( double value )
{
    m_mass = value;

    // 構造変化イベントを発生する
    OnMechanicsChanged( this, nullptr );
}

/// 重心を設定する [ Property ]
void Link::CenterOfGravity::set( Robotics::PositionVector^ value )
{
    m_centerOfGravity = value;


    // 位置ベクトルの 値の変更イベントに登録する
    m_centerOfGravity->ValueChanged += gcnew System::EventHandler( this, &Link::OnMechanicsChanged );


    /// @note
    /// 位置ベクトルのプロパティに直接 設定された場合にも、
    /// このクラスから変更イベントを発生できるようにしている。
}

/// 構造変化イベントを発生する
void Link::OnMechanicsChanged( System::Object^, System::EventArgs^ e )
{
    MechanicsChanged( this, e );
}


/// 形状の頂点の絶対位置
PositionVectors^ Link::GetAbsolutePositionOfVertices()
{
    PositionVectors^ result = m_shape->GetAbsolutePositionOfVertices(
        m_joint->AbsolutePosition,
        m_posture
    );
    return result;
}

/// 形状の最下部を取得する
Robotics::PositionVector^ Link::GetBottomOfShape()
{
    PositionVector^ result;
    double bottom = Double::MaxValue;   // 最下部

    // 全ての形状の頂点の絶対位置について調べる
    for each( PositionVector^ vertex in GetAbsolutePositionOfVertices() )
    {
        if( vertex->Z < bottom )
        {
            // 最下部を更新する
            bottom = vertex->Z;

            // 結果を更新する
            result = vertex;
        }
    }
    Debug::Assert( bottom != Double::MaxValue, "最下部は更新される" );

    return result;
}


/// 位置と姿勢を設定する
void Link::SetPositionAndPosture( Robotics::PositionVector^ position, Robotics::RotationMatrix^ posture )
{
    Debug::Assert( !IsStandard(), "基準リンクの 位置と姿勢は変更されない" );


    if( ( position != AbsolutePosition ) || ( posture != m_posture ) )
    {
        // 位置と姿勢の変更イベントを発生する
        PositionAndPostureChanged( this, gcnew PositionAndPostureChangeEventArgs( position, posture ) );
    }
}

/// 位置と姿勢を 強制的に設定する
void Link::SetPositionAndPostureForcibly( Robotics::PositionVector^ position, Robotics::RotationMatrix^ posture )
{
    // 基準リンクを解除する
    m_standard = nullptr;

    /// @note
    /// 基準リンクの 位置と姿勢は変更できないため、
    /// 先にそれを解除する必要がある。


    Posture = posture;              // 姿勢
    AbsolutePosition = position;    // 絶対位置

    /// @note
    /// 位置より先に姿勢を設定しなければならない。
    /// (詳細は 絶対位置プロパティを参照せよ)


    Debug::Assert( IsSupportingPhase(), "支持相に設定されている" );


    // 基準リンクにする
    m_standard = this;

    /// @note
    /// 基準リンクの変更イベントを発生させないために、
    /// ここではプロパティを用いずに設定している。
}


/// ローカル座標系における 重心ベクトルを取得する
Robotics::PositionVector^ Link::GetCenterOfGravityInLocalPosition()
{
    return( m_posture * m_centerOfGravity );
}

/// ワールド座標系における 重心ベクトルを取得する
Robotics::PositionVector^ Link::GetCenterOfGravityInWorldPosition()
{
    // ローカル座標系における重心ベクトルを取得する
    PositionVector^ localPosition = GetCenterOfGravityInLocalPosition();

    // ... ワールド座標系における重心ベクトルを求める
    return m_mass * ( m_joint->AbsolutePosition + localPosition );
}


/// 胴体を設定する [ Property ]
void Link::Body::set( Link^ value )
{
    Debug::WriteLine( "胴体を設定する : " + value->Name );

    Debug::Assert( value != nullptr, "有効なインスタンスである" );
    Debug::Assert( value->Connection->Upper == nullptr, "接続元を持たない" );


    // 胴体を設定する
    m_body = value;
}


/// 基準リンクを設定する [ Property ]
void Link::Standard::set( Link^ value )
{
    Debug::Assert( value != nullptr, "基準リンクは解除されない" );
    Debug::Assert( value->IsSupportingPhase(), "支持相である" );


    if( m_standard != value )
    {
        // 現在の値と異なるならば、設定する
        m_standard = value;

        // ... 基準リンクの変更イベントを発生する
        StandardChanged( value, nullptr );

        Debug::WriteLine( "基準リンクを設定した : " + value->Name );
    }
}


/// 支持相か?
bool Link::IsSupportingPhase()
{
    if( GroundHeight < Math::Abs( AbsolutePosition->Z ) )
    {
        // 地面に接していなければ、支持相ではない
        return false;
    }


    // 姿勢の誤差[ deg ]
    double errorOfPosture = Angle::CalculateRadian( 0.1 );
    if( !Posture->IsHorizontal( errorOfPosture ) )
    {
        // 水平でなければ、支持相ではない
        return false;
    }
    return true;
}


/// 胴体まで 自身を経路に追加する (接続元を 胴体まで呼び出す)
void Link::AddOwnToRouteToBody( LinkList^ route )
{
    Debug::Assert( route != nullptr, "格納先の経路は生成されている" );
    Debug::Assert( Body != nullptr, "胴体は設定されている" );


    if( !IsBody() )
    {
        // 胴体でなければ、自身を経路に追加する
        route->Add( this );


        Link^ upper = m_connectionRelation->Upper; // 接続元
        Debug::Assert( upper != nullptr, "接続元は存在する" );

        // ... 接続元の同一関数を呼び出す
        upper->AddOwnToRouteToBody( route );
    }

    // [ Reference ]「ヒューマノイドロボット」梶田秀司(オーム社) P64
}

/// 末端まで 自身を経路に追加する (対等な接続と接続先を 末端まで呼び出す)
void Link::AddOwnToRouteToEnd( LinkList^ route )
{
    Debug::Assert( route != nullptr, "格納先の経路は生成されている" );


    // 自身を経路に追加する
    route->Add( this );


    if( ( m_connectionRelation->Equal != nullptr ) && ( route->Count != 1 ) )
    {
        // 経路の最初でなく 対等な接続が存在するならば、それを呼び出す
        m_connectionRelation->Equal->AddOwnToRouteToEnd( route );
    }

    if( m_connectionRelation->Lower != nullptr )
    {
        // 接続先が存在するならば、それを呼び出す
        m_connectionRelation->Lower->AddOwnToRouteToEnd( route );
    }
}


/// 関節の位置が変更された
void Link::JointPositionChanged( System::Object^, System::EventArgs^ e )
{
    Debug::Assert( !IsStandard(), "基準リンクの関節の位置は変更されない" );

    // 自身を発生元として、イベントを発生する
    AbsolutePositionChanged( this, e );


    /// @note
    /// リンクの絶対位置は、関節の絶対位置から算出している。
    /// よって、関節の位置が変更された場合には、リンクも変更されたものとして
    /// それを通知する必要がある。
}


/// トルクを求める
double Link::CalculateTorque( Robotics::PositionVector^ force, Robotics::PositionVector^ pointOfAction )
{
    // 作用点と関節位置との変位量を求める
    PositionVector^ displacement = ( pointOfAction - m_joint->AbsolutePosition );

    // ... ワールド座標系での軸方向とその変位量との ベクトルの外積から モーメント・アームの大きさを求める
    Vector^ momentArm = Vector::CrossProduct( this->AxialDirection, displacement );

    // ... モーメント・アームの大きさと力の内積から トルクを求める
    return Vector::InnerProduct( momentArm, force );


    // [ Reference ]「ロボット制御基礎論」吉川恒夫(コロナ社) P65
    // [ Reference ]「ヒューマノイドロボット」梶田秀司(オーム社) P61
}


/// 図形を生成する
void Link::CreateFigure( Microsoft::DirectX::Direct3D::Device^ device )
{
    if( !IsBody() )
    {
        // 図形の 頂点バッファを生成する
        m_figure = gcnew DirectX::Direct3D::VertexBuffer(
            DirectX::Direct3D::CustomVertex::PositionColored::typeid,   // 頂点データを定義した構造体
            2,                                                          // 頂点数
            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, &Link::SetFigure );

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


        // 関節の図形を生成する
        m_joint->CreateFigure( device );
    }


    // 形状の図形を生成する
    m_shape->CreateFigure( device );
}

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

    PositionVector^ position = m_joint->RelativePosition;   // 関節の相対位置


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

    // ... 頂点の位置を設定する
    vertices[ 0 ].Position = DirectX::Vector3( 0.0f, 0.0f, 0.0f );
    vertices[ 1 ].Position = DirectX::Vector3(
        safe_cast< float >( -position->X ),
        safe_cast< float >( -position->Y ),
        safe_cast< float >( -position->Z )
        );


    int color = Drawing::Color::White.ToArgb();

    // ... 頂点の色を設定する
    vertices[ 0 ].Color = color;
    vertices[ 1 ].Color = color;


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

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


/// 機構モデルを描画する
void Link::RenderMechanismModel( Microsoft::DirectX::Direct3D::Device^ device )
{
    if( !IsBody() )
    {
        // ライトを無効にする
        device->RenderState->Lighting = false;


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

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


        // 関節を描画する
        m_joint->Render( device );
    }


    /// @note 胴体ならば、胴体の形状を描画する。
    /// @note 末端ならば、形状を描画する。


    // 基準リンクかどうかで、形状の色を設定する
    m_shape->Color = ( IsStandard() )?
        Drawing::Color::Gray :
        Drawing::Color::White;


    if( !IsBody() )
    {
        // 軸回りに回転する
        m_joint->RotateAroundAxis( device );
    }
    // ... シンプルな形状を描画する
    m_shape->RenderSimpleFigure( device );
}

/// リアルなモデルを描画する
void Link::RenderRealModel( Microsoft::DirectX::Direct3D::Device^ device )
{
    if( !IsBody() )
    {
        // 軸回りに回転する
        m_joint->RotateAroundAxis( device );
    }
    // ... リアルな形状を描画する
    m_shape->RenderRealFigure( device );
}