422 lines
10 KiB
C++
422 lines
10 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 "DrawingObject.h"
|
|
#include <math.h>
|
|
|
|
#define INITIAL_OBJ_WIDTH 200
|
|
#define INITIAL_OBJ_HEIGHT 200
|
|
#define DEFAULT_DIRECTION 0
|
|
|
|
CDrawingObject::CDrawingObject(HWND hwnd, CD2DDriver* d2dDriver) :
|
|
m_hWnd(hwnd),
|
|
m_d2dDriver(d2dDriver)
|
|
{
|
|
// Get the render target for drawing to
|
|
m_spRT = m_d2dDriver->GetRenderTarget();
|
|
}
|
|
|
|
CDrawingObject::~CDrawingObject()
|
|
{
|
|
}
|
|
|
|
// Sets the default position, dimensions and color for the drawing object
|
|
VOID CDrawingObject::ResetState(const FLOAT startX, const FLOAT startY,
|
|
const int ixClient, const int iyClient,
|
|
const int iScaledWidth, const int iScaledHeight,
|
|
const DrawingColor colorChoice)
|
|
{
|
|
// Set width and height of the client area
|
|
// must adjust for dpi aware
|
|
m_iCWidth = iScaledWidth;
|
|
m_iCHeight = iScaledHeight;
|
|
|
|
// Initialize width height of object
|
|
m_fWidth = INITIAL_OBJ_WIDTH;
|
|
m_fHeight = INITIAL_OBJ_HEIGHT;
|
|
|
|
// Set outer elastic border
|
|
UpdateBorders();
|
|
|
|
// Set the top, left starting position
|
|
|
|
// Set cooredinates given by processor
|
|
m_fXI = startX;
|
|
m_fYI = startY;
|
|
|
|
// Set coordinates used for rendering
|
|
m_fXR = startX;
|
|
m_fYR = startY;
|
|
|
|
// Set touch origin to 0
|
|
m_fOX = 0.0f;
|
|
m_fOY = 0.0f;
|
|
|
|
// Initialize scaling factor
|
|
m_fFactor = 1.0f;
|
|
|
|
// Initialize angle
|
|
m_fAngleCumulative = 0.0f;
|
|
|
|
if(m_spRT)
|
|
{
|
|
HRESULT hr;
|
|
D2D1_SIZE_U size;
|
|
size.width = ixClient;
|
|
size.height = iyClient;
|
|
hr= m_spRT->Resize(size);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
m_d2dDriver->DiscardDeviceResources();
|
|
InvalidateRect(m_hWnd, NULL, FALSE);
|
|
}
|
|
}
|
|
|
|
// Determines what brush to use for drawing this object and
|
|
// gets the brush from the D2DDriver class
|
|
|
|
switch (colorChoice){
|
|
case Blue:
|
|
m_currBrush = m_d2dDriver->get_GradBrush(CD2DDriver::GRB_Blue);
|
|
break;
|
|
case Orange:
|
|
m_currBrush = m_d2dDriver->get_GradBrush(CD2DDriver::GRB_Orange);
|
|
break;
|
|
case Green:
|
|
m_currBrush = m_d2dDriver->get_GradBrush(CD2DDriver::GRB_Green);
|
|
break;
|
|
case Red:
|
|
m_currBrush = m_d2dDriver->get_GradBrush(CD2DDriver::GRB_Red);
|
|
break;
|
|
default:
|
|
m_currBrush = m_d2dDriver->get_GradBrush(CD2DDriver::GRB_Blue);
|
|
}
|
|
}
|
|
|
|
VOID CDrawingObject::Paint()
|
|
{
|
|
if(!(m_spRT->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED))
|
|
{
|
|
FLOAT fGlOffset = 2.5f;
|
|
|
|
// Setup our matrices for performing transforms
|
|
|
|
D2D_MATRIX_3X2_F rotateMatrix;
|
|
D2D_MATRIX_3X2_F identityMatrix;
|
|
identityMatrix = D2D1::Matrix3x2F::Identity();
|
|
|
|
// Apply rotate transform
|
|
|
|
rotateMatrix = D2D1::Matrix3x2F::Rotation(
|
|
m_fAngleCumulative,
|
|
D2D1::Point2F(
|
|
m_fXR + m_fWidth/2.0f,
|
|
m_fYR + m_fHeight/2.0f
|
|
)
|
|
);
|
|
|
|
m_spRT->SetTransform(&rotateMatrix);
|
|
|
|
// Store the rotate matrix to be used in hit testing
|
|
m_lastMatrix = rotateMatrix;
|
|
|
|
// Get glossy brush
|
|
m_pGlBrush = m_d2dDriver->get_GradBrush(CD2DDriver::GRB_Glossy);
|
|
|
|
// Set positions of gradients based on the new coordinates of the objecs
|
|
|
|
m_currBrush->SetStartPoint(
|
|
D2D1::Point2F(
|
|
m_fXR,
|
|
m_fYR
|
|
)
|
|
);
|
|
|
|
m_currBrush->SetEndPoint(
|
|
D2D1::Point2F(
|
|
m_fXR,
|
|
m_fYR + m_fHeight
|
|
)
|
|
);
|
|
|
|
m_pGlBrush->SetStartPoint(
|
|
D2D1::Point2F(
|
|
m_fXR,
|
|
m_fYR
|
|
)
|
|
);
|
|
|
|
m_pGlBrush->SetEndPoint(
|
|
D2D1::Point2F(
|
|
m_fXR + m_fWidth/15.0f,
|
|
m_fYR + m_fHeight/2.0f
|
|
)
|
|
);
|
|
|
|
// Create rectangle to draw
|
|
|
|
D2D1_RECT_F rectangle = D2D1::RectF(
|
|
m_fXR,
|
|
m_fYR,
|
|
m_fXR+m_fWidth,
|
|
m_fYR+m_fHeight
|
|
);
|
|
|
|
D2D1_ROUNDED_RECT roundedRect = D2D1::RoundedRect(
|
|
rectangle,
|
|
10.0f, 10.0f
|
|
);
|
|
|
|
// Create glossy effect
|
|
|
|
D2D1_RECT_F glossyRect = D2D1::RectF(
|
|
m_fXR+fGlOffset,
|
|
m_fYR+fGlOffset,
|
|
m_fXR+m_fWidth-fGlOffset,
|
|
m_fYR+m_fHeight/2.0f
|
|
);
|
|
|
|
D2D1_ROUNDED_RECT glossyRoundedRect = D2D1::RoundedRect(
|
|
glossyRect,
|
|
10.0f,
|
|
10.0f
|
|
);
|
|
|
|
// D2D requires that a geometry is created for the rectangle
|
|
m_d2dDriver->CreateGeometryRoundedRect(
|
|
roundedRect,
|
|
&m_spRoundedRectGeometry
|
|
);
|
|
|
|
// Fill the geometry that was created
|
|
m_spRT->FillGeometry(
|
|
m_spRoundedRectGeometry,
|
|
m_currBrush
|
|
);
|
|
|
|
// Draw glossy effect
|
|
m_spRT->FillRoundedRectangle(
|
|
&glossyRoundedRect,
|
|
m_pGlBrush
|
|
);
|
|
|
|
// Restore our transform to nothing
|
|
m_spRT->SetTransform(&identityMatrix);
|
|
}
|
|
}
|
|
|
|
VOID CDrawingObject::Translate(FLOAT fdx, FLOAT fdy, BOOL bInertia)
|
|
{
|
|
m_fdX = fdx;
|
|
m_fdY = fdy;
|
|
|
|
FLOAT fOffset[2];
|
|
fOffset[0] = m_fOX - m_fdX;
|
|
fOffset[1] = m_fOY - m_fdY;
|
|
|
|
// Translate based on the offset caused by rotating
|
|
// and scaling in order to vary rotational behavior depending
|
|
// on where the manipulation started
|
|
|
|
if(m_fAngleApplied != 0.0f)
|
|
{
|
|
FLOAT v1[2];
|
|
v1[0] = GetCenterX() - fOffset[0];
|
|
v1[1] = GetCenterY() - fOffset[1];
|
|
|
|
FLOAT v2[2];
|
|
RotateVector(v1, v2, m_fAngleApplied);
|
|
|
|
m_fdX += v2[0] - v1[0];
|
|
m_fdY += v2[1] - v1[1];
|
|
}
|
|
|
|
if(m_fFactor != 1.0f)
|
|
{
|
|
FLOAT v1[2];
|
|
v1[0] = GetCenterX() - fOffset[0];
|
|
v1[1] = GetCenterY() - fOffset[1];
|
|
|
|
FLOAT v2[2];
|
|
v2[0] = v1[0] * m_fFactor;
|
|
v2[1] = v1[1] * m_fFactor;
|
|
|
|
m_fdX += v2[0] - v1[0];
|
|
m_fdY += v2[1] - v1[1];
|
|
}
|
|
|
|
m_fXI += m_fdX;
|
|
m_fYI += m_fdY;
|
|
|
|
// The following code handles the effect for
|
|
// bouncing off the edge of the screen. It takes
|
|
// the x,y coordinates computed by the inertia processor
|
|
// and calculates the appropriate render coordinates
|
|
// in order to achieve the effect.
|
|
|
|
if (bInertia)
|
|
{
|
|
ComputeElasticPoint(m_fXI, &m_fXR, m_iBorderX);
|
|
ComputeElasticPoint(m_fYI, &m_fYR, m_iBorderY);
|
|
}
|
|
else
|
|
{
|
|
m_fXR = m_fXI;
|
|
m_fYR = m_fYI;
|
|
|
|
// Make sure it stays on screen
|
|
EnsureVisible();
|
|
}
|
|
}
|
|
|
|
VOID CDrawingObject::EnsureVisible()
|
|
{
|
|
m_fXR = max(0,min(m_fXI, (FLOAT)m_iCWidth-m_fWidth));
|
|
m_fYR = max(0,min(m_fYI, (FLOAT)m_iCHeight-m_fHeight));
|
|
RestoreRealPosition();
|
|
}
|
|
|
|
VOID CDrawingObject::Scale(const FLOAT dFactor)
|
|
{
|
|
m_fFactor = dFactor;
|
|
|
|
FLOAT scaledW = (dFactor-1) * m_fWidth;
|
|
FLOAT scaledH = (dFactor-1) * m_fHeight;
|
|
FLOAT scaledX = scaledW/2.0f;
|
|
FLOAT scaledY = scaledH/2.0f;
|
|
|
|
m_fXI -= scaledX;
|
|
m_fYI -= scaledY;
|
|
|
|
m_fWidth += scaledW;
|
|
m_fHeight += scaledH;
|
|
|
|
// Only limit scaling in the case that the factor is not 1.0
|
|
|
|
if(dFactor != 1.0f)
|
|
{
|
|
m_fXI = max(0, m_fXI);
|
|
m_fYI = max(0, m_fYI);
|
|
|
|
m_fWidth = min(min(m_iCWidth, m_iCHeight), m_fWidth);
|
|
m_fHeight = min(min(m_iCWidth, m_iCHeight), m_fHeight);
|
|
}
|
|
|
|
// Readjust borders for the objects new size
|
|
UpdateBorders();
|
|
}
|
|
|
|
VOID CDrawingObject::Rotate(const FLOAT fAngle)
|
|
{
|
|
m_fAngleCumulative += fAngle;
|
|
m_fAngleApplied = fAngle;
|
|
}
|
|
|
|
VOID CDrawingObject::SetManipulationOrigin(FLOAT x, FLOAT y)
|
|
{
|
|
m_fOX = x;
|
|
m_fOY = y;
|
|
}
|
|
|
|
// Helper method that rotates a vector using basic math transforms
|
|
VOID CDrawingObject::RotateVector(FLOAT *vector, FLOAT *tVector, FLOAT fAngle)
|
|
{
|
|
FLOAT fAngleRads = fAngle / 180.0f * 3.14159f;
|
|
FLOAT fSin = sin(fAngleRads);
|
|
FLOAT fCos = cos(fAngleRads);
|
|
|
|
FLOAT fNewX = (vector[0]*fCos) - (vector[1]*fSin);
|
|
FLOAT fNewY = (vector[0]*fSin) + (vector[1]*fCos);
|
|
|
|
tVector[0] = fNewX;
|
|
tVector[1] = fNewY;
|
|
}
|
|
|
|
FLOAT CDrawingObject::GetPosY()
|
|
{
|
|
return m_fYI;
|
|
}
|
|
|
|
FLOAT CDrawingObject::GetPosX()
|
|
{
|
|
return m_fXI;
|
|
}
|
|
|
|
FLOAT CDrawingObject::GetWidth()
|
|
{
|
|
return m_fWidth;
|
|
}
|
|
|
|
FLOAT CDrawingObject::GetHeight()
|
|
{
|
|
return m_fHeight;
|
|
}
|
|
|
|
FLOAT CDrawingObject::GetCenterX()
|
|
{
|
|
return m_fXI + m_fWidth/2.0f;
|
|
}
|
|
|
|
FLOAT CDrawingObject::GetCenterY()
|
|
{
|
|
return m_fYI + m_fHeight/2.0f;
|
|
}
|
|
|
|
// Hit testing method handled with Direct2D
|
|
BOOL CDrawingObject::InRegion(LONG x, LONG y)
|
|
{
|
|
BOOL b = FALSE;
|
|
|
|
m_spRoundedRectGeometry->FillContainsPoint(
|
|
D2D1::Point2F((FLOAT)x, (FLOAT)y),
|
|
&m_lastMatrix,
|
|
&b
|
|
);
|
|
return b;
|
|
}
|
|
|
|
// Sets the internal coordinates to render coordinates
|
|
VOID CDrawingObject::RestoreRealPosition()
|
|
{
|
|
m_fXI = m_fXR;
|
|
m_fYI = m_fYR;
|
|
}
|
|
|
|
VOID CDrawingObject::UpdateBorders()
|
|
{
|
|
m_iBorderX = m_iCWidth - (int)m_fWidth;
|
|
m_iBorderY = m_iCHeight - (int)m_fHeight;
|
|
}
|
|
|
|
// Computes the the elastic point and sets the render coordinates
|
|
VOID CDrawingObject::ComputeElasticPoint(FLOAT fIPt, FLOAT *fRPt, int iBSize)
|
|
{
|
|
// If the border size is 0 then do not attempt
|
|
// to calculate the render point for elasticity
|
|
if(iBSize == 0)
|
|
return;
|
|
|
|
// Calculate render coordinate for elastic border effect
|
|
|
|
// Divide the cumulative translation vector by the max border size
|
|
int q = (int)abs(fIPt) / iBSize;
|
|
int direction = q % 2;
|
|
|
|
// Calculate the remainder this is the new render coordinate
|
|
FLOAT newPt = abs(fIPt) - (FLOAT)(iBSize*q);
|
|
|
|
if (direction == DEFAULT_DIRECTION)
|
|
{
|
|
*fRPt = newPt;
|
|
}
|
|
else
|
|
{
|
|
*fRPt = (FLOAT)iBSize - newPt;
|
|
}
|
|
}
|