From: warwick@cs.uq.oz.au (Warwick Allison)
Newsgroups: rec.games.programmer
Subject: Sprite ROTATION code (anti-aliased, etc.)
Date: 2 Sep 92 08:40:34 GMT
Reply-To: warwick@cs.uq.oz.au


The code below is VERBATIM of the code I have at home.  There is a small
amount of non-interesting stuff (such as the lines that actually save the
sprites), but I've left it all together.  Also, it is written in C++, but
I haven't really used any C++ features, other than having declarations
occuring after statements.  Use it as a code example of the previously
described algorithm that involved reducing and rotating an image.

Also, please don't take this as an example of my coding ability - I know
it is ugly, but that's because I just translated it directly from the
Modula-2 code that I originally wrote it in.


Anyway, here it is...

------ Cut here ------


#include <math.h>
#include <Screen.h>
#include "SmallGraphics.h"
#include <Sprite.h>
#include <stdlib.h>
#include <stdio.h>
#include <bool.h>
#include <EasySprite.h>
#include <osbind.h>

const double PI=3.1415926535897;

static double DestinationCentreX=8.0;
static double DestinationCentreY=8.0;
static double SourceCentreX=16.0;
static double SourceCentreY=16.0;
static int SourceWidth=32;
static int SourceHeight=32;
static double Reduction=2.0;

static int Subdivisions=4;
static inca=0;
static int CurrentFeatures=SP_COLOUR;
static int DestinationWidth=16;
static int DestinationHeight=16;
const int SourceX=1;
const int SourceY=1;
static int SourceSpacingX=33;
static int SourceSpacingY=33;
static int SourcesAcross=9;
static int NumRotations=32;

static int CancelProgram=0;
static double xxif,xyif,yxif,yyif;
static Screen *Source, *View, *Desk;


double Trunc(double x) { return (double)(int)x; }


void Rotate(double angle, double x, double y, double& ToX, double& ToY)
{
	double r,o;

	x=x-DestinationCentreX;
	y=y-DestinationCentreY;
	r=sqrt(x*x+y*y);
	if (r<0.000001) {
		o=0.0; // Anything.
	} else {
		o=atan2(y,x)+angle;
	}

	ToX=r*cos(o);
	ToY=r*sin(o);
}


void FindRotatedSource(double angle, double x, double y, double& ToX, double& ToY)
{
	Rotate(angle,x,y,ToX,ToY);

	ToX=ToX*Reduction+SourceCentreX;
	ToY=ToY*Reduction+SourceCentreY;
}


double GreenLevel(short C)
{
	return (double)((C>>4)&7);
}

double RedLevel(short C)
{
	return (double)((C>>8)&7);
}

double BlueLevel(short C)
{
	return (double)((C>>0)&7);
}


short ColourSimilarTo(double R, double G, double B, short Except)
{
	short C,Col,BestC;
	double Difference,MinDifference;

	MinDifference=HUGE;
	BestC=1;

	for (Col=0; Col<16; Col++) if (Col != Except) {
		C=Source->Colour()[Col];
		Difference=fabs(R-RedLevel(C))+fabs(G-GreenLevel(C))+fabs(B-BlueLevel(C));
		if (Difference<MinDifference) {
			MinDifference=Difference;
			BestC=Col;
		}
	}

	return BestC;
}


void CalculateColour(double x, double y, int XX, int YY)
{
	short C;
	double Red=0.0,Green=0.0,Blue=0.0;
	double fx,fy;
	short X,Y;

	short BackgroundColourIndex=ColourAt(XX,YY);
	short BackgroundColour=C=Source->Colour()[BackgroundColourIndex];

	bool NonBackground = FALSE;

	for (X=1; X<=Subdivisions; X++) {
		fx=x;
		fy=y;
		for (Y=1; Y<=Subdivisions; Y++) {
			int rfx=int(fx+0.0000001);
			int rfy=int(fy+0.0000001);

			if (rfx<0 || rfy<0 || rfx>=SourceWidth || rfy>=SourceHeight) {
				C=BackgroundColour;
			} else {
				short CI=ColourAt(XX+rfx,YY+rfy);
				if (CI==BackgroundColourIndex) {
					C=BackgroundColour;
				} else {
					C=Source->Colour()[CI];
					NonBackground = TRUE;
				}
			}

			Red=Red+RedLevel(C);
			Green=Green+GreenLevel(C);
			Blue=Blue+BlueLevel(C);

			fx=fx+yxif;
			fy=fy+yyif;
		}
		x=x+xxif;
		y=y+xyif;
	}

	Red=Red / Subdivisions / Subdivisions;
	Green=Green / Subdivisions / Subdivisions;
	Blue=Blue / Subdivisions / Subdivisions;

	if (NonBackground) Colour(ColourSimilarTo(Red,Green,Blue,BackgroundColourIndex));
	else Colour(0);
}


void CalculateSource(short Of, int& x, int& y)
{
	x=SourceX+SourceSpacingX*(Of % SourcesAcross);
	y=SourceY+SourceSpacingY*(Of / SourcesAcross);
}

void CreateRotatedIncarnations(short& Pic, Sprite* Sp, double hotx, double hoty)
{
	double xxi,yyi,xyi,yxi;
	double angle;
	int XX,YY;
	double fx,fy,gx,gy;
	double x1,y1,x2,y2,x3,y3;
	short I;

	View->Clear();

	CalculateSource(Pic,XX,YY);

	angle=0.0;

	for (I=0; I<NumRotations/4; I++) {
		FindRotatedSource(angle,0.0,0.0,x1,y1);
		FindRotatedSource(angle,1.0,0.0,x2,y2);
		FindRotatedSource(angle,0.0,1.0,x3,y3);

		xxi=x2-x1;
		xyi=y2-y1;
		yxi=x3-x1;
		yyi=y3-y1;
		xxif=xxi/Subdivisions;
		xyif=xyi/Subdivisions;
		yxif=yxi/Subdivisions;
		yyif=yyi/Subdivisions;

		gx=x1;
		gy=y1;
		for (int x=0; x<DestinationWidth; x++) {
			fx=gx;
			fy=gy;
			for (int y=0; y<DestinationHeight; y++) {
				Source->Use();
				CalculateColour(fx,fy,XX,YY);
				View->Use();
				Plot(x,y);
				Plot(DestinationWidth+y,DestinationHeight-1-x);
				Plot(DestinationWidth*3-1-x,DestinationHeight-1-y);
				Plot(DestinationWidth*4-1-y,x);

				fx=fx+yxi;
				fy=fy+yyi;
			}
			gx=gx+xxi;
			gy=gy+xyi;

			if (Cconis()) { CancelProgram=1; return; }
		}

		for (int X=0; X<4; X++) {
			Rotate(2*PI-(angle+X*PI/2.0),hotx,hoty,x1,y1);
			Incarnation* Inc=GetIncarnation(*View,X*DestinationWidth,0,DestinationHeight,CurrentFeatures);
			Inc->SetHotSpot(int(x1+DestinationCentreX),int(y1+DestinationCentreY));
			Sp->SetImage(inca+I+(NumRotations/4*X),Inc);
		}

		angle=angle+2.0*PI/NumRotations;
	}
	inca+=NumRotations;
}


void CreateShrunkIncarnation(short Pic, Sprite* Sp, double hotx, double hoty)
{
	double xxi,yyi,xyi,yxi;
	short X,Y,x,y;
	double fx,fy,gx,gy;
	int XX,YY;
	short I;

	View->Clear();

	CalculateSource(Pic,XX,YY);

	xxi=Reduction;
	yyi=Reduction;
	xxif=xxi/Subdivisions;
	yyif=yyi/Subdivisions;
	xyif=0;
	yxif=0;

	gx=0;
	for (x=0; x<DestinationWidth; x++) {
		fx=gx;
		fy=0;
		for (y=0; y<DestinationHeight; y++) {
			Source->Use();
			CalculateColour(fx,fy,XX,YY);
			View->Use();
			Plot(x,y);
			fx=fx+yxi;
			fy=fy+yyi;
		}
		gx=gx+xxi;

		if (Cconis()) { CancelProgram=1; return; }
	}

	Incarnation* Inc=GetIncarnation(*View,0,0,DestinationHeight,CurrentFeatures);
	Inc->SetHotSpot(int(hotx),int(hoty));
	Sp->SetImage(inca++,Inc);
}


void CreateDirectIncarnation(short Pic, Sprite* Sp, double hotx, double hoty)
{
	int XX,YY;
	short X,Y,x,y;

	CalculateSource(Pic,XX,YY);
	x=XX;
	y=YY;

	for (X=0; X<DestinationWidth; X++) {
		for (Y=0; Y<DestinationHeight; Y++) {
			Source->Use();
			Colour(ColourAt(x+X,y+Y));
			View->Use();
			Plot(X,Y);
		}

		if (Cconis()) { CancelProgram=1; return; }
	}

	Incarnation* Inc=GetIncarnation(*View,0,0,DestinationHeight,CurrentFeatures);
	Inc->SetHotSpot(int(hotx),int(hoty));
	Sp->SetImage(inca++,Inc);
}


main(int argc, char* argv[])
{
	if (argc<3) {
		fprintf(stderr,"Usage: %s cmd {cmd}\n\n",argv[0]);
		fprintf(stderr,"  D SPheight ImgW ImgH        Set dimensions\n");
		fprintf(stderr,"  I imagefile                 Load image file\n");
		fprintf(stderr,"  S savefile                  Set sprite save file\n");
		fprintf(stderr,"  R #rotations                Set # rotations\n");
		fprintf(stderr,"  s #incarnations             New sprite\n");
		fprintf(stderr,"  r from hotx hoty            Add n rotated incas\n");
		fprintf(stderr,"  m from hotx hoty            Add shrunk inca\n");
		fprintf(stderr,"  d from hotx hoty            Add direct inca\n");
		fprintf(stderr,"  a accuracy                  Set pixel subdivision\n");
		fprintf(stderr,"  f features                  Set current features\n");
		fprintf(stderr,"      (w=wide, f=fast)\n");
		exit(1);
	}

	InitSmallGraphics();

	ResolutionChange FromOriginal(STLow);

	Desk=new Screen(DESKSCREEN);
	View=new Screen;
	Source=new Screen;

	View->Show();

	Sprite* Sp=0;
	short Pic;
	double hotx,hoty;
	FILE* fd=0;

	for (int arg=1; arg<argc && !CancelProgram; arg++) {
		switch (argv[arg][0]) {
		 case 'I':
			Source->Load(argv[++arg]);
			Source->ShowPalette();
		break; case 'D':
			DestinationHeight=atoi(argv[++arg]);
			SourceWidth=atoi(argv[++arg]);
			SourceHeight=atoi(argv[++arg]);

			SourceCentreX=SourceWidth/2;
			SourceCentreY=SourceHeight/2;
			DestinationCentreY=DestinationHeight/2;
			Reduction=(double)SourceHeight/DestinationHeight;
			SourceSpacingX=SourceWidth+1;
			SourceSpacingY=SourceHeight+1;
			SourcesAcross=ScreenWidth[STLow]/SourceSpacingX;
		break; case 'R':
			NumRotations=atoi(argv[++arg]);
		break; case 'S':
			if (fd) fclose(fd);
			fd=fopen(argv[++arg],"wb");
		break; case 's':
			if (Sp && fd) Sp->fput(fd);
			inca=0;
			Sp=new Sprite(atoi(argv[++arg]));
		break; case 'r':
			Pic=atoi(argv[++arg]);
			hotx=atof(argv[++arg]);
			hoty=atof(argv[++arg]);
			CreateRotatedIncarnations(Pic,Sp,hotx,hoty);
		break; case 'm':
			Pic=atoi(argv[++arg]);
			hotx=atof(argv[++arg]);
			hoty=atof(argv[++arg]);
			CreateShrunkIncarnation(Pic,Sp,hotx,hoty);
		break; case 'd':
			Pic=atoi(argv[++arg]);
			hotx=atof(argv[++arg]);
			hoty=atof(argv[++arg]);
			CreateDirectIncarnation(Pic,Sp,hotx,hoty);
		break; case 'a':
			Subdivisions=atoi(argv[++arg]);
		break; case 'f':
			arg++;
			CurrentFeatures=SP_COLOUR;
			DestinationWidth=16;
			for (int c=0; argv[arg][c]; c++) {
				switch (argv[arg][c]) {
				 case 'w':
					CurrentFeatures|=SP_WIDE;
					DestinationWidth=32;
				break; case 'f':
					CurrentFeatures|=SP_FAST;
				}
			}
			DestinationCentreX=DestinationWidth/2;
		}
	}

	if (Sp && fd) Sp->fput(fd);
	if (fd) fclose(fd);

	Desk->ShowPalette();
	Desk->Show();
	Desk->Use();
}


------ Cut here ------
--
  _-_|\      warwick@cs.uq.oz.au            /Disclaimer:
 /     * <-- Computer Science Department,  /  
 \_.-._/     University of Queensland,    /      void (if removed)
      v      Brisbane, Australia.        /  
Newsgroups: rec.games.programmer,comp.os.coherent