Line Art Rotation
Volume Number: 6
Issue Number: 5
Column Tag: C Forum
Related Info: Quickdraw
Line Art Rotation
By Jeffrey J. Martin, College Station, TX
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
[ Jeff Martin is a student at Texas A&M University working on his bachelors in
computer science. He has been a personal computer technician at the campus computer
center, a system operator on the campus mainframes, and now freelances graphic work
for various professors. He hopes that one day a motion picture computer animation
company will take him away from all of this.]
This being my first stab at an article, I will try to keep it short while leaving in
all of the essential vitamins and nutrients. In that spirit my user interface will bring
back nostalgic thoughts to those past Apple II and TRS-80 users, and any PC people
will feel right at home.
The essence of this program is to show how a seemingly complicated
transformation and rotation can be applied to an array of points that form any
arbitrary line art.
Of course to form a transformation on the array of points (e.g. offset the points to
the left) we simply add some delta x(dx) and/or delta y(dy) to every point:
/* 1 */
for(i=0;i
{points[i].h+=dx;points[i].v+=dy;}
Now rotation is a little harder, but to spare you the heartache, it can be shown
that for rotation about the origin(fig 1):
So the trick of rotating about some arbitrary point is to first transform that
pivot point to be the origin(transforming every other point by the save amount).
Second, perform the rotation of all points by the angle theta. Third, transform the
pivot back(once again transforming all other points as well).
Now all of this may seem to be a costly maneuver, but the fact is that we can roll
all of these into a single matrix multiplication, using homogeneous coordinates:
where
form one matrix.
Fig. 2 shows the multiplication of a homogeneous coordinate and a translation
matrix. Please verify that this results in (X+dx,Y+dy) (if unfamiliar with matrix
multiplication see mult procedure in program).
Similarly figure 3 shows multiplication with a rotation matrix - an exact
translation of our rotation equations in matix form.
So the translation, rotation, and inverse translation matrices are as shown in
figure 4. Which forms one matrix to be multiplied times the vertices.
The following program allows the user to enter in points with the mouse until a
key is pressed. At that time the user then uses the mouse to enter a pivot point. The
program uses the pivot point to form the translation and inverse translation
matrices(from the x and y coordinates). The program then forms a rotation matrix of
a constant rotation angle(π/20) and calculates the new vertices based on the values of
the old ones. The program undraws the old lines and redraws the new and calculates
again until the object has rotated through a shift of 4π(2 rotations). press the mouse
button again to exit program.
Once again, I point out that the code does not follow the user guidelines, but then
it is not exactly meant to be an application in itself. Build your own program around it
and see what you can do. One suggestion is to cancel the erasing of the object to achieve
spirograph patterns. I think too many of the submissions to MacTutor contain an
interface that we all know too well, and for those just interested in the algorithms it
can mean a lot of extra work. Have Fun.
/* 2 */
#include
int errno;
void mult(); /*out matrix mult proc*/
/*floating value of points to avoid roundoff*/
typedef struct rec {float h,v;} points;
main()
{
int buttondown=0, /*flagg for mouse */
n=-1, /*number of vertices */
keypressed=0, /*flagg for key */
flip=0, /*to allow alternating */
flop=1, /*vertices to be drawn */
i; /*array counter */
float x, /*angle counter */
T[3][3], /*translation matrix */
Tinv[3][3], /*translate back */
Rz[3][3], /*rotate matrix */
c[3][3], /*result of T&R */
d[3][3]; /*result of c&Tinv */
long curtick, /*for delay loop */
lastick; /*for delay loop */
EventRecord next event;/*to get mouse&key */
Point origin,dummy; /*pivot and locator */
points points[2][30];/*vertices(don’t draw Eiffel tower) */
WindowPtr scnwdw; /*window pointer */
Rect scnrect; /*window rect */
/*************************************
* Set things up *
*************************************/
InitGraf(&thePort);
InitFonts();
InitWindows();
InitDialogs((Ptr)0L);
TEInit();
InitMenus();
scnrect=screenBits.bounds;
InsetRect(&scnrect,10,25);
scnwdw=NewWindow(0,&scnrect,”\p”,TRUE,dBoxProc, -1,FALSE,0);
SetPort(scnwdw);
InitCursor();

/*************************************
* Get points *
*************************************/
while(!keypressed)
{
buttondown=0;
SystemTask();
if(GetNextEvent(-1,&next event))
if(next event.what==mouseDown) buttondown=1;
else if(next event.what==keyDown) keypressed=1;
if( buttondown) /*get a point and draw it*/
{
GetMouse(&dummy);
points[0][++n].h=dummy.h;points[0][n].v=dummy.v;
if(n==0)
MoveTo((int)points[0][0].h,(int)points[0][0].v);
LineTo((int)points[0][n].h,(int)points[0][n].v);
} /*end of get point*/
} /*end of get points*/

/*************************************
* Get origin *
*************************************/
buttondown=0;
do
{
SystemTask();
if(GetNextEvent(-1,&next event))
if(next event.what==mouseDown) buttondown=1;
}while(! buttondown);
GetMouse(&origin);

/*************************************
* Make translation matrix *
*************************************/
T[0][0]=1;T[0][1]=0;T[0][2]=0;
T[1][0]=0;T[1][1]=1;T[1][2]=0;
T[2][0]=-origin.h;T[2][1]=-origin.v;T[2][2]=1;
Tinv[0][0]=1;Tinv[0][1]=0;Tinv[0][2]=0;
Tinv[1][0]=0;Tinv[1][1]=1;Tinv[1][2]=0;
Tinv[2][0]=origin.h;Tinv[2][1]=origin.v;Tinv[2][2]=1;
Rz[0][2]=0;Rz[1][2]=0;Rz[2][0]=0;Rz[2][1]=0;Rz[2][2]=1;
/*************************************
* Rotate *
*************************************/
x=0.157; /*rotation angle - about 9 degrees*/
Rz[0][0]=Rz[1][1]=cos(x);Rz[0][1]=sin(x);
Rz[1][0]=-Rz[0][1];
mult(T,Rz,c);
mult(c,Tinv,d);
for(x=.157;x<=12.56;x+=0.157)
{
flip++;flip=flip%2;flop++;flop=flop%2;
for(i=0;i<=n;i++)
{
points[flip][i].h=points[flop][i].h*d[0][0]
+points[flop][i].v*d[1][0]+1*d[2][0];
points[flip][i].v=points[flop][i].h*d[0][1]
+points[flop][i].v*d[1][1]+1*d[2][1];
} /*end update points*/
ForeColor(whiteColor); /*undraw flop*/
lastick=TickCount(); /*time delay for retace to improve
animation*/
do{curtick=TickCount();} while(lastick+1>curtick);
MoveTo((int)points[flop][0].h,(int)points[flop][0].v);
for(i=1;i<=n;i++)
LineTo((int)points[flop][i].h,(int)points[flop][i].v);
ForeColor(blackColor); /*draw flip*/
lastick=TickCount();
do{curtick=TickCount();} while(lastick+1>curtick);
MoveTo((int)points[flip][0].h,(int)points[flip][0].v);
for(i=1;i<=n;i++)
LineTo((int)points[flip][i].h,(int)points[flip][i].v);
} /*end rotate*/

/*************************************
* End everything *
*************************************/
buttondown=0;
do
{
SystemTask();
if(GetNextEvent(-1,&next event))
if(next event.what==mouseDown) buttondown=1;
}while(! buttondown);
DisposeWindow(scnwdw);
} /*program end*/
void mult(A,B,C)
float A[][3],B[][3],C[][3];
{
int i,j,k;

for(i=0;i<=2;i++)
for(j=0;j<=2;j++)
{
C[i][j]=0.0;
for(k=0;k<=2;k++)
C[i][j]+=A[i][k]*B[k][j];