Joint.cpp

#include "StdAfx.h"
#include "Joint.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 ]
Joint::Joint( Robotics::PositionVector^ relativePosition, Robotics::AxisVector^ axialDirection, Robotics::Angle angle ) :
    m_relativePosition( relativePosition ),
    m_axialDirection( axialDirection ),
    m_angle( angle ),
    m_initialAngle( angle )
{
    m_absolutePosition = gcnew PositionVector();


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


/// 絶対位置を設定する [ Property ]
void Joint::AbsolutePosition::set( PositionVector^ value )
{
    if( m_absolutePosition != value )
    {
        // 現在の値と異なるならば、設定する
        m_absolutePosition = value;

        // ... 絶対位置の変更イベントを発生する
        AbsolutePositionChanged( this, nullptr );
    }
}


/// 回転角を設定する [ Property ]
void Joint::Angle::set( Robotics::Angle value )
{
    Debug::Assert( m_angleRange.IsWithinRange( value ), "指定値は範囲内である" );

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

        // ... 角度の変更イベントを発生する
        AngleChanged( this, nullptr );
    }
}


/// 範囲内で回転角に加算する
void Joint::AddAngleWithinRange( double value )
{
    double newValue = m_angle + value;

    // 回転角の範囲内に収まるように修正する
    newValue = Math::Max( newValue, m_angleRange.Minimum );
    newValue = Math::Min( newValue, m_angleRange.Maximum );

    this->Angle = Robotics::Angle( newValue );  // 回転角
}

/// 回転角を初期化する
void Joint::InitializeAngle()
{
    this->Angle = m_initialAngle;
}


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


/// インスタンスの説明を文字列で返す
System::String^ Joint::ToString()
{
    return this->Name;
}


/// 図形を生成する
void Joint::CreateFigure( Microsoft::DirectX::Direct3D::Device^ device )
{
    // 標準メッシュから図形を生成する
    Figure = DirectX::Direct3D::Mesh::Cylinder(
        device,
        0.007f, // Z軸の負の方向に向いている面の円の半径[ m ]
        0.007f, // Z軸の正の方向に向いている面の円の半径[ m ]
        0.025f, // 円柱の長さ[ m ]
        8,      // 円以外の棒の部分にあたる Z 軸に平行な分割数
        1       // 円以外の棒の部分にあたる Z 軸に垂直な分割数
        );
}

/// 描画する
void Joint::Render( Microsoft::DirectX::Direct3D::Device^ device )
{
    Debug::Assert( m_axialDirection != nullptr, "軸方向は定義されている" );

    // マテリアルを設定する
    SetupMaterial( device );


    // ワールド座標を保存する
    DirectX::Matrix worldMatrix = device->Transform->World;


    // 回転角を求める
    double rotationY = Math::Atan2( m_axialDirection->X, m_axialDirection->Z );
    double rotationX = Math::Atan2( m_axialDirection->Y, m_axialDirection->Z );

    // 回転軸の方向へ回転するための ワールド変換行列を求める
    WorldTransformOfFigure
        = DirectX::Matrix::RotationY( safe_cast< float >( rotationY ) )
        * DirectX::Matrix::RotationX( safe_cast< float >( rotationX ) )
        * worldMatrix;

    // ... それをワールド座標に設定する
    device->Transform->World = WorldTransformOfFigure;

    /// @note
    /// 初期状態でZ軸方向を向いているため、X軸とY軸回りに回転させるだけでよい。


    // レンダリングする
    Figure->DrawSubset( 0 );


    // ワールド座標を元に戻す
    device->Transform->World = worldMatrix;
}


/// 軸周りに回転する
void Joint::RotateAroundAxis( Microsoft::DirectX::Direct3D::Device^ device )
{
    Debug::Assert( m_axialDirection != nullptr, "軸方向は定義されている" );


    // 軸方向から 回転軸を生成する
    DirectX::Vector3 axis = DirectX::Vector3(
        safe_cast< float >( m_axialDirection->X ),
        safe_cast< float >( m_axialDirection->Y ),
        safe_cast< float >( m_axialDirection->Z )
        );

    // ... 軸方向を中心に ワールド座標を回転する
    device->Transform->World
        = DirectX::Matrix::RotationAxis( axis, safe_cast< float >( m_angle ) )
        * device->Transform->World;
}