Link to home
Start Free TrialLog in
Avatar of HansChr
HansChr

asked on

Coordinate question for Matrix transformation in System.Drawing.Drawing2D

I have used a lot of time understanding the graphics class in C# because I need to make a program with function plotting.

I have gone through this interesting program about function plotting:
http://blog.csharphelper.com/2012/01/23/graph-an-equation-y--fx-in-c.aspx

I have understood the basic matrix transformation that makes objects rotate, flip, scale etc. but I still cant figure out one thing:


If you make a graphics transformation from world coordinates to page like this:

int wid = picGraph.ClientSize.Width;
            int hgt = picGraph.ClientSize.Height;
            Bitmap bm = new Bitmap(wid, hgt);
            Graphics gr = Graphics.FromImage(bm))

 RectangleF rect = new RectangleF(xmin, ymin, xmax - xmin, ymax - ymin);
                PointF[] pts = 
                {
                    new PointF(0, hgt),
                    new PointF(wid, hgt),
                    new PointF(0, 0),
                };

gr.Transform = new Matrix(rect, pts);
 gr.DrawLine(graph_pen, xmin, 0, xmax, 0);
gr.DrawLine(graph_pen, 0, ymin, 0, ymax);
            {

Open in new window


http://msdn.microsoft.com/en-us/library/749kbe3w.aspx

Then you get a coordinate system that is stretched vertically and horizontally to the sides of the picturebox:User generated image
But if you manually enter the values from the transformation matrix then you only get a small coordinate system that is centered correctly but isnt scaled properly:
User generated image

 How come the transformation matrix's scale values are not higher then? (look at the other attached screen capture.

If you print the matrix elements you get when x-min/max  and y-min/max are -3,3, -3,3 by:
gr.Transform = new Matrix(rect, pts);
Matrix ele = gr.Transform;
 float[] ele = inverse.Elements;
            float el = ele.ElementAt(0);
            float el2 = ele.ElementAt(1);
            float el3 = ele.ElementAt(2);
            float el4 = ele.ElementAt(3);
            float el5 = ele.ElementAt(4);
            float el6 = ele.ElementAt(5);
            textBox1.Text += el.ToString() + " ";
            textBox1.Text += el2.ToString() + " ";
            textBox1.Text += el3.ToString() + " ";
            textBox1.Text += el4.ToString() + " ";
            textBox1.Text += el5.ToString() + " ";
            textBox1.Text += el6.ToString();

Open in new window


Matrix elements are then:

[m11,m12,m21,m22,dx,dy] = [5,96,0,0,-3,58,179,107,5]

If I instead enter the transformation Matrix by "hand" like this and of course make no other transformation before (remove the line:  gr.Transform = new Matrix(rect, pts);):
gr.Transform = new Matrix((float)5.966666, 0, 0, (float)-3.583333, 179,107.5);

Open in new window


I get the much smaller coordinate system if I draw the two lines:

gr.DrawLine(graph_pen, xmin, 0, xmax, 0);
gr.DrawLine(graph_pen, ymin, 0, ymax, 0);

Open in new window


In theory this is correct as far as I understand, because what actually happens is that the coordinate transformation is done by (look at the bottom of the page http://msdn.microsoft.com/en-us/library/w8s1ct0z) )

multiplying the original coordinates by the transformation matrix (here just for the x-axis):


User generated image
One can see that the pixel distance between the two coordinate sets are: 35,76. Then it is obvious why the coordinate system is so small.

My question is then: Why does the transformation matrix have scaling values that are so small and how does it stretch according to the first matrix constructor? This is very confusing. Its like the constructor adds something to stretch the rectangle to the border other than just the transformation matrix but I cant understand the theory and why.

If the scaling was greater in the transformation Matrix for example like this:

User generated image
This would result in a larger coordinate system closer to the border of the picturebox:

User generated image
I hope I have made myself clear and apologize if something is confusing.

Thanks a lot.

Regards

Hans
Avatar of Robert Schutt
Robert Schutt
Flag of Netherlands image

The problem seems to be in your code to determine the elements. I use this code after setting gr.Transform:
textBox1.Text = string.Join(", ", gr.Transform.Elements);

Open in new window

With the values this code returns (that differ from yours probably just because it's another Matrix) I can set it manually for the same size:
gr.Transform = new Matrix(60, 0, 0, (float)-36.16667, 180, (float)108.5);

Open in new window

Note that your code to print the elements uses 'ele' twice and actually prints the elements of the "Matrix inverse".
Avatar of HansChr
HansChr

ASKER

.
Avatar of HansChr

ASKER

First of all thank you very much for your comment. I think we are very close to the solution.

I have to state that I pasted some of the code wrongly I appologize :

The code should have been and was indeed (I just accidently pasted the wrong code. So the problem is not because of this but thanks for noticing):

gr.Transform = new Matrix(rect, pts);
Matrix m = gr.Transform;
 float[] ele = m.Elements;
            float el = ele.ElementAt(0);
            float el2 = ele.ElementAt(1);
            float el3 = ele.ElementAt(2);
            float el4 = ele.ElementAt(3);
            float el5 = ele.ElementAt(4);
            float el6 = ele.ElementAt(5);
            textBox1.Text += el.ToString() + " ";
            textBox1.Text += el2.ToString() + " ";
            textBox1.Text += el3.ToString() + " ";
            textBox1.Text += el4.ToString() + " ";
            textBox1.Text += el5.ToString() + " ";
            textBox1.Text += el6.ToString();

Open in new window




First of all if I copy your code in Visual Studio I get the following error:

 User generated image

I don't know why it won't cast. I read somewhere that if you only have old versions of net installed this method doesnt work but I have up to Net 4.0 installed.

Instead I tried this:
string[] strings = Array.ConvertAll<float, string>(val, Convert.ToString);
                    textBox1.Text = string.Join(", ",strings);

Open in new window


from:  http://stackoverflow.com/questions/1970738/convert-system-array-to-string

This seems to work. But it doesnt change the output I get.

I also tried this:

string s = string.Join(" ", val.Select(f => f.ToString()).ToArray());
                    textBox1.Text = s;

Open in new window



from: http://stackoverflow.com/questions/3976707/what-is-the-fastest-way-of-converting-an-array-of-floats-to-string

This doesnt change the result either.



But I think I might have found the error:

It seems like there are two different versions of the graph. One with user input enabled and one without. When I use the original input from the file downloaded here:
http://blog.csharphelper.com/2012/01/24/graph-an-equation-entered-by-the-user-in-c.aspx where user input is enabled I get this screenshot:
User generated image

But if I use the other one at:

http://blog.csharphelper.com/2012/01/23/graph-an-equation-y--fx-in-c.aspx

I get this one:

 User generated image

I tracked the problem down to the difference in the code in the beginning of the program:

In the user input version the code for x-min/max and y-min/max come from user input:
   float xmin = float.Parse(txtXmin.Text);
            float xmax = float.Parse(txtXmax.Text);
            float ymin = float.Parse(txtYmin.Text);
            float ymax = float.Parse(txtYmax.Text);

Open in new window


In the other version code is:

            float xmin = -3;
            float xmax = 3;
            float ymin = -3;
            float ymax = 3;

Open in new window


Apparently the code gets translated wrongly somehow.

If I change the min and max values of the axis to -3,3,-3,3 I get the correct look and the correct coordinates:
User generated image

The float values at input is apparently interpreted wrongly. Instead of x-min being float -3.0, 3.0 as inputted from the textbox its gets set to -30, 30 and the same for the y.axis.

User generated image

Why I dont know.
Avatar of HansChr

ASKER

I think I found the error:

The author of the graph made a mistake in the property value of the textboxes containing the xmin/max ymin/max values:

User generated image
He made the decimal with a dot "." but it should have been a comma ",". If you put a dot it just get ignored and 3.0 will be interpreted as 30.

Of course I didnt notice that because it is the "start"-up proporty of the graph. If I change the proporty of all the 4 textboxes to "," instead of "." I get the correct answer.


User generated image
Oh my God. I used 2 weeks thinking about this and the problem was a dot instead of a comma. I love computers :-/....
ASKER CERTIFIED SOLUTION
Avatar of Robert Schutt
Robert Schutt
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of HansChr

ASKER

I see your point. Its the same with Excel with respect to decimals:

In regional settings I changed the "decimal symbol" from "," to "." and now the original code works :

User generated image
User generated image
So it seems like a localized issue. Maybe changing to "British/american" doesnt change the "," to ".".

I didnt understand the last thing you wrote about being aware of the problem ;-).

Maybe the author could have made a case of "." and a case of "," to take the regional settings into account.
> I didnt understand the last thing you wrote about being aware of the problem ;-).

Oh, think nothing of it, I just meant it seemed from your post that you had already come across the problem of printing 5,96 for m11 and in the code adjusting the comma to a decimal point.

> Maybe the author could have made a case of "." and a case of "," to take the regional settings into account

Well yes, that's the big question for me, if it should be handled by Visual Studio maybe.
Avatar of HansChr

ASKER

Well anyway thank you very much for your help ;-).

It seems like Visual Studio complains if I changed the decimal in regional settings from "," to "." and then changed the input to "3,0" but not the other way or something...

At least i wont make this mistake again!