アンマネージド コードでの慣性の処理

このセクションでは、アンマネージ コードで慣性を処理するために IInertiaProcessor インターフェイスを使用する方法について説明します。

概要

アンマネージ コードで慣性を使用するには、操作プロセッサと慣性プロセッサの両方にイベント シンクを実装する必要があります。 まず、「アンマネージド コードへの操作のサポートの追加」セクションの説明に従 って、アプリケーションに操作サポートを追加します。 操作のサポートでは、ジェスチャ メッセージではなくタッチ メッセージを使用して、操作プロセッサにイベント データをフィードする必要があることに注意してください。 操作が機能したら、 IInertiaProcessor インターフェイスによって生成されるイベントの 2 つ目のイベント シンクも実装する必要があります。また、 IInertiaProcessor インターフェイスと IManipulationProcessor インターフェイスによって生成されるイベントの両方に対応するように、既存のイベント シンクを変更する必要があります。 この例では、「アンマネージド コードへの操作サポートの追加」セクションで作成したイベント シンクから開始し、操作プロセッサではなく慣性プロセッサで動作する 2 つ目のコンストラクターを追加する方が簡単です。 これにより、イベント シンクの実装は、操作プロセッサまたは慣性プロセッサに対して機能できます。 2 つ目のコンストラクターを追加するだけでなく、イベント シンクには、操作入力ではなく慣性入力に基づいて操作を実行するかどうかを示す変数が含まれます。

操作プロセッサ イベント シンクに慣性サポートを追加する

次のコードは、新しいイベント シンク コンストラクター、 IInertiaProcessor インターフェイスの新しいメンバー変数、およびシンクが慣性のために外挿されているかどうかを示すフラグを示しています。

    CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd);
    CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd);
    IInertiaProcessor*      m_pInert;
    BOOL fExtrapolating; 

クラス ヘッダーに新しいコンストラクターと、外挿しているかどうかを示すフラグが設定されたら、イベント シンクを実装して、 IManipulationProcessor イベントと IInertiaProcessor イベント用に個別の処理ブロックを設定できます。 IManipulationProcessorIInertiaProcessor を受け取るコンストラクターは、fExtrapolating フラグを false に設定する必要があります。これは、これが IManipulationProcessor イベント ハンドラーであることを示します。 次のコードは、 IManipulationProcessor を使用するイベント シンクのコンストラクターを実装する方法を示しています。

CManipulationEventSink::CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd)
{
    m_hWnd = hWnd;

    //Set initial ref count to 1.
    m_cRefCount = 1;

    fExtrapolating=FALSE;

    m_pManip = pManip;
    
    m_pInert = pInert;
    
    m_pManip->put_PivotRadius(-1);

    m_cStartedEventCount = 0;
    m_cDeltaEventCount = 0;
    m_cCompletedEventCount = 0;

    HRESULT hr = S_OK;

    //Get the container with the connection points.
    IConnectionPointContainer* spConnectionContainer;
    
    hr = pManip->QueryInterface(
      IID_IConnectionPointContainer, 
          (LPVOID*) &spConnectionContainer
        );
    //hr = manip->QueryInterface(&spConnectionContainer);
    if (spConnectionContainer == NULL){
        // something went wrong, try to gracefully quit
        
    }

    //Get a connection point.
    hr = spConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnPoint);
    if (m_pConnPoint == NULL){
        // something went wrong, try to gracefully quit
    }

    DWORD dwCookie;

    //Advise.
    hr = m_pConnPoint->Advise(this, &dwCookie);
}

次のコードは、 IInertiaProcessor を使用するイベント シンクのコンストラクターを実装する方法を示しています。 このコンストラクターは 、fExtrapolating フラグを true に設定し、イベント シンク クラスのこのインスタンスが外挿を実行し、操作プロセッサ イベントによって以前に実行されたすべての移動操作を実行することを示します。

CManipulationEventSink::CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd)
{
    m_hWnd = hWnd;

    m_pInert = pInert;
    //Set initial ref count to 1.
    m_cRefCount = 1;

    fExtrapolating=TRUE;

    m_cStartedEventCount = 0;
    m_cDeltaEventCount = 0;
    m_cCompletedEventCount = 0;

    HRESULT hr = S_OK;

    //Get the container with the connection points.
    IConnectionPointContainer* spConnectionContainer;
    
    hr = pInert->QueryInterface(
      IID_IConnectionPointContainer, 
          (LPVOID*) &spConnectionContainer
        );
    //hr = manip->QueryInterface(&spConnectionContainer);
    if (spConnectionContainer == NULL){
        // something went wrong, try to gracefully quit        
    }

    //Get a connection point.
    hr = spConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnPoint);
    if (m_pConnPoint == NULL){
        // something went wrong, try to gracefully quit
    }
    DWORD dwCookie;

    //Advise.
    hr = m_pConnPoint->Advise(this, &dwCookie);
}   

注意

操作プロセッサ イベント シンクからのイベント シンク クラスの実装は、慣性プロセッサのイベント シンクとして再利用されます。

 

このクラス CManipulationEventSink を構築すると、操作プロセッサのイベント シンクとして、または慣性プロセッサのイベント シンクとして構築できます。 慣性プロセッサ イベント シンクとして構築されると、 fExtrapolating フラグが true に設定され、操作イベントを外挿する必要があることを示します。

注意

ManipulationStarted は、 IManipulationProcessor インターフェイスと IInertiaProcessor インターフェイスの両方によって発生します。

 

操作が開始されると、 IInertiaProcessor インターフェイス プロパティが設定されます。 次のコードは、開始イベントの処理方法を示しています。

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationStarted( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y)
{
    m_cStartedEventCount ++;       

    // set origins in manipulation processor
    m_pInert->put_InitialOriginX(x);
    m_pInert->put_InitialOriginY(y);
    
    RECT screenRect;

    HWND desktop = GetDesktopWindow();
    GetClientRect(desktop, &screenRect);

    // physics settings
    // deceleration is units per square millisecond
    m_pInert->put_DesiredDeceleration(.1f);

    // set the boundaries        
    screenRect.left-= 1024;
    m_pInert->put_BoundaryLeft  ( static_cast<float>(screenRect.left   * 100));
    m_pInert->put_BoundaryTop   ( static_cast<float>(screenRect.top    * 100));
    m_pInert->put_BoundaryRight ( static_cast<float>(screenRect.right  * 100));
    m_pInert->put_BoundaryBottom( static_cast<float>(screenRect.bottom * 100));
    
    
    // Elastic boundaries - I set these to 90% of the screen 
    // so... 5% at left, 95% right, 5% top,  95% bottom
    // Values are whole numbers because units are in centipixels
    m_pInert->put_ElasticMarginLeft  (static_cast<float>(screenRect.left   * 5));
    m_pInert->put_ElasticMarginTop   (static_cast<float>(screenRect.top    * 5));
    m_pInert->put_ElasticMarginRight (static_cast<float>(screenRect.right  * 95));
    m_pInert->put_ElasticMarginBottom(static_cast<float>(screenRect.bottom * 95));
    
    
    return S_OK;
}

この例では、操作デルタを使用してウィンドウを移動します。 次のコードは、デルタ イベントの処理方法を示しています。

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationDelta( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y,
    /* [in] */ FLOAT translationDeltaX,
    /* [in] */ FLOAT translationDeltaY,
    /* [in] */ FLOAT scaleDelta,
    /* [in] */ FLOAT expansionDelta,
    /* [in] */ FLOAT rotationDelta,
    /* [in] */ FLOAT cumulativeTranslationX,
    /* [in] */ FLOAT cumulativeTranslationY,
    /* [in] */ FLOAT cumulativeScale,
    /* [in] */ FLOAT cumulativeExpansion,
    /* [in] */ FLOAT cumulativeRotation)
{
    m_cDeltaEventCount ++;
        
    RECT rect;
            
    GetWindowRect(m_hWnd, &rect);
        
    int oldWidth =  rect.right-rect.left;
    int oldHeight = rect.bottom-rect.top;            

    // scale and translate the window size / position    
    MoveWindow(m_hWnd,                                              // the window to move
        static_cast<int>(rect.left + (translationDeltaX / 100.0f)), // the x position
        static_cast<int>(rect.top + (translationDeltaY/100.0f)),    // the y position
        static_cast<int>(oldWidth * scaleDelta),                    // width
        static_cast<int>(oldHeight * scaleDelta),                   // height
        TRUE);                                                      // redraw
                     
    return S_OK;
}

この例では、操作が完了したイベントは、IInertiaProcessor インターフェイスで Process を呼び出すタイマーを開始または停止します。 次のコードは、完了した操作イベントの処理方法を示しています。

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationCompleted( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y,
    /* [in] */ FLOAT cumulativeTranslationX,
    /* [in] */ FLOAT cumulativeTranslationY,
    /* [in] */ FLOAT cumulativeScale,
    /* [in] */ FLOAT cumulativeExpansion,
    /* [in] */ FLOAT cumulativeRotation)
{
    m_cCompletedEventCount ++;

    m_fX = x;
    m_fY = y;

    // place your code handler here to do any operations based on the manipulation   
    
    if (fExtrapolating){
        //Inertia Complete, stop the timer used for processing      
        KillTimer(m_hWnd,0);        
    }else{ 
        // setup velocities for inertia processor
        float vX = 0.0f;
        float vY = 0.0f;
        float vA = 0.0f;
        m_pManip->GetVelocityX(&vX);
        m_pManip->GetVelocityY(&vY);
        m_pManip->GetAngularVelocity(&vA);

        // complete any previous processing
        m_pInert->Complete();
        
        // Reset sets the  initial timestamp
        m_pInert->Reset();
                
        // 
        m_pInert->put_InitialVelocityX(vX);
        m_pInert->put_InitialVelocityY(vY);        
        
        m_pInert->put_InitialOriginX(x);
        m_pInert->put_InitialOriginY(y);
        
           
        // Start a timer
        SetTimer(m_hWnd,0, 50, 0);        
    }

    return S_OK;
}

次のコードは、WndProcWM_TIMERメッセージを解釈して、IInertiaProcessor インターフェイスで Process の呼び出しを実行する方法を示しています。

case WM_TIMER:       
  if (g_pIInertProc){
    BOOL b;       
    g_pIInertProc->Process(&b);        
  }
  break;

慣性プロセッサと操作プロセッサを CoInitialize し、イベント シンクを初期化する

IManipulationProcessorIInertiaProcessor の両方をサポートするようにイベント シンクを変更したら、イベント シンクを初期化し、アプリケーションから実行するように設定する準備が整います。 次のコードは、インターフェイス ポインターの割り当て方法を示しています。

//Include windows.h for touch events
#include "windows.h"  

// Manipulation implementation file
#include <manipulations_i.c>
    
// Smart Pointer to a global reference of a manipulation processor, event sink
IManipulationProcessor* g_pIManipProc;
IInertiaProcessor*      g_pIInertProc;

次のコード例は、インターフェイスをインスタンス化する方法を示しています。

   HRESULT hr = CoInitialize(0);
        
   hr = CoCreateInstance(CLSID_ManipulationProcessor,
       NULL,
       CLSCTX_INPROC_SERVER,
       IID_IUnknown,
       (VOID**)(&g_pIManipProc)
   );
   
   hr = CoCreateInstance(CLSID_InertiaProcessor,
       NULL,
       CLSCTX_INPROC_SERVER,
       IID_IUnknown,
       (VOID**)(&g_pIInertProc)
   );

次のコード例は、インターフェイス ポインターを指定してイベント シンクを構築し、タッチ入力用にウィンドウを登録する方法を示しています。

   g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, g_pIInertProc, hWnd);
   g_pManipulationEventSink = new CManipulationEventSink(g_pIInertProc, hWnd);


   RegisterTouchWindow(hWnd, 0);  

慣性