2025-11-28 00:35:46 +09:00

312 lines
8.7 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#include "LayoutManager.h"
#include "UIAnimationSample.h"
#include "CustomInterpolator.h"
#include <math.h>
CLayoutManager::CLayoutManager() :
m_pAnimationManager(NULL),
m_pAnimationTimer(NULL),
m_pTransitionLibrary(NULL),
m_pTransitionFactory(NULL),
m_uThumbCount(0),
m_thumbs(NULL)
{
}
CLayoutManager::~CLayoutManager()
{
// Animation
SafeRelease(&m_pAnimationManager);
SafeRelease(&m_pAnimationTimer);
SafeRelease(&m_pTransitionLibrary);
SafeRelease(&m_pTransitionFactory);
// Do not delete m_thumbs, as client owns that
}
// Initialization
HRESULT CLayoutManager::Initialize(
IUIAnimationManager *pAnimationManager,
IUIAnimationTimer *pAnimationTimer,
IUIAnimationTransitionLibrary *pTransitionLibrary,
IUIAnimationTransitionFactory *pTransitionFactory,
UINT uThumbCount,
CThumbnail *thumbs
)
{
HRESULT hr = S_OK;
m_pAnimationManager = pAnimationManager;
m_pAnimationManager->AddRef();
m_pAnimationTimer = pAnimationTimer;
m_pAnimationTimer->AddRef();
m_pTransitionLibrary = pTransitionLibrary;
m_pTransitionLibrary->AddRef();
m_pTransitionFactory = pTransitionFactory;
m_pTransitionFactory->AddRef();
// Store a pointer to thumbnail array
m_uThumbCount = uThumbCount;
m_thumbs = thumbs;
return hr;
}
// Arranges the thumbnails randomly
HRESULT CLayoutManager::Arrange(
D2D1_SIZE_F sizeClient
)
{
// Create storyboard for all the thumbnail transitions
IUIAnimationStoryboard *pStoryboard;
HRESULT hr = m_pAnimationManager->CreateStoryboard(
&pStoryboard
);
if (SUCCEEDED(hr))
{
// Arrange the thumbnails, adding transitions to move each thumbnail to a random new location
for (UINT i = 0; i < m_uThumbCount; i++)
{
D2D1_SIZE_F size = m_thumbs[i].GetSize();
DOUBLE xDest = RandomFromRange(
size.width * 0.5,
sizeClient.width - size.width * 0.5
);
DOUBLE yDest = RandomFromRange(
sizeClient.height * 0.25 + size.height * 0.5,
sizeClient.height * 0.75 - size.height * 0.5
);
// Get the current position
// Note that this technique is valid only when the storyboard will begin playing immediately
DOUBLE xCur;
hr = m_thumbs[i].m_pAnimationVariableX->GetValue(&xCur);
if (SUCCEEDED(hr))
{
DOUBLE yCur;
hr = m_thumbs[i].m_pAnimationVariableY->GetValue(&yCur);
if (SUCCEEDED(hr))
{
// Add transitions for x and y movement
if (fabs(xDest - xCur) > fabs(yDest - yCur))
{
// If the thumbnail has further to travel horizontally than vertically, use a parabolic transition
// on X that will determine the duration of the storyboard, and stretch an accelerate-decelerate
// transition on Y to have the same duration.
hr = AddThumbnailTransitions(
pStoryboard,
m_thumbs[i].m_pAnimationVariableX,
xDest,
m_thumbs[i].m_pAnimationVariableY,
yDest
);
}
else
{
// If the thumbnail has further to travel vertically than horizontally, use a parabolic transition
// on Y that will determine the duration of the storyboard, and stretch an accelerate-decelerate
// transition on X to have the same duration.
hr = AddThumbnailTransitions(
pStoryboard,
m_thumbs[i].m_pAnimationVariableY,
yDest,
m_thumbs[i].m_pAnimationVariableX,
xDest
);
}
}
}
if (FAILED(hr))
{
break;
}
}
if (SUCCEEDED(hr))
{
hr = ScheduleStoryboard(pStoryboard);
}
pStoryboard->Release();
}
return hr;
}
// Animates all y values of the thumbnails to a final value using the same acceleration
HRESULT CLayoutManager::Attract(
DOUBLE finalYValue
)
{
const DOUBLE ACCELERATION = 2500.0;
// Create storyboard for all the thumbnail transitions
IUIAnimationStoryboard *pStoryboard;
HRESULT hr = m_pAnimationManager->CreateStoryboard(
&pStoryboard
);
if (SUCCEEDED(hr))
{
// Add transitions to move each thumbnail to the final y value
for (UINT i = 0; i < m_uThumbCount; i++)
{
// Compute an offset to align the thumbnail's edge with the final value,
// rather than its center
D2D1_SIZE_F size = m_thumbs[i].GetSize();
DOUBLE offset = (finalYValue > 0.0 ? -1.0 : 1.0) * size.height * 0.5;
IUIAnimationTransition *pTransition;
hr = CAttractInterpolator::CreateTransition(
m_pTransitionFactory,
finalYValue + offset,
ACCELERATION,
&pTransition
);
if (SUCCEEDED(hr))
{
hr = pStoryboard->AddTransition(
m_thumbs[i].m_pAnimationVariableY,
pTransition
);
pTransition->Release();
}
if (FAILED(hr))
{
break;
}
}
if (SUCCEEDED(hr))
{
hr = ScheduleStoryboard(pStoryboard);
}
pStoryboard->Release();
}
return hr;
}
// Adds two transitions to a storyboard: one primary parabolic transition, which will determine
// the storyboard duration, and a secondary accelerate-decelerate transition, which will be
// stretched to that duration.
HRESULT CLayoutManager::AddThumbnailTransitions(
IUIAnimationStoryboard *pStoryboard,
IUIAnimationVariable *pVariablePrimary,
DOUBLE valuePrimary,
IUIAnimationVariable *pVariableSecondary,
DOUBLE valueSecondary
)
{
const DOUBLE ACCELERATION = 2000;
const DOUBLE ACCELERATION_RATIO = 0.3;
const DOUBLE DECELERATION_RATIO = 0.3;
IUIAnimationTransition *pTransitionPrimary;
HRESULT hr = m_pTransitionLibrary->CreateParabolicTransitionFromAcceleration(
valuePrimary,
0.0,
ACCELERATION,
&pTransitionPrimary
);
if (SUCCEEDED(hr))
{
hr = pStoryboard->AddTransition(
pVariablePrimary,
pTransitionPrimary
);
if (SUCCEEDED(hr))
{
UI_ANIMATION_KEYFRAME keyframeEnd;
hr = pStoryboard->AddKeyframeAfterTransition(
pTransitionPrimary,
&keyframeEnd
);
if (SUCCEEDED(hr))
{
IUIAnimationTransition *pTransitionSecondary;
hr = m_pTransitionLibrary->CreateAccelerateDecelerateTransition(
1.0, // Will be overwritten, so unimportant
valueSecondary,
ACCELERATION_RATIO,
DECELERATION_RATIO,
&pTransitionSecondary
);
if (SUCCEEDED(hr))
{
hr = pStoryboard->AddTransitionBetweenKeyframes(
pVariableSecondary,
pTransitionSecondary,
UI_ANIMATION_KEYFRAME_STORYBOARD_START,
keyframeEnd
);
pTransitionSecondary->Release();
}
}
}
pTransitionPrimary->Release();
}
return hr;
}
// Gets the current time and schedules a storyboard for play
HRESULT CLayoutManager::ScheduleStoryboard(
IUIAnimationStoryboard *pStoryboard
)
{
UI_ANIMATION_SECONDS secondsNow;
HRESULT hr = m_pAnimationTimer->GetTime(
&secondsNow
);
if (SUCCEEDED(hr))
{
hr = pStoryboard->Schedule(
secondsNow
);
}
return hr;
}
// Returns a random value between the given minimum and maximum
DOUBLE CLayoutManager::RandomFromRange(
DOUBLE minimum,
DOUBLE maximum
)
{
return minimum + (maximum - minimum) * rand() / RAND_MAX;
}