Click here to Skip to main content
15,887,356 members
Articles / Desktop Programming / MFC
Article

Warping Coordinates with Matrices

Rate me:
Please Sign up or sign in to vote.
4.73/5 (17 votes)
23 Jan 2005CPOL6 min read 75.3K   3.1K   42   13
How to transform point from one arbitrary coordinate system to another.

Picture of Demo Application

Introduction

This article is centered around one function, which takes three points defining one coordinate system, and three defining another, and creates a matrix to transform any point from one system to the other. It's more interesting than it sounds, honest!

Four years ago, I had a problem of translating a set of coordinates from a CAD model of an aircraft to the coordinate system of some large test equipment. As the part would not necessarily be loaded even in the same orientation as it was in the model, I had to translate the coordinates about. I managed to get something together to do the job. It was ugly. Fortunately, the parts were never quite the same at this stage of manufacture as they would be on the aircraft, so the whole exercise was a waste of time.

Two years ago, I had a similar problem. I had to put a CAD overlay on my data on the screen. It had three known points to match up with three specific points on the data. As this was a close problem to the first one, my brain had a head start. This time I could do the job properly, and write a nice reusable class.

A few days ago, a question on the message boards[^] brought it all back to mind. So I've written this article as a thank you to other authors here whose code has helped me so much.

Theory

This section will go in to the math in a little detail. I've tried to pitch it so it's easy on the novice, but not patronizing for the expert. Not an easy task!

I will explain the theory in two dimensions. As with most of the graphics, going to 3D makes the variables bigger, not the ideas.

To define a coordinate system, you need three points in 2D, and four in 3D. You can cheat a bit in 3D and use the cross product of the three other points, but that makes the system less flexible. For simplicity's sake (and because I can't just cut and paste my original code!), I'm going to concentrate on 2D.

Vectors

As most folks know, a Cartesian coordinate (x,y), can be represented as a vector. That in turn can be broken into a group of unit vectors and scalars.

i = X axis unit vector.
j = Y axis unit vector.
k = Z axis unit vector. (But we're not doing 3D today!)

Image 2

X Axis unit vector.

Image 3

A simple vector.

Image 4

The same vector broken into scalars and unit vectors.

Image 5

A vector broken down into unit vectors and scalars, but written more compactly.

Matrices

Now that we've covered the extreme basics, we can start to get a little more complicated…

We want to make a matrix which will make the X axis unit vector transform to (0.866, 0.5), and make the Y axis unit vector transform to (-0.5, 0.866).

Image 6

Image 7

It doesn't take a math genius to solve the equation above.

a = 0.866, b = 0.5, c = -0.5, d = 0.866.

If we fill in the matrix, and group a & b, and c & d together, we get the following:

Image 8

So we can make up any transformation matrix by knowing what happens to each unit vector. If i transforms to p, and j transforms to q, then we can make the matrix by setting its columns, to p & q.

Image 9

We can use this matrix to transform any point on the X/Y plane to a new coordinate system. To transform back, just multiply by M-1.

Transforming from one coordinate system to another.

So far, we dealt with going to / from an arbitrary coordinate system to Cartesian coordinates. Both systems have been transforming about (0,0).

Extending that to be more general needs three points in each coordinate system. An origin, and two Eigen vectors acting as unit-vectors for that system. We can specify the two vectors as the vectors from the origin to two points.

Calling the three points in the first system A, B & C, and the three points in the second system as A', B', and C', where A & A' are acting origins, we can proceed.

To create a matrix to change a vector V to its corresponding V', we would do the following steps:

  1. Make a translation T, matrix from -A.
  2. Use that matrix on B & C to give us AB and AC.
  3. Now we have Eigen vectors, create a matrix E which would transform any point in the "Unit" system to these coordinates.
  4. Calculate its inverse, as that's what we really want to do.
  5. Do steps (1) and (2) for A', B' and C', giving T' and E'.

Now we can put these steps together to give us a matrix M:

Image 10

Using the Code

The core classes (imcMatric / imcVector) do not depend on MFC. The only time MFC is used in them is in the operators / constructors using CPoint as a convenience. Remove those and you have classic C++.

You could use the vector and matrix classes that come with DirectX, as I started to do. But I found I was having precision problems with a sizable data set, and lots of scaling. Instead, I rolled my own matrix / vector classes. These do nothing radical that other libraries don't (including matrix classes here on CodeProject). But they are adequate to the needs of this project. As I've rolled my own classes, the only dependence they have on MFC (as mentioned above) is on CPoint. The demo project uses MFC heavily, but that is only a quick project.

The important member function for this article is the imcMatrix::WarpVectors method. This sets the matrix up from two sets of three vectors, three from each coordinate system.

struct imcVector
{
    double x, y;
    // Bunch of operators to make life easy.
    …
}

// Handy grouping of three vectors. Make its harder to miss one than an array!
struct imcVectorTriple
{
    imcVectorTriple (imcVector v1, imcVector v2, imcVector v3);
    imcVectorTriple (CPoint p1, CPoint p2, CPoint p3);
    imcVectorTriple ();

    imcVector v [3];

    void operator+= (const imcVector &v); // Useful for subtracting "origin".
    void operator-= (const imcVector &v);
};

class imcMatrix
{
public:
    …
    BOOL    WarpVectors (imcVectorTriple coordinatesystemFrom, 
                          imcVectorTriple coordinatesystemTo);
    …
};

If you have three coordinates in one system (p1, p2, p3) and three in another (q1, q2, q3), you can make a matrix to transform from P to Q.

imcVectorTriple vtFrom (p1, p2, p3), vtTo (q1, q2, q3);

imcMatrix M;
M.WarpVectors (vtFrom, vtTo);

Q = M * P;

And back again…

imcMatric M_Back;

M_Back.WarpVectors (vtTo, vtFrom);

P = M_Back * Q;

Computing the matrix is expensive, so you wouldn't want to do it in a loop. But using it to transform a vector is very efficient. Just a bunch of multiply / accumulates. What a processor does best!

There are most of the other matrix and vector functions you would think of. They are missing some functions, e.g., cross product, but they are not necessary for this job. Feel free to add any I've missed and email them. I'll be happy to add them for completeness.

Demo Application

This is a quick scribble like application I put together to demonstrate and prove the classes.

Drag the mouse to add a stroke. To move anchor points, select the view and press keys 1, 2, or 3. You can create a new window (Window | New) to see the same strokes in another coordinate system.

Acknowledgements:

One of the biggest problems I had was doing the equations! Fortunately, tbw's [^] Formula Editor [^] came to the rescue.

Also, Keith Rule's [^] CMemDC [^] class came to the rescue as it does in almost every drawing routine I write. Flicker is baaaaad.

History

20 Jan 2005 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Sweden Sweden
I have now moved to Sweden for love, and recently married a lovely Swede.


-----------------
I started programming on BBC micros (6502) when I was six and never quite stopped even while I was supposed to be studying physics and uni.

I've been working for ~13 years writing software for machine control and data analysis. I now work on financial transaction transformation software, for a Software company in Gamlastan, Stockholm.
Look at my articles to see my excellent coding skills. I'm modest too!

Comments and Discussions

 
GeneralSerializing data Pin
MeShadow22-Apr-05 11:50
MeShadow22-Apr-05 11:50 
GeneralRe: Serializing data Pin
Iain Clarke, Warrior Programmer22-Apr-05 12:09
Iain Clarke, Warrior Programmer22-Apr-05 12:09 
GeneralRe: Serializing data Pin
MeShadow25-Apr-05 8:20
MeShadow25-Apr-05 8:20 
GeneralRe: Serializing data Pin
Ravi Bhavnani22-Apr-05 13:26
professionalRavi Bhavnani22-Apr-05 13:26 
GeneralRe: Serializing data Pin
MeShadow25-Apr-05 8:14
MeShadow25-Apr-05 8:14 
Generallow level of math knowledge Pin
Anonymous4-Feb-05 14:39
Anonymous4-Feb-05 14:39 
GeneralRe: low level of math knowledge Pin
Iain Clarke, Warrior Programmer6-Feb-05 2:17
Iain Clarke, Warrior Programmer6-Feb-05 2:17 
GeneralRe: low level of math knowledge Pin
fkoestner7-Feb-05 7:44
fkoestner7-Feb-05 7:44 
GeneralRe: low level of math knowledge Pin
Ed The C3-May-05 6:55
Ed The C3-May-05 6:55 
GeneralRe: low level of math knowledge Pin
Chris Hills7-Sep-05 10:11
Chris Hills7-Sep-05 10:11 
Generallearn some linear algebra. Pin
Anonymous3-Feb-05 13:42
Anonymous3-Feb-05 13:42 
GeneralRe: learn some linear algebra. Pin
Iain Clarke, Warrior Programmer4-Feb-05 0:42
Iain Clarke, Warrior Programmer4-Feb-05 0:42 
GeneralRe: learn some linear algebra. Pin
padpub31-Dec-05 9:35
padpub31-Dec-05 9:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.