Dynamic zoom with GDI

Hello,
I'm implementing a CAD application and I'd like to improve my zoom tools (zoom in/out, pam, scroll,...) to look like AutoCAD - dynamic zoom.

Currently I erase the background and redraw the visible drawing elements each time the zoom changes.

Does anyone have an insight on how to implement the dynamic zoom?

Thanks in advance for any help.

Regards,
Adriano
[418 byte] By [coser] at [2007-11-20 10:40:27]
# 1 Re: Dynamic zoom with GDI
Double buffering.

You specifically stop WM_ERASEBKGN.

You draw only on a bitmap (using a memory DC).

You blit the result to the window (which, since it's a complete replacement, obviates the need for an erase).

Erase is applied to the bitmap, but since that's not a window you must logically implement it yourself (there was a default for the window itself).

AutoCAD has some fairly considerable work done to make it function the way it does. The 'heidi' display component (they call it a driver) is fairly efficient, but it breaks down into a blitted double buffer window (probably with partial window optimizations) with custom drawing primitives (they implemented some of the drawing primitives to suit CAD better than GDI could do), and probably manipulate the bitmap 'directly' rather than using GDI. There is some potential for performance increases there, threading, etc. I suspect there may have been 'sharing' of code from their much earlier DOS product, which would have been entirely without the benefit of GDI (so there was a considerable body of drawing primitive 'logic' and history from which to draw upon).

Whether you want to choose to make your own drawing primitive functions or rely upon GDI is a matter of personal choice and design requirements.

Other options follow similar lines, but move to OpenGL or DirectX for transporting the bitmap to the display.

I assume that since you already have a zoom, you already have, therefore, a display pipeline with user adjustable parameters, so I'll skip that here.

You're now in for mouse interface development. You will have state tracking (if the mouse down was in the context of a pan or drawing a zoom-in box, etc). Each state tracking will lead to a series of mouse-capture/drag/release-capture/end processes. Some care must be taken for the 'lost capture' message.

I highly recommend considering the mouse programming project as a grid of logical options. I imagine the columns applying to the button/state functions (LDown, LUp, MDown, MUp). The rows of the grid apply to each possible state of the mouse (user has selected the zoom-in, so any LDown within the window would start the drawing of a zoom box).

I personally use a series of objects for this work, all based on a "mouse reponse" virtual base, defaulting to a 'do nothing' for all responses.

I have a unique object for each 'row' of this grid, where the response functions for that particular state of mouse operation are virtual overrides for that base.

One of the benefits I witness with this approach are cleaner, simpler response functions and rather obvious development goals - this means I can manage much more finesse in mouse interface design without resorting to large, convoluted "if/then/else" constructs based on various state variables. The object representing the state IS the state variable.

The "window" routes all mouse messages through this "mouse responder" object, and I have an array of them, one for each entry in the grid.

When the mouse switches operational state, I simply switch the 'current' mouse responder. The routing of the window's messages are then automatically "selected" among the rows of the grid.

For example, you have a dragging operation in drawing a zoom-in rectangle that implies a captured state. One row is the "initialized" response - this object, a mouse response derivative, would handle all mouse messages (moves, buttons, wheels, etc). Most of the responses will be the default "do nothing" functions in the base. The derived object representing the "zoom-in: init" only has functions for LButton down. This automatically means I'm "clear" of right button errant responses if I don't have them. I could 'chain' or 'composite' these objects if required, but lets stay with a simple example for now.

When the lbutton down happens, the mouse is captured, the initial down point is noted, and the 'current' mouse response object is switched to one representing 'zoom-in:captured'. At this point the only two functions in THIS version of the mouse response object are the move and the lbutton up.

As the "zoom-in:captured" object (representing THAT row of the mouse/operations grid) handles moves, the rectangle drawing management is called upon (tracking the current point, animating the rectangle for zoom). The simple fact that this object is the current mouse responder MEANS that the mouse is captured. I don't need another state variable to indicate that, and therefore no "if (captured)" statements to convolute the design of the response functions.

When the button release is processed, the capture is dropped, the rectangle is "finalized", and the "current" mouse response object is switched to some default (whatever your default mouse 'actions' might be).

I've used this method of mouse operations design with excellent results over the years. One thing that used to just nag me, back, say, around '90/'91, was that - when I designed using state variables (a bunch of bools or ints that indicated what the mouse should be doing right now), I would end up with errant responses to right button presses. This was because I'd forget to ignore the right button in certain contexts, and the resulting design of the response functions was difficult to comprehend, limiting flexibility - making the notion of finessing the mouse interface a dreaded and laborious task.

Using objects to represent state, in this way, gave my development practice sanity no matter how much finesse I ended up putting into the mouse design. At each "state" - if there's no function explicitly stated for the right mouse, you CAN'T have something just happen to fire simply because you forget about it. You have to explicitly provide for it.

This also reduced my reliance for state variables - so the window object is cleaner. You will probably need some reference or pointer to the window from within the mouse response object, so that you call member functions of the window common to various mouse response objects, but it's a minor price to pay.
JVene at 2007-11-10 3:48:15 >
# 2 Re: Dynamic zoom with GDI
Thanks a lot for your answer.

I'll concentrate my primary efforts on my drawing routine, trying to use the bitmat strategy you've suggested.

Depending on the results, I might also consider to move to OpenGL.

Thanks also for the great explanation on mouse operation, I'll try these concepts to optimize my capture routines.

Regards,
Adriano.
coser at 2007-11-10 3:49:22 >
# 3 Re: Dynamic zoom with GDI
CreateDIBSection - This function returns a handle to a bitmap (HBITMAP) and also gives you direct access to the bitmap bits.

You can select this HBITMAP into a memory DC. ;)

In this manner, you can use GDI functions, and custom routines that manipulate the bits directly.

If you are working with your own DIB implementation (DEVICE indepedent bitmap) and you need to figure how to put the data on the DC, then see SetDIBitsToDevice.
JamesSchumacher at 2007-11-10 3:50:19 >
# 4 Re: Dynamic zoom with GDI
Thank you very much James.

Regards.
coser at 2007-11-10 3:51:24 >