
Coordinate system conversionThe resolution independent nature of GDI+ can cause problems when trying to convert from mouse coordinates to the current GraphicsUnit setting or from world units to pixels. When the page units are set to GraphicsUnit.Pixel, everything is fine but as soon as you begin using a real-world measurement such as points, or inches the conversion between the mouse position in pixels and the page position in inches is not immediately clear. Add to this the fact that the view of the page may have been scrolled and you have a potential head-scratcher that can eat up hours of development time. What you need is a simple formula that will enable you to translate from screen-pixel units to page units and back again whatever the page-unit setting and whatever the current scroll position settings. The basic formula to convert from mouse position to page-units is: page unit position = (mousepos-scrollpos) * scaling
factor (where scaling factor is 1/resolution) worldpos-(scrollpos*scalingfactor) These formulae are good for both the X and the Y values but you must remember to calculate the X and Y resolutions independently because they are not guaranteed to be the same. The following listing defines an application that manages scaling, scrolling, mouse input and paint output to any one of the three Page Unit settings Pixel, Millimeter and Inch. using
System; using
System.Drawing; using
System.Drawing.Drawing2D; using
System.Collections; using
System.ComponentModel; using
System.Windows.Forms; using
System.Data; namespace
ScaleDraw {
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container
components = null;
public Form1()
{
//
// Required for Windows
Form Designer support
//
InitializeComponent();
this.AutoScrollMinSize=new
Size(640,480);
this.MaximumSize=new
Size(640,480);
// initial size values.
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if
(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing
);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not
modify
/// the contents of this method with the code
editor.
/// </summary>
private void InitializeComponent()
{
this.mainMenu1 = new
System.Windows.Forms.MainMenu();
this.menuItem1 = new
System.Windows.Forms.MenuItem();
this.menuItem2 = new
System.Windows.Forms.MenuItem();
this.menuItem3 = new
System.Windows.Forms.MenuItem();
this.menuItem4 = new
System.Windows.Forms.MenuItem();
this.menuItem5 = new
System.Windows.Forms.MenuItem();
this.menuItem6 = new
System.Windows.Forms.MenuItem();
//
// mainMenu1
//
this.mainMenu1.MenuItems.AddRange(new
System.Windows.Forms.MenuItem[] {
this.menuItem1,
this.menuItem3});
//
// menuItem1
//
this.menuItem1.Index = 0;
this.menuItem1.MenuItems.AddRange(new
System.Windows.Forms.MenuItem[] {
this.menuItem2});
this.menuItem1.Text =
"File";
//
// menuItem2
//
this.menuItem2.Index = 0;
this.menuItem2.Text =
"Exit";
//
// menuItem3
//
this.menuItem3.Index = 1;
this.menuItem3.MenuItems.AddRange(new
System.Windows.Forms.MenuItem[] {
this.menuItem4,
this.menuItem5,
this.menuItem6});
this.menuItem3.Text =
"PageUnit";
//
// menuItem4
//
this.menuItem4.Index = 0;
this.menuItem4.Text =
"Pixel";
this.menuItem4.Popup +=
new System.EventHandler(this.menuItem4_Popup);
this.menuItem4.Click +=
new System.EventHandler(this.menuItem4_Click);
//
// menuItem5
//
this.menuItem5.Index = 1;
this.menuItem5.Text =
"Millimeter";
this.menuItem5.Popup +=
new System.EventHandler(this.menuItem5_Popup);
this.menuItem5.Click +=
new System.EventHandler(this.menuItem5_Click);
//
// menuItem6
//
this.menuItem6.Index = 2;
this.menuItem6.Text =
"Inch";
this.menuItem6.Popup +=
new System.EventHandler(this.menuItem6_Popup);
this.menuItem6.Click +=
new System.EventHandler(this.menuItem6_Click);
//
// Form1
//
this.AutoScaleBaseSize =
new System.Drawing.Size(5, 13);
this.AutoScroll = true;
this.AutoScrollMinSize =
new System.Drawing.Size(150, 150);
this.BackColor =
System.Drawing.Color.White;
this.ClientSize = new
System.Drawing.Size(536, 249);
this.MaximumSize = new
System.Drawing.Size(1024, 768);
this.Menu =
this.mainMenu1;
this.Name =
"Form1";
this.Text =
"Form1";
this.SizeChanged += new
EventHandler(this.Form1_SizeChanged);
this.Paint += new
PaintEventHandler(this.Form1_Paint);
this.MouseMove += new
MouseEventHandler(this.Form1_MouseMove);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new
Form1());
}
PointF CanvasPoint;
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.MenuItem menuItem1;
private System.Windows.Forms.MenuItem menuItem2;
private System.Windows.Forms.MenuItem menuItem3;
private System.Windows.Forms.MenuItem menuItem4;
private System.Windows.Forms.MenuItem menuItem5;
private System.Windows.Forms.MenuItem menuItem6; //This is used to hold points drawn by the user
ArrayList
blobs = new ArrayList();
private void Form1_MouseMove(object sender,
MouseEventArgs e)
{
Graphics g =
CreateGraphics();
//calculate scaling
factor
PointF ScalingFactor=new
PointF(1,1);
switch(PageUnit)
{
case
GraphicsUnit.Millimeter:
ScalingFactor
= new PointF(1.0f / (g.DpiX/25.4f),1.0f / (g.DpiY/25.4f));
break;
case
GraphicsUnit.Inch:
ScalingFactor
= new PointF(1.0f / g.DpiX,1.0f / g.DpiY);
break;
}
//get world position.
CanvasPoint = new PointF(
(float)(e.X-this.AutoScrollPosition.X)*ScalingFactor.X,
(float)(e.Y-this.AutoScrollPosition.Y)*ScalingFactor.Y
);
// store blob if mouse is
pressed
if(Control.MouseButtons
== MouseButtons.Left)
{
blobs.Add(CanvasPoint);
Invalidate();
}
}
private void Form1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
SolidBrush b=new
SolidBrush(Color.Black);
PointF ScalingFactor=new
PointF(1,1);
//calculate scaling
factor (duplicated because I'm lazy);
switch(PageUnit)
{
case
GraphicsUnit.Millimeter:
ScalingFactor
= new PointF(1.0f / (e.Graphics.DpiX/25.4f),1.0f / (e.Graphics.DpiY/25.4f));
break;
case
GraphicsUnit.Inch:
ScalingFactor
= new PointF(1.0f / e.Graphics.DpiX,1.0f / e.Graphics.DpiY);
break;
}
// store the graphics
GraphicsState gs =
e.Graphics.Save();
e.Graphics.PageUnit=this.PageUnit;
// adjust the scroll
position according to the page unit scale
Matrix m = new Matrix();
m.Translate(this.AutoScrollPosition.X*ScalingFactor.X,this.AutoScrollPosition.Y*ScalingFactor.Y,MatrixOrder.Append);
e.Graphics.Transform=m;
// draw all the blobs in
world coordinates (regardless of page scaling or units)
foreach(PointF pnt in
blobs)
{
e.Graphics.FillEllipse(b,pnt.X-2,pnt.Y-2,4,4);
}
b.Dispose();
// get the original clean
graphics back
e.Graphics.Restore(gs);
}
private void Form1_SizeChanged(object sender,
System.EventArgs e)
{
Invalidate();
}
GraphicsUnit PageUnit=GraphicsUnit.Pixel;
private void menuItem4_Click(object sender,
System.EventArgs e)
{
// set page unit
PageUnit =
GraphicsUnit.Pixel;
//adjust scroll sizes
this.AutoScrollMinSize=new
Size(640,480);
Invalidate();
}
private void menuItem5_Click(object sender,
System.EventArgs e)
{
Graphics g =
CreateGraphics();
// set page units
PageUnit =
GraphicsUnit.Millimeter;
// adjust scroll sizes
this.AutoScrollMinSize=new
Size((int)(640*(g.DpiX/25.4)),(int)(480*(g.DpiY/25.4)));
Invalidate();
g.Dispose();
}
private void menuItem6_Click(object sender,
System.EventArgs e)
{
Graphics g =
CreateGraphics();
//set page units
PageUnit = GraphicsUnit.Inch;
// you know the dril...
this.AutoScrollMinSize=new
Size((int)(640*g.DpiX),(int)(480*g.DpiY));
Invalidate();
g.Dispose();
}
private void menuItem4_Popup(object sender,
System.EventArgs e)
{
if(PageUnit == 0)
this.menuItem4.Checked=true;
else
this.menuItem4.Checked=false;
}
private void menuItem5_Popup(object sender,
System.EventArgs e)
{
if(PageUnit == 0)
this.menuItem5.Checked=true;
else
this.menuItem5.Checked=false;
}
private void menuItem6_Popup(object sender,
System.EventArgs e)
{
if(PageUnit == 0)
this.menuItem6.Checked=true;
else
this.menuItem6.Checked=false;
}
} }
copyright Robert W. Powell 2003. All rights reserved. |