I'm writing an app which will draw on a window. The drawing is architectural in scale, out to a maximum size of 10,000x10,000 inches. I'm using centered coordinates sized in 1/10th's of an inch. I want scroll bars and a zoom capability; the zoom will be controlled by the mouse wheel, and will be from 300% to 5%, where 100% means each pixel on the screen is 1/2" on the plan. Obviously, the zoom will raise and lower that value. I need the zoom to work such that if any part of the plan is at the center of the drawing window, zooming in and out will leave that part centered on the screen (I'm sure there has to be a neat name for that capability, but I don't know what it might be...iso-centered, perhaps?
.
I've been experimenting with code to try to cover these requirements, but haven't been able to get it done the way I want it to be. Here is the current code, excerpted from the window process:
Note that there are lots of magic numbers in the code, as I'm trying to make it work reasonably. I would prefer to use constants that are theoretically derived, but I don't know enough about all the possibilities - all I can do is guess at this point. Pointers to tutorials or other matter that expand on what MS says in msdn or the compiler help would be appreciated. Good books about this area? Any hints, or places where I'm obviously barking up the wrong tree?
I've been experimenting with code to try to cover these requirements, but haven't been able to get it done the way I want it to be. Here is the current code, excerpted from the window process:
Code:
#define PIXPERINCH 2
LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxClient, cyClient;
int zoom;
SCROLLINFO si;
PAINTSTRUCT ps;
HDC hdc;
switch (message) {
case WM_CREATE:
zoom = 1.;
break;
case WM_SIZE:
{
if (lParam > 0) {
cxClient = LOWORD (lParam);
cyClient = HIWORD (lParam);
}
si.cbSize = sizeof (si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nPage = (int) (cxClient / (5 * PIXPERINCH * opts.de.zoom));
si.nMin = -100;
si.nMax = 100 + si.nPage;
SetScrollInfo (hWnd, SB_VERT, &si, TRUE);
si.cbSize = sizeof (si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nPage = (int) (cyClient / (5 * PIXPERINCH * opts.de.zoom));
si.nMin = -100;
si.nMax = 100 + si.nPage;
SetScrollInfo (hWnd, SB_HORZ, &si, TRUE);
InvalidateRect (hWnd, NULL, TRUE);
}
break;
case /*WM_MOUSEWHEEL*/ 0x20a:
// makes 24 clicks to cover zoom range of .05 to 3.0; about 19% increase/decrease per click.
zoom *= (pow (1.189, (double) (((short) HIWORD (wParam)) / 120)));
zoom = __max (.05, __min (opts.de.zoom, 3.0));
SendMessage (hWnd, WM_SIZE, SIZE_RESTORED, 0);
return TRUE;
case WM_VSCROLL:
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
GetScrollInfo (hWnd, SB_VERT, &si);
switch (LOWORD (wParam)) {
case SB_TOP:
si.nPos = si.nMin;
break;
case SB_BOTTOM:
si.nPos = si.nMax;
break;
case SB_LINEUP:
si.nPos--;
break;
case SB_LINEDOWN:
si.nPos++;
break;
case SB_PAGEUP:
si.nPos -= si.nPage;
break;
case SB_PAGEDOWN:
si.nPos += si.nPage;
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
}
si.fMask = SIF_POS;
SetScrollInfo (hWnd, SB_VERT, &si, TRUE);
InvalidateRect (hWnd, NULL, TRUE);
return TRUE;
case WM_HSCROLL:
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
GetScrollInfo (hWnd, SB_HORZ, &si);
switch (LOWORD (wParam)) {
case SB_LINELEFT:
si.nPos--;
break;
case SB_LINERIGHT:
si.nPos++;
break;
case SB_PAGELEFT:
si.nPos -= si.nPage;
break;
case SB_PAGERIGHT:
si.nPos += si.nPage;
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
}
si.fMask = SIF_POS;
SetScrollInfo (hWnd, SB_HORZ, &si, TRUE);
InvalidateRect (hWnd, NULL, TRUE);
return TRUE;
case WM_PAINT:
hdc = BeginPaint (hWnd = hIn, &ps);
SetMapMode (hdc, MM_ISOTROPIC);
SetWindowExtEx (hdc, 100000, 100000, NULL);
SetViewportExtEx (hdc, xClient / 2, zClient / 2, NULL);
SetViewportOrgEx (hdc, xClient / 2, zClient / 2, NULL);
// Scale and translate:
si.cbSize = sizeof (si);
si.fMask = SIF_POS;
GetScrollInfo (hWnd, SB_VERT, &si);
int zpos = si.nPos;
si.cbSize = sizeof (si);
si.fMask = SIF_POS;
GetScrollInfo (hWnd, SB_HORZ, &si);
OffsetViewportOrgEx (hdc, -si.nPos * 13, -zpos * 13, NULL);
ScaleWindowExtEx (hdc, 1000, (int) (o->de.zoom * 1000.),
1000, (int) (o->de.zoom * 1000.), NULL);
// Go ahead and draw the image on the window...
EndPaint (hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return DefWindowProc (hWnd, message, wParam, lParam);
}
return 0;
}
Note that there are lots of magic numbers in the code, as I'm trying to make it work reasonably. I would prefer to use constants that are theoretically derived, but I don't know enough about all the possibilities - all I can do is guess at this point. Pointers to tutorials or other matter that expand on what MS says in msdn or the compiler help would be appreciated. Good books about this area? Any hints, or places where I'm obviously barking up the wrong tree?