Title: Graph equations while preserving aspect ratio in WPF and C#
The previous example, Graph equations entered by the user in WPF and C# shows how to graph an equation entered by the user. The user must also enter the coordinate bounds that the program should use. For example, in the following picture, the program is graphing the equation for -20 < X < 20, -3 < Y < 12.
Unfortunately the coordinates entered by the user compress the graph horizontally. To understand why, you need to know a few simple terms.
The world window is the area in world coordinates where the equation is evaluated. It's the X and Y coordinates that we want to draw. The units of the world window are whatever wee find convenient. The user enters the bounds for the world window.
The device window is the area on the screen where we want to draw the graph. Those units are pixels and the size of the device window is determined by the size of the Canvas control where we draw the graph.
Finally, an aspect ratio is the ratio of a window's width to height.
The reason the graph in the previous picture is compressed is that the world window's aspect ratio is not the same as the device window's aspect ratio. In this example, the world window is relatively short and wide, so it must be compressed horizontally to fit in the device window.
One way you can avoid this problem is to adjust the world window so it has the same aspect ratio as the device window. (You cannot adjust the device window without resizing the Canvas control, so our only hope is to adjust the world window.)
This example does uses the following AdjustAspectRatio method to give the world window the same aspect ratio as the device window.
// Make the world and device aspect ratios match.
private void AdjustAspectRatio(
ref double wxmin, ref double wxmax,
ref double wymin, ref double wymax,
double dxmin, double dxmax, double dymin, double dymax)
{
double wwid = wxmax - wxmin;
double whgt = wymax - wymin;
double dwid = dxmax - dxmin;
double dhgt = dymax - dymin;
double w_aspect = wwid / whgt;
double d_aspect = Math.Abs(dwid / dhgt);
if (w_aspect > d_aspect)
{
// The world window is too short
// and wide. Make it taller.
whgt = wwid / d_aspect;
double wcy = (wymax + wymin) / 2;
wymin = wcy - whgt / 2;
wymax = wcy + whgt / 2;
}
else
{
// The world window is too tall
// and thin. Make it wider.
wwid = whgt * d_aspect;
double wcx = (wxmax + wxmin) / 2;
wxmin = wcx - wwid / 2;
wxmax = wcx + wwid / 2;
}
}
This method calculates the widths and heights of the world and device windows. It then uses them to calculate the windows' aspect ratios.
Notice that the code takes the absolute value of the aspect ratio for the device window. It does that because device coordinates usually start with (0, 0) in the upper left corner and increase downward and to the right. That means ymin may be larger than ymax. That will give a negative device window width and therefore a negative aspect ratio.
After it has calculated the aspect ratios, the code compares them. If the world aspect ratio is greater than the device aspect ratio, then the world window is too short and wide compared to the device window. In that case, the code calculates the height is needs to give the world window to make it have the same aspect ratio as the device window. It updates wxmin and wxmax by adding half of the new width to the window's central X coordinate.
If the world aspect ratio is not greater than the device aspect ratio, then the world window is too tall and thin compared to the device window. In that case, the code calculates the width is needs to give the world window to make it have the same aspect ratio as the device window. It updates wymin and wymax by adding half of the new height to the window's central Y coordinate.
Now the two windows have the same aspect ratio so the graph is drawn without distortion.
Download the example to experiment with it and to see additional details.
|