//---------------------------------------------------------------------------- // ObjectWindows - (C) Copyright 1991, 1993 by Borland International // MoveToLineTo demo window //---------------------------------------------------------------------------- #include #include #include "line.h" #include DEFINE_RESPONSE_TABLE1(TMoveToLineToWindow, TBaseDemoWindow) EV_WM_SIZE, END_RESPONSE_TABLE; IMPLEMENT_CASTABLE1(TMoveToLineToWindow, TBaseDemoWindow); TMoveToLineToWindow::TMoveToLineToWindow() : TBaseDemoWindow() { Rotation = 0; PointCount = MaxPoints; Iconized = FALSE; RotatePoints(); } void TMoveToLineToWindow::EvSize(UINT sizeType, TSize& size) { TBaseDemoWindow::EvSize(sizeType, size); Invalidate(); if (sizeType == SIZE_MINIMIZED) { if (!Iconized) { Rotation = 0; Iconized = TRUE; PointCount = IconicPoints; RotatePoints(); } } else { if (Iconized) { Rotation = 0; Iconized = FALSE; PointCount = MaxPoints; RotatePoints(); } } } void TMoveToLineToWindow::TimerTick() { RotatePoints(); if (Iconized) { // Iconized windows don't process paint messages, so we'll manually // update the image here. Doing this painting during the timer tick // will slow things down a bit, especially with several of these // windows iconized at the same time. // Paint(TClientDC(*Parent), FALSE, TRect()); } else { // In terms of Windows resources and system wide performance, letting // paint do the work is 'faster' because it reduces the CPU time spent // handling each timer tick. Paint messages are low priority, so other // messages like mouse clicks and other user input get processed first. // The downside is that the paint messages are handled last, when // there's nothing else to do, which can make animation look a bit jerky // on a busy machine. // Invalidate(FALSE); // Let the Paint method draw the new figure... } } void TMoveToLineToWindow::RotatePoints() { // NOTE: all figures are in radians const float M_2PI = 2 * M_PI; // 2 pi radians in a circle float StepAngle = M_2PI / PointCount; // angular distance between points Rotation += M_PI / 32; // Increment the angle of rotation of figure if (Rotation > StepAngle) Rotation -= StepAngle; // Keep rotation less than distance between points // The loop below has i walking through the Points array, while j walks // simultaneously through the angles to each point on the circle. // Incrementing j by StepAngle moves j to the next point on the circle with // no complicated arithmetic (everything has been set up in advance of the // loop). Initializing j with Rotation causes the entire figure to shift // clockwise a small amount. int i; float j; for (i = 0, j = Rotation; i < PointCount; i++, j += StepAngle) { Points[i].X = cos(j); // These values will be multiplied by the Points[i].Y = sin(j); // current radius at display time. } } void TMoveToLineToWindow::Paint(TDC& dc, BOOL, TRect&) { TRect rect; if (Iconized) Parent->GetClientRect(rect); else GetClientRect(rect); int centerX = rect.right / 2; int centerY = rect.bottom / 2; int radius = min(centerY, centerX); // The follow memory DC operations are not required to draw lines, but // were added to reduce screen flicker and speed up screen updates. // TMemoryDC memDC(dc); TBitmap bitmap(dc, radius*2, radius*2); memDC.SelectObject(bitmap); // Initiallize the new bitmap to all white. // memDC.PatBlt(0, 0, radius*2, radius*2, WHITENESS); // The Ellipse and the loop are all that's really needed to draw. If you // substitute dc for memDC, the draws will go directly to the screen. // (Though the figure would no longer be centered, since the figure is drawn // on a memDC bitmap, and the bitmap is then centered on the dc...) // Since this line window is animated, it is frequently updated, which would // cause the window to spend most of its time flickering if the dc were // used. Thus, the need for memory DC operations. If the window were not // animated, drawing onto the dc would look just fine. // memDC.Ellipse(0, 0, radius*2, radius*2); int i,j; for (i = 0; i < PointCount; i++) { for (j = i + 1; j < PointCount; j++) { memDC.MoveTo(radius + floor(Points[i].X * radius), radius + floor(Points[i].Y * radius)); memDC.LineTo(radius + floor(Points[j].X * radius), radius + floor(Points[j].Y * radius)); } } // Now transfer what was drawn on the (invisible) memory DC onto the visible // dc. This one BitBlt transfer is much faster than the many // individual operations that were performed above. // dc.BitBlt(centerX-radius, centerY-radius, radius*2, radius*2, memDC, 0, 0, SRCCOPY); // Restore original bitmap before leaving // memDC.RestoreBitmap(); // Footnotes: Drawing this figure doesn't require a memory DC. Animating // the figure requires a memory DC only to reduce flicker to a tolerable // level. // To make the animation faster still, (but use more memory, too), // you could keep that memory DC hanging around between screen paints - // constructing a DC takes some effort, and we're constructing one every // time we get a timer message. You'd get the biggest improvement in // animation speed by calculating a sequence of bitmaps, then just // displaying them in the proper sequence. This demo reconstructs the // points list and redraws the figure for every timer message - a lot of // work for the CPU, but it's code is simpler and doesn't use as much // memory as more elaborate schemes might. // A challenge: Turn the rotating figure into a ball that bounces off the // walls of the window. Don't forget the english (spin) the ball should // pick up when bouncing off the wall... }