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

212 lines
6.3 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.
//
// This class handles manipulation of the drawing object (rectangle) as a
// response to multi-touch gestures
//
// Initially, we define rectangle to be in the middle of the client area.
// Whenever user resizes the window the rectangle is placed in the middle of the
// client area with the width set to a half of the width of the client area and
// height set to a half of the height of the client area. The rectangle is placed
// in the center of the client area.
//
// Through the gestures user can zoom in, zoom out, move or rotate the rectangle.
// By invoking two finger tap the user can add/remove diagonals from the drawing.
//
#include "DrawingObject.h"
// This macro is used to round double and cast it to LONG
#define ROUND_DOUBLE_TO_LONG(x) ((LONG)floor(0.5 + (x)))
// This is initialization of colors that we are going to shift through the
// rectangle object whenever the user invokes press and tap gesture
const COLORREF CDrawingObject::s_colors[] =
{
RGB(0, 0, 0), // black
RGB(255, 255, 0), // yellow
RGB(255, 0, 0), // red
RGB(0, 255, 0), // green
RGB(0, 0, 255) // blue
};
// Default constructor
CDrawingObject::CDrawingObject()
{
// The main window application is responsible to invoke ResetObject function
// to initialize variables.
// It should be done whenever the main window gets WM_SIZE message.
}
// Destructor
CDrawingObject::~CDrawingObject()
{
}
// This function shifts color index by one
void CDrawingObject::ShiftColor()
{
// increase current color index by one
_iColorIndex++;
if (MAX_COLORS == _iColorIndex)
{
// if index is out of the bound then reset it to zero
_iColorIndex = 0;
}
}
// This function resets the rectangle object information and it is called by
// the main app whenever the user resizes the client area
// in:
// cxClient - new width of the client area
// cyClient - new height of the client area
void CDrawingObject::ResetObject(const int cxClient, const int cyClient)
{
// Initial positon of the rectangle is the center of the client area
_iCx = cxClient/2;
_iCy = cyClient/2;
// Initial width and height are half of the size of the client area
_iWidth = cxClient/2;
_iHeight = cyClient/2;
// Initial scaling factor is 1.0 (no scaling)
_dScalingFactor = 1.0;
// Initial rotation angle is 0.0 (no rotation)
_dRotationAngle = 0.0;
_bDrawDiagonals = false; // no drawing of the diagonals
_iColorIndex = 0; // set initial color to black
}
// This function will be called by the main app whenever WM_PAINT message is
// received. It is responsible to redraw the rectangle. Here we calculate the
// positon of the rectangle corners.
// in:
// hdc - handle to device context
void CDrawingObject::Paint(HDC hdc)
{
// create new pen with a width 1
HPEN hPen = CreatePen(PS_SOLID,1,s_colors[_iColorIndex]);
// select new pen for drawing
HGDIOBJ hPenOld = SelectObject(hdc,hPen);
// first create a polyline that describes the rectangle scaled by the
// scaling factor
POINT ptRect[5];
// upper left cofner
ptRect[0].x = -(LONG)(_dScalingFactor * _iWidth/2);
ptRect[0].y = -(LONG)(_dScalingFactor * _iHeight/2);
// upper right corner
ptRect[1].x = (LONG)(_dScalingFactor * _iWidth/2);
ptRect[1].y = ptRect[0].y;
// lower right corner
ptRect[2].x = ptRect[1].x;
ptRect[2].y = (LONG)(_dScalingFactor * _iHeight/2);
// lower left corner
ptRect[3].x = ptRect[0].x;
ptRect[3].y = ptRect[2].y;
// upper left corner, we are closing the rectangle
ptRect[4] = ptRect[0];
// now we should rotate the rectangle
double dCos = cos(_dRotationAngle);
double dSin = sin(_dRotationAngle);
for (int i = 0; i < 5; i++)
{
LONG lDX = ptRect[i].x;
LONG lDY = ptRect[i].y;
ptRect[i].x = (LONG)(lDX*dCos + lDY*dSin);
ptRect[i].y = (LONG)(lDY*dCos - lDX*dSin);
}
// finally we should translate the rectangle
for (int i = 0; i < 5; i++)
{
ptRect[i].x += _iCx;
ptRect[i].y += _iCy;
}
Polyline(hdc, ptRect, 5);
if (_bDrawDiagonals)
{
// draw diagonals
MoveToEx(hdc, ptRect[0].x, ptRect[0].y, NULL);
LineTo(hdc, ptRect[2].x, ptRect[2].y);
MoveToEx(hdc, ptRect[1].x, ptRect[1].y, NULL);
LineTo(hdc, ptRect[3].x, ptRect[3].y);
}
// select old pen
SelectObject(hdc, hPenOld);
// destroy new pen
DeleteObject(hPen);
}
// This function is responsible for translating the center of the rectangle.
// in:
// ldx - shift of the x-coordinate
// ldy - shift of the y-coordinate
void CDrawingObject::Move(LONG ldx, LONG ldy)
{
_iCx += ldx;
_iCy += ldy;
}
// This function zooms the rectanlge in/out
// in:
// dZoomFactor - scaling factor of the zoom
// iZx - x coordinate of the zoom center
// iZy - y coordinate of the zoom center
void CDrawingObject::Zoom(const double dZoomFactor, const LONG iZx, const LONG iZy)
{
_iCx = ROUND_DOUBLE_TO_LONG(iZx * (1.0-dZoomFactor) + _iCx * dZoomFactor);
_iCy = ROUND_DOUBLE_TO_LONG(iZy * (1.0-dZoomFactor) + _iCy * dZoomFactor);
_dScalingFactor *= dZoomFactor;
}
// This function rotates the rectangle
// in:
// dAngle - angle of rotation in radians
// iOx - x-coordinate of the rotation center
// iOy - y-coordinate of the rotation center
//
// Note that during the rotation gesture the user defines center of rotation.
// This is going to move the center of the rectangle too. That is why we have to
// recalculate new center of the rectangle.
void CDrawingObject::Rotate(const double dAngle, const LONG iOx, const LONG iOy)
{
// rotate center point around point O
double dCos = cos(dAngle);
double dSin = sin(dAngle);
LONG lDX = _iCx - iOx;
LONG lDY = _iCy - iOy;
_iCx = iOx + ROUND_DOUBLE_TO_LONG(lDX*dCos + lDY*dSin);
_iCy = iOy + ROUND_DOUBLE_TO_LONG(lDY*dCos - lDX*dSin);
_dRotationAngle += dAngle;
}