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

236 lines
8.4 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.
//
// DrawingObject.cs
//
// 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.
//
// DrawingObject class holds information about the rectangle. Instead of storing
// two oposite corner points we do calculate them dynamically by using the other
// information that we store in this class about the rectangle. This way we do
// not get deformation of the rectangle due to precission issues.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace Microsoft.Samples.Touch.MTGestures
{
public class DrawingObject
{
// This is an array of colors that we will shift through when enduser
// generates press and tap gesture
static private Color[] _arrColors = new Color[] {
Color.Black,
Color.Yellow,
Color.Red,
Color.Green,
Color.Blue
};
// We do retain client area rectangle
private Rectangle _rcClient;
// We do retain center point of the rectangle (diagonals intersection)
private Point _ptCenter;
// Then we keep information about size of the rectangle
private Size _szRectangle;
// Zooming in/out will scale the width and the height by some factor
private double _dScalingFactor;
// Here we store total rotation angle of the rectangle (from x-axis)
private double _dRotationAngle;
// This variable triggers drawing of diagonals if set to true
bool _bDrawDiagonals;
// This variable defines the color of the rectangle (index)
int _iColorIndex;
// Constructor
public DrawingObject()
{
// The main window form is responsible to invoke ResetObject
// method to initialize variables. This is done in
// OnSizeChanged method of MTGesturesForm class
}
// This method shifts color index by one
public void ShiftColor()
{
_iColorIndex++;
_iColorIndex %= _arrColors.Length;
}
// This method resets the rectangle object information and it is called
// by the main app whenever the user resizes the client area
// in:
// rcClient - client area rectangle
public void ResetObject(Rectangle rcClient)
{
// Save client area rectangle
_rcClient = rcClient;
// Initial positon of the rectangle is the center of the client area
_ptCenter.X = _rcClient.Width / 2;
_ptCenter.Y = _rcClient.Height / 2;
// Initial width and height are half of the size of the client area
_szRectangle.Width = _rcClient.Width / 2;
_szRectangle.Height = _rcClient.Height / 2;
// Initial scaling factor is 1.0 (no scaling)
_dScalingFactor = 1.0;
// Initial rotation angle is 0.0 (no rotation)
_dRotationAngle = 0.0; // no rotation intially
_bDrawDiagonals = false; // no drawing of the diagonals
_iColorIndex = 0; // set initial color to black
}
// This mehod will be called by the MTGesturesForm whenever OnPaint event
// handler is invoked. It is responsible to redraw the rectangle. Here we
// calculate the positon of the rectangle corners.
// in:
// graphics - Graphics object
public void Paint(Graphics graphics)
{
if (null == graphics)
{
// this code should never be executed
throw new Exception("Invalid graphic object!");
}
// create new pen
Pen p = new Pen(_arrColors[_iColorIndex]);
// first create a polyline that describes the rectangle scaled by the
// scaling factor
Point[] arrPts = new Point[5];
// upper left cofner
arrPts[0].X = -(int)(_dScalingFactor * _szRectangle.Width / 2);
arrPts[0].Y = -(int)(_dScalingFactor * _szRectangle.Height / 2);
// upper right corner
arrPts[1].X = (int)(_dScalingFactor * _szRectangle.Width / 2);
arrPts[1].Y = arrPts[0].Y;
// lower right corner
arrPts[2].X = arrPts[1].X;
arrPts[2].Y = (int)(_dScalingFactor * _szRectangle.Height / 2);
// lower left corner
arrPts[3].X = arrPts[0].X;
arrPts[3].Y = arrPts[2].Y;
// upper left corner, we are closing the rectangle
arrPts[4] = arrPts[0];
// now we should rotate and translate the rectangle
double dCos = Math.Cos(_dRotationAngle);
double dSin = Math.Sin(_dRotationAngle);
for (int j = 0; j < 5; j++)
{
int idx = arrPts[j].X;
int idy = arrPts[j].Y;
// rotation
arrPts[j].X = (int)(idx * dCos + idy * dSin);
arrPts[j].Y = (int)(idy * dCos - idx * dSin);
// translation
arrPts[j].X += _ptCenter.X;
arrPts[j].Y += _ptCenter.Y;
}
graphics.DrawLines(p, arrPts);
if(_bDrawDiagonals)
{
// draw diagonals
graphics.DrawLine(p, arrPts[0], arrPts[2]);
graphics.DrawLine(p, arrPts[1], arrPts[3]);
}
}
// This method is responsible for translating the center of the rectangle.
// in:
// deltaX - shift of the x-coordinate
// deltaY - shift of the y-coordinate
public void Move(int deltaX,int deltaY)
{
_ptCenter.X += deltaX;
_ptCenter.Y += deltaY;
}
// This method toggles the drawing of the diagonals
public void ToggleDrawDiagonals()
{
_bDrawDiagonals = !_bDrawDiagonals;
}
// This method zooms the rectangle in/out
// in:
// zoomFactor - scaling factor of the zoom
// coefficientX - x coordinate of the zoom center
// coefficientY - y coordinate of the zoom center
public void Zoom(double zoomFactor,int coefficientX,int coefficientY)
{
_ptCenter.X = RoundAndCastValue(coefficientX*(1.0-zoomFactor)+_ptCenter.X*zoomFactor);
_ptCenter.Y = RoundAndCastValue(coefficientY*(1.0-zoomFactor)+_ptCenter.Y*zoomFactor);
_dScalingFactor *= zoomFactor;
}
// This method rotates the rectangle
// in:
// angle - angle of rotation in radians
// centerX - x-coordinate of the rotation center
// centerY - 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.
public void Rotate(double angle,int centerX,int centerY)
{
double dCos = Math.Cos(angle);
double dSin = Math.Sin(angle);
int idx = _ptCenter.X - centerX;
int idy = _ptCenter.Y - centerY;
_ptCenter.X = centerX + RoundAndCastValue(idx * dCos + idy * dSin);
_ptCenter.Y = centerY + RoundAndCastValue(idy * dCos - idx * dSin);
_dRotationAngle += angle;
}
// This macro is used to round double and cast it to int
static protected int RoundAndCastValue(double number)
{
return (int)(Math.Floor(0.5 + number));
}
}
}