ModelPanel.cpp

#include "StdAfx.h"
#include "ModelPanel.h"


#include "Model.h"
#include "ModelFactory.h"

#include "Link.h"
#include "Joint.h"

#include "Draw.h"
#include "DrawModel.h"


using namespace Core;
using namespace System;
using namespace System::Diagnostics;


/// [ Constructor ]
ModelPanel::ModelPanel( void )
{
    // コンポーネントを初期化する
    InitializeComponent();


    // モデルの生成を行うメソッドを生成する
    ModelFactory^ factory = gcnew ModelFactory();

    // ... モデルを生成する
    m_model = factory->Create( "ModelData.xml" );

    /// @note 作業フォルダから読み込んでいる。
}

/// [ Destructor ]
ModelPanel::~ModelPanel()
{
    if( components != nullptr )
    {
        delete components;
    }
}


#pragma region Windows Form Designer generated code
/// <summary>
/// デザイナ サポートに必要なメソッドです。このメソッドの内容を
/// コード エディタで変更しないでください。
/// </summary>
void ModelPanel::InitializeComponent(void)
{
    this->m_drawPanel = (gcnew System::Windows::Forms::Panel());
    this->SuspendLayout();
    //
    // m_drawPanel
    //
    this->m_drawPanel->BackColor = System::Drawing::SystemColors::Control;
    this->m_drawPanel->Dock = System::Windows::Forms::DockStyle::Fill;
    this->m_drawPanel->Location = System::Drawing::Point(0, 0);
    this->m_drawPanel->Name = L"m_drawPanel";
    this->m_drawPanel->Size = System::Drawing::Size(292, 266);
    this->m_drawPanel->TabIndex = 0;
    this->m_drawPanel->MouseWheel += gcnew System::Windows::Forms::MouseEventHandler(this, &ModelPanel::PanelMouseWheel);
    this->m_drawPanel->MouseDown += gcnew System::Windows::Forms::MouseEventHandler(this, &ModelPanel::PanelMouseDown);
    this->m_drawPanel->MouseMove += gcnew System::Windows::Forms::MouseEventHandler(this, &ModelPanel::PanelMouseMove);
    this->m_drawPanel->MouseDoubleClick += gcnew System::Windows::Forms::MouseEventHandler(this, &ModelPanel::PanelMouseDoubleClick);
    this->m_drawPanel->Resize += gcnew System::EventHandler(this, &ModelPanel::PanelResize);
    this->m_drawPanel->Paint += gcnew System::Windows::Forms::PaintEventHandler(this, &ModelPanel::PanelPaint);
    //
    // ModelPanel
    //
    this->AllowEndUserDocking = false;
    this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
    this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
    this->ClientSize = System::Drawing::Size(292, 266);
    this->CloseButton = false;
    this->Controls->Add(this->m_drawPanel);
    this->DockAreas = WeifenLuo::WinFormsUI::Docking::DockAreas::Document;
    this->Name = L"ModelPanel";
    this->TabText = L"Model";
    this->Text = L"Model";
    this->ResumeLayout(false);

}
#pragma endregion


/// フォームが初めて表示される
void ModelPanel::OnLoad( System::EventArgs^ e )
{
    // 基本クラスの同一関数を呼び出す
    WeifenLuo::WinFormsUI::Docking::DockContent::OnLoad( e );


    // 描画クラスを生成する
    m_draw = gcnew Draw();

    // ... 初期化する
    if( !m_draw->Initialize( m_drawPanel, m_model ) )
    {
        Debug::Fail( "描画クラスの初期化に失敗した" );

        /// @todo
        /// エラー情報を表示して、アプリケーションを終了させる。
    }
    // 表示モードを初期化する
    m_draw->Model->ExpressionMode = ExpressionModel::RealModel;

    m_draw->RotateViewpoint( -0.5f, 1.2f ); // 視点回転角 (値は適当)
    m_draw->Scale = 45.0f;                  // 縮尺 (値は適当)

    // @todo ここで視点の位置も設定するようにする。


    // 再描画イベントのハンドラを生成する
    System::EventHandler^ eventHandler = gcnew System::EventHandler( this, &ModelPanel::Redraw );

    // ... それを登録する
    m_model->StateChanged += eventHandler;    // モデルの状態変化イベント
    Link::StandardChanged += eventHandler;    // 基準リンクの変更イベント
    Link::MechanicsChanged += eventHandler;   // リンクの構造変化イベント
    Joint::MechanicsChanged += eventHandler;  // 関節の構造変化イベント
}


/// 再描画する
System::Void ModelPanel::PanelPaint( System::Object^, System::Windows::Forms::PaintEventArgs^ )
{
    Redraw( this, nullptr );
}

/// 再描画する
void ModelPanel::Redraw( System::Object^, System::EventArgs^ )
{
    m_draw->Render();
}


/// サイズが変更された
System::Void ModelPanel::PanelResize( System::Object^, System::EventArgs^ )
{
    /// @note この処理は不要と思われる。

/*
    if( m_draw == nullptr )
    {
        // 描画クラスが生成されていないならば、処理を中断する。
        // (フォームの生成時、Loadイベント発生前に Resizeイベントが発生することへの対処)
        return;
    }

    m_draw->SetupScreenSize( Width, Height );   // 画面サイズ

    // ... 再描画する
    Redraw( this, nullptr );
*/
}


/// マウスがクリックされた
System::Void ModelPanel::PanelMouseDown( System::Object^, System::Windows::Forms::MouseEventArgs^ e )
{
    switch( e->Button )
    {
        case Windows::Forms::MouseButtons::Left:

            // 左ボタンが押されたならば、オブジェクトを選択する
            SelecetObject( e->Location );
            break;

        case Windows::Forms::MouseButtons::Right:
        case Windows::Forms::MouseButtons::Middle:

            // 右ボタンまたは中央ボタンが押されたならば、位置を保存する
            m_mousePosition = e->Location;
            break;
    }
}

/// マウスがダブルクリックされた
System::Void ModelPanel::PanelMouseDoubleClick( System::Object^, System::Windows::Forms::MouseEventArgs^ e )
{
    switch( e->Button )
    {
        case Windows::Forms::MouseButtons::Left:    // 左ボタン

            // 選択されているオブジェクトを接地させる
            m_model->LinkGrounded( m_draw->Model->SelectedLink );
            break;

        case Windows::Forms::MouseButtons::Right:   // 右ボタン

            // モデルの図形を再構築する
            m_draw->Model->CreateModelFigure();

            // ... 表示モードを再設定する
            m_draw->Model->ExpressionMode = m_draw->Model->ExpressionMode;

            break;
    }
}


/// マウスポインタが移動した
System::Void ModelPanel::PanelMouseMove( System::Object^, System::Windows::Forms::MouseEventArgs^ e )
{
    switch( e->Button )
    {
        case Windows::Forms::MouseButtons::Right:

            // 右ボタンが押されているならば、視点を回転させる
            RotateViewpoint( e->Location );
            break;

        case Windows::Forms::MouseButtons::Middle:

            // 中央ボタンが押されているならば、視点を移動する
            TranslateViewpoint( e->Location );
            break;
    }

    if( e->Button == ( Windows::Forms::MouseButtons::Right | Windows::Forms::MouseButtons::Left ) )
    {
        /// 左右ボタンが両方とも押されているならば、視点を移動する
        TranslateViewpoint( e->Location );

        /// @note
        /// 同時押しは switch文で処理できないため、分けて記述している。
    }
}

/// マウスホイールが動いた
System::Void ModelPanel::PanelMouseWheel( System::Object^, System::Windows::Forms::MouseEventArgs^ e )
{
    const float ScallCoefficient = 0.03f;           // 縮尺の補正係数
    m_draw->Scale += e->Delta * ScallCoefficient;   // 縮尺
}


/// 視点を回転させる
void ModelPanel::RotateViewpoint( System::Drawing::Point location )
{
    // ドラッグの移動量を求める
    Drawing::Point distanceOfDrag = location - Drawing::Size( m_mousePosition );

    // ... ドラッグの開始位置を保存する
    m_mousePosition = location;


    const float TurnCoefficient = -0.005f;  // 回転角度の補正係数

    // 視点を回転させる
    m_draw->RotateViewpoint(
        distanceOfDrag.X * TurnCoefficient,
        distanceOfDrag.Y * TurnCoefficient
        );
}

/// 視点を移動させる
void ModelPanel::TranslateViewpoint( System::Drawing::Point location )
{
    // ドラッグの移動量を求める
    Drawing::Point distanceOfDrag = location - Drawing::Size( m_mousePosition );

    // ... ドラッグの開始位置を保存する
    m_mousePosition = location;


    const float MoveCoefficient = 0.001f;  // 移動量の補正係数

    // 視点を移動させる
    m_draw->TranslateViewpoint(
        distanceOfDrag.X * MoveCoefficient,
        distanceOfDrag.Y * MoveCoefficient
        );
}


/// オブジェクトを選択する
void ModelPanel::SelecetObject( System::Drawing::Point location )
{
    if( m_draw->SelectObject( location ) )
    {
        // 選択が変更されたならば、選択されたオブジェクトを取得する
        Distinction^ selected = m_draw->Model->SelectedLink;

        // ... 選択イベントを発生する
        Selected( this, gcnew SelectEventArgs( selected ) );
    }
}


/// 表示モードを取得する [ Delegate ]
ExpressionModel ModelPanel::ExpressionMode::get()
{
    return m_draw->Model->ExpressionMode;
}

/// 表示モードを切り替える
void ModelPanel::ChangeExpressionMode()
{
    // 現在の設定とは異なるものに設定する
    m_draw->Model->ExpressionMode =
        ( m_draw->Model->ExpressionMode == ExpressionModel::MechanismModel )?
        ExpressionModel::RealModel :
        ExpressionModel::MechanismModel;

    // 再描画する
    Redraw( this, nullptr );
}