📄 paint.c
字号:
/******************************************************************************\
*
* MODULE: PAINT.C
*
* PURPOSE: This is the module responsible for painting the SPINCUBE
* custom control. When Paint() is called we retrieve a
* pointer to a SPINCUBEINFO structure, and then use it's
* current rotation & translation values to transform the
* polyhedron described by gNormalizedVertices & gaiFacets.
* Once we've transformed the vertices, we draw the
* background, which consists of a grey rectangle and a few
* black lines (a crass attempt to render a perspective
* view into a "room"), on the offscreen bitmap associated
* with the control (i.e. pSCI->hbmCompat). Then we walk the
* facet list of the transformed polyhedron (gXformedVertices
* & gaiFacets), drawing only those facets whose outward
* normal faces us (again, drawing on pSCI->hbmCompat).
* Finally, we BitBlt the appropriate rectangle from our
* offscreen bitmap to the screen itself.
*
* Drawing to the offscreen bitmap has two advantages over
* drawing straight to the screen:
*
* 1. The actual drawing the user sees consists of only
* a single BitBlt. Otherwise, the user would see us
* both erase the polyhedron in it's old position and
* draw it in it's new position (alot of flashing- not
* very smooth animation).
*
* 2. When a spincube control with the SS_ERASE style
* is brought to the foreground, all it's contents
* i.e. the cube trails) are saved & can be re-Blted
* to the screen. Otherwise, all this info would be
* lost & there'd be a big blank spot in the middle
* of the control!
*
* Interested persons should consult a text on 3 dimensional
* graphics for more information (i.e. "Computer Graphics:
* Principles and Practice", by Foley & van Dam).
*
* Notes:
*
* - A 3x2 tranformation matrix is used instead of a 3x3
* matrix, since the transformed z-values aren't needed.
* (Normally these would be required for use in depth
* sorting [for hidden surface removal], but since we
* draw only a single convex polyhedron this is not
* necessary.)
*
* - A simplified perspective viewing transformation
* (which also precludes the need for the transformed z
* coordinates). In a nutshell, the perspective scale
* is as follows:
*
* p' = S x p
* per
*
* where:
* S = WindowDepth /
* per (WindowDepth + fCurrentZTranslation)
*
* (WindowDepth is the greater of the control's window
* height or window width.)
*
*
* FUNCTIONS: Paint() - the paint routine
* TransformVertices() - transforms vertices
* ComputeRotationTransformation() - computes xformation
* based on current x, y
* and z rotation angles
*
\******************************************************************************/
#include <windows.h>
#include <math.h>
#include <stdlib.h>
#include "spincube.h"
#include "paint.h"
/******************************************************************************\
*
* FUNCTION: Paint
*
* INPUTS: hwnd - Handle of the window to draw into.
*
* COMMENTS: Draws window background & a polyhedron in the window.
*
\******************************************************************************/
void Paint (HWND hwnd)
{
PSPINCUBEINFO pSCI;
RECT rect;
int i;
LONG lScaleFactor;
PAINTSTRUCT ps;
HRGN hrgnClip;
HBRUSH hBrush, hBrushSave;
int iX, iY, iCX, iCY;
int facetIndex, numPoints;
POINT polygn[MAX_POINTS_PER_FACET];
POINT vector1, vector2;
COLORREF acrColor[6] = { 0x0000ff, 0x00ff00, 0xff0000,
0x00ffff, 0xff00ff, 0xffff00 };
pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd, GWL_SPINCUBEDATA);
BeginPaint (hwnd, &ps);
if (memcmp((void *)&ps.rcPaint, (void *)&pSCI->rcCubeBoundary, sizeof(RECT))
& !REPAINT_BKGND(pSCI))
{
//
// We're not here because it's time to animate (i.e. this paint isn't
// the result of a WM_TIMER), so just do the Blt & blow out of here...
//
BitBlt (ps.hdc,
ps.rcPaint.left,
ps.rcPaint.top,
ps.rcPaint.right - ps.rcPaint.left,
ps.rcPaint.bottom - ps.rcPaint.top,
pSCI->hdcCompat, ps.rcPaint.left,
ps.rcPaint.top, SRCCOPY);
EndPaint (hwnd, &ps);
return;
}
//
// The rectangle we get back is in Desktop coordinates, so we need to
// modify it to reflect coordinates relative to this window.
//
GetWindowRect (hwnd, &rect);
rect.right -= rect.left;
rect.bottom -= rect.top;
rect.left = rect.top = 0;
//
// Determine a "best fit" scale factor for our polyhedron
//
if (!(lScaleFactor = rect.right > rect.bottom ?
rect.bottom/12 : rect.right/12))
lScaleFactor = 1;
TransformVertices (hwnd, &rect, pSCI, lScaleFactor);
//
// Draw the window frame & background
//
// Note: The chances are that we are coming through here because we
// got a WM_TIMER message & it's time to redraw the cube to simulate
// animation. In that case all we want to erase/redraw is that small
// rectangle which bounded the polyhedron the last time. The less
// drawing that actually gets done the better, since we wnat to
// minimize the flicker on the screen. __BeginPaint__ is perfect for
// this because it causes all drawing outside of the invalid region
// to be "clipped" (no drawing is performed outside of the invalid
// region), and it also validates the invalid region.
//
if (DO_ERASE(hwnd) || REPAINT_BKGND(pSCI))
{
hrgnClip = CreateRectRgnIndirect (&ps.rcPaint);
SelectClipRgn (pSCI->hdcCompat, hrgnClip);
DeleteObject (hrgnClip);
SelectObject (pSCI->hdcCompat, GetStockObject (GRAY_BRUSH));
Rectangle (pSCI->hdcCompat, (int)rect.left, (int)rect.top,
(int)rect.right, (int)rect.bottom);
iX = (rect.right - rect.left) / 4;
iY = (rect.bottom - rect.top ) / 4;
MoveToEx (pSCI->hdcCompat, (int)rect.left, (int)rect.top, NULL);
LineTo (pSCI->hdcCompat, (int)rect.left + iX, (int)rect.top + iY);
LineTo (pSCI->hdcCompat, (int)rect.left + iX, (int)rect.bottom - iY);
LineTo (pSCI->hdcCompat, (int)rect.left, (int)rect.bottom);
MoveToEx (pSCI->hdcCompat, (int)rect.right, (int)rect.top, NULL);
LineTo (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.top + iY);
LineTo (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.bottom- iY);
LineTo (pSCI->hdcCompat, (int)rect.right, (int)rect.bottom);
MoveToEx (pSCI->hdcCompat, (int)rect.left + iX, (int)rect.top + iY, NULL);
LineTo (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.top + iY);
MoveToEx (pSCI->hdcCompat, (int)rect.left + iX, (int)rect.bottom - iY, NULL);
LineTo (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.bottom - iY);
SelectClipRgn (pSCI->hdcCompat, NULL);
pSCI->iOptions &= ~SPINCUBE_REPAINT_BKGND;
}
//
// Draw the polyhedron. We'll walk through the facets list and compute
// the normal for each facet- if the normal has z > 0, then the facet
// faces us and we'll draw it. Note that this algorithim is ONLY valid
// for scenes with a single, convex polyhedron.
//
// Note: Use GetDC here because the above call to BeginPaint will
// probably not give us a DC with access to as much real estate as
// we'd like (we wouldn't be able to draw outside of the invalid
// region). We can party on the entire control window with the DC
// returned by GetDC.
//
for (i = 0, facetIndex = 0; i < NUMFACETS; i++)
{
vector1.x = gXformedVertices[gaiFacets[facetIndex + 1]].x -
gXformedVertices[gaiFacets[facetIndex]].x;
vector1.y = gXformedVertices[gaiFacets[facetIndex + 1]].y -
gXformedVertices[gaiFacets[facetIndex]].y;
vector2.x = gXformedVertices[gaiFacets[facetIndex + 2]].x -
gXformedVertices[gaiFacets[facetIndex + 1]].x;
vector2.y = gXformedVertices[gaiFacets[facetIndex + 2]].y -
gXformedVertices[gaiFacets[facetIndex + 1]].y;
for (numPoints = 0; gaiFacets[facetIndex] != -1; numPoints++, facetIndex++)
{
polygn[numPoints].x = gXformedVertices[gaiFacets[facetIndex]].x;
polygn[numPoints].y = gXformedVertices[gaiFacets[facetIndex]].y;
}
facetIndex++; /* skip over the -1's in the facets list */
if ((vector1.x*vector2.y - vector1.y*vector2.x) > 0)
{
hBrush = CreateSolidBrush (acrColor[i]);
hBrushSave = (HBRUSH) SelectObject (pSCI->hdcCompat, hBrush);
Polygon (pSCI->hdcCompat, &polygn[0], numPoints);
SelectObject (pSCI->hdcCompat, hBrushSave);
DeleteObject (hBrush);
}
}
iX = pSCI->rcCubeBoundary.left < ps.rcPaint.left ?
pSCI->rcCubeBoundary.left : ps.rcPaint.left;
iY = pSCI->rcCubeBoundary.top < ps.rcPaint.top ?
pSCI->rcCubeBoundary.top : ps.rcPaint.top;
iCX = (pSCI->rcCubeBoundary.right > ps.rcPaint.right ?
pSCI->rcCubeBoundary.right : ps.rcPaint.right) - iX;
iCY = (pSCI->rcCubeBoundary.bottom > ps.rcPaint.bottom ?
pSCI->rcCubeBoundary.bottom : ps.rcPaint.bottom) - iY;
EndPaint (hwnd, &ps);
ps.hdc = GetDC (hwnd);
BitBlt (ps.hdc, iX, iY, iCX, iCY, pSCI->hdcCompat, iX, iY, SRCCOPY);
ReleaseDC (hwnd, ps.hdc);
}
/******************************************************************************\
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -