IMSL C# Programmers Guide
|
Actions >> Zoom |
|
Zoom
IMSL C# provides the functionality to implement a variety of zoom policies instead of implementing a single specific zoom policy. The following example shows how to build a zoom policy that draws a rubber-band box in a scatter plot and zooms both the x-axis and y-axis to correspond to the region selected. Zoom out is also implemented.
This code can be used as the basis of an implementation of other zoom policies. For example, with a function plot one might want to only change the x-axis in response to a zoom and leave the y-axis alone. In other situations it may be desirable to zoom in or out a fixed amount around the location of a mouse click.
Example
In the constructor:
MouseEventHandler is added to the panel. A FrameChart contains a
number of components, including a Panel in which the chart is drawn. It is
important that the MouseEventHandler be added to the component in which
the chart is drawn, so that the MouseEventArgs coordinates are the same
as the device coordinates of the chart.
A mouse press event starts the zoom. In response to the event a
MouseEventHandler is added to the panel to listen for mouse drag events. The
location of this initial event is also recorded.
A mouse drag event resizes the area of the zoom. In response, the old rubber-band
box is erased and a new box is drawn. The new box has the initial mouse press location
as one corner and this drag event location as the opposite corner. The location
of this event is stored in (xLast,yLast).
A mouse release event completes the zoom. In response,
MouseEventHandler with for MouseMove is removed from the panel,
Window attributes for the x-axis and y-axis are saved on a stack.
They may be used later to zoom out.
Window is computed. The Window is the range, in user coordinates,
along an axis. The method Axis.MapDeviceToUser is used to map the
device (pixel) coordinates to the user coordinate space. The new windows are
constrained to be inside of the existing window.
Window values will be used without
further modification.
The menu items, added by the constructor to the main menu, can be used to zoom
out. The added menu items add an EventHandler with the menuItem_Click
delegate as the method called when these menu items are selected. The
menuItem_Click method implements Out by popping previous Window at-
tribute values off a stack. It implements Restore by re-enabling autoscaling.
(Download Code)
using System;
using System.Windows.Forms;
using System.Drawing;
using Imsl.Chart2D;
public class SampleZoom : FrameChart
{
private Panel panel;
private AxisXY axis;
private int xFirst, yFirst;
private int xLast, yLast;
private System.Collections.Stack stack;
private MouseEventHandler mouseMoveHandler;
public SampleZoom()
{
panel = this.Panel;
Chart chart = this.Chart;
axis = new AxisXY(chart);
int n = 1000;
double[] x = new double[n];
double[] y = new double[n];
Random ran = new Random();
for (int k = 0; k < n; k++) {
x[k] = 10.0 * ran.NextDouble();
y[k] = 10.0 * ran.NextDouble();
}
Data data = new Data(axis, x, y);
data.DataType = Data.DATA_TYPE_MARKER;
// add menu Zoom menu
MenuItem menuZoom = new MenuItem();
menuZoom.Text = &Zoom;
menuZoom.Shortcut = Shortcut.CtrlZ;
AddMenuItem(menuZoom, Out, Shortcut.CtrlO);
AddMenuItem(menuZoom, Restore, Shortcut.CtrlR);
this.Menu.MenuItems.Add(menuZoom);
// save x-axis and y-axis Window attributes on this stack.
// they are used to zoom out one level.
stack = new System.Collections.Stack();
// listen for mouse press events
panel.MouseDown += new MouseEventHandler(Panel_MouseDown);
panel.MouseUp += new MouseEventHandler(Panel_MouseUp);
mouseMoveHandler = new MouseEventHandler(panel_MouseMove);
}
// Add a menu item
private void AddMenuItem(MenuItem menuZoom, string title, Shortcut sc)
{
MenuItem menuItem = new MenuItem(title);
menuItem.Text = title;
menuItem.Shortcut = sc;
menuZoom.MenuItems.Add(menuItem);
menuItem.Click += new EventHandler(menuItem_Click);
}
// A mouse press starts the zoom
// Record location of this point and listen for drag (motion) events.
void Panel_MouseDown(object sender, MouseEventArgs e)
{
xFirst = xLast = e.X;
yFirst = yLast = e.Y;
panel.MouseMove += mouseMoveHandler;
}
// Releasing the mouse button ends the zoom
// Stop listening for move events and update the Window attributes
void Panel_MouseUp(object sender, MouseEventArgs e)
{
panel.MouseMove -= mouseMoveHandler;
// ignore degenerate zooms
if (xFirst == xLast || yFirst == yLast)
{
Refresh();
return;
}
// get window and convert to user coordinates
double[] windowX = (double[])axis.AxisX.GetWindow();
double[] windowY = (double[])axis.AxisY.GetWindow();
// save the windows on the stack (for zoom out option)
stack.Push(windowX);
stack.Push(windowY);
// get user coordinate of left-upper corner of the box
// limit it to stay inside current window
double[] boxLU = new double[2];
int x = Math.Min(xFirst, xLast);
int y = Math.Min(yFirst, yLast);
axis.MapDeviceToUser(x, y, boxLU);
// get user coordinate of right-lower corner of the box
// limit it to stay inside current window
double[] boxRL = new double[2];
x = Math.Max(xFirst, xLast);
y = Math.Max(yFirst, yLast);
axis.MapDeviceToUser(x, y, boxRL);
// set axis window to range of rubber-band box
// and disable autoscale to force use of window settings
axis.AutoscaleInput = ChartNode.AUTOSCALE_OFF;
double xa = Math.Max(windowX[0], boxLU[0]);
double xb = Math.Min(windowX[1], boxRL[0]);
double ya = Math.Max(windowY[0], boxRL[1]);
double yb = Math.Min(windowY[1], boxLU[1]);
axis.AxisX.SetWindow(xa, xb);
axis.AxisY.SetWindow(ya, yb);
// force redraw of the chart
Refresh();
}
// Moving the mouse after a click continues the zoom.
// Erase the old rubber band box and draw a new one
// Also keep track of the location of this event
void panel_MouseMove(object sender, MouseEventArgs e)
{
// erase previous rectangle
Graphics g = panel.CreateGraphics();
DrawBox(g, Color.White);
// draw new rectangle
xLast = e.X;
yLast = e.Y;
DrawBox(g, Color.Red);
}
// Draw a box with (xFirst, yFirst) and (xLast, yLast) as its corners
private void DrawBox(Graphics g, Color color)
{
int x = Math.Min(xFirst, xLast);
int y = Math.Min(yFirst, yLast);
int w = Math.Abs(xLast - xFirst);
int h = Math.Abs(yLast - yFirst);
g.DrawRectangle(new Pen(color), x, y, w, h);
}
void menuItem_Click(object sender, EventArgs e)
{
string cmd = ((MenuItem)sender).Text;
if (cmd.Equals(Out))
{
try
{
// zoom out by restoring window settings from the stack
axis.AxisY.SetWindow((double[])stack.Pop());
axis.AxisX.SetWindow((double[])stack.Pop());
}
catch (System.InvalidOperationException)
{
// no more levels to zoom out
// restore original setting by turning on autoscale
axis.AutoscaleInput = ChartNode.AUTOSCALE_DATA;
}
}
else if (cmd.Equals(Restore))
{
// restore original setting by turning on autoscale an empty stack
axis.AutoscaleInput = ChartNode.AUTOSCALE_DATA;
stack.Clear();
}
// force redraw of the chart
Refresh();
}
public static void Main(string[] argv)
{
System.Windows.Forms.Application.Run(new SampleZoom());
}
}
| © Visual Numerics, Inc. All rights reserved. |
|