[C# Helper]
Index Books FAQ Contact About Rod
[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

[C# 24-Hour Trainer]

[C# 5.0 Programmer's Reference]

[MCSD Certification Toolkit (Exam 70-483): Programming in C#]

Title: Map drawing coordinates without distortion in C#

[Map drawing coordinates without distortion in C#]

The post Easily map drawing coordinates in C# lets you easily map a rectangle in drawing coordinates to a rectangle in device coordinates. Often, however, you'll want to ensure that the drawing area isn't distorted. In other words, you don't want the picture to be stretched out of shape vertically or horizontally.

This example uses the following method to map a rectangle in drawing coordinates to a rectangle in device coordinates, making the drawing rectangle as large as possible without distorting it.

// Map from world coordinates to device coordinates // without distortion. private void SetTransformationWithoutDisortion(Graphics gr, RectangleF world_rect, RectangleF device_rect, bool invert_x, bool invert_y) { // Get the aspect ratios. float world_aspect = world_rect.Width / world_rect.Height; float device_aspect = device_rect.Width / device_rect.Height; // Asjust the world rectangle to maintain the aspect ratio. float world_cx = world_rect.X + world_rect.Width / 2f; float world_cy = world_rect.Y + world_rect.Height / 2f; if (world_aspect > device_aspect) { // The world coordinates are too short and width. // Make them taller. float world_height = world_rect.Width / device_aspect; world_rect = new RectangleF( world_rect.Left, world_cy - world_height / 2f, world_rect.Width, world_height); } else { // The world coordinates are too tall and thin. // Make them wider. float world_width = device_aspect * world_rect.Height; world_rect = new RectangleF( world_cx - world_width / 2f, world_rect.Top, world_width, world_rect.Height); } // Map the new world coordinates into the device coordinates. SetTransformation(gr, world_rect, device_rect, invert_x, invert_y); }

This method first calculates the aspect ratios (ratio of width to height) for the world and device rectangles. If the world aspect ratio is greater than the device aspect ratio, then the world rectangle is relatively short and wide compared to the device rectangle. In that case the code calculates a new height to make the world rectangle taller so it has the same shape as the device rectangle. It uses the new height to create a new world rectangle centered over the original one.

Conversely if the world aspect ratio is smaller than the device aspect ratio, then the world rectangle is relatively tall and thin compared to the device rectangle. In that case the code calculates a new width to make the world rectangle wider so it has the same shape as the device rectangle. It uses the new width to create a new world rectangle centered over the original one.

At this point the new world rectangle and the device rectangle have the same shapes, so the program simply calls the SetTransformation method described in the earlier post. Because the two rectangles have the same aspect ratios, this transformation doesn't distort the drawing area.

The example program uses the following code to demonstrate the SetTransformationWithoutDisortion method.

// Draw the smiley as big as possible without distortion. private void picCanvas_Paint(object sender, PaintEventArgs e) { e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // Set the transformation. Flip vertically. RectangleF drawing_rect = new RectangleF(-1, -1, 2, 2); const int margin = 4; Rectangle device_rect = new Rectangle( margin, margin, picCanvas.ClientSize.Width - 2 * margin, picCanvas.ClientSize.Height - 2 * margin); SetTransformationWithoutDisortion(e.Graphics, drawing_rect, device_rect, false, true); // Draw the smiley. DrawSmiley(e.Graphics); // Reset the transformation and draw a box around it. e.Graphics.ResetTransform(); e.Graphics.DrawRectangle(Pens.Red, device_rect); }

This code makes a world rectangle with -1 ≤ X ≤ 1 and -1 ≤ Y ≤ 1 because that's the area where the DrawSmiley method draws its smiley face. It makes a device rectangle to fit the PictureBox minus a margin. It then calls SetTransformationWithoutDisortion to map the world coordinates to the device coordinates and calls the DrawSmiley method.

Finally the method resets the Graphics object's transformation and draws a rectangle around the device coordinate rectangle. If you resize the form, you can see that the program draws the smiley face as large as possible without distorting it and while fitting it into the device rectangle.

Download the example to experiment with it and to see additional details.

© 2009-2023 Rocky Mountain Computer Consulting, Inc. All rights reserved.