#include "matrix.h"
typedef enum {
MatrixIdentity=0x01,
MatrixTranslation=0x02,
MatrixScaling=0x04,
MatrixUnknown=0x08
} MATRIXFLAGS;
typedef struct{
double eM11;
double eM12;
double eM21;
double eM22;
double eDx;
double eDy;
DWORD flags;
DWORD padding;
} MATRIXINTERNAL;
#ifdef DEBUG
#define ASSERTMATRIX(x) AssertMatrix(x,__FILE__,__LINE__)
#else
#define ASSERTMATRIX(x)
#endif
static void AssertMatrix(MATRIX *matrix, char *file, int line){
DWORD flags=0;
MATRIXINTERNAL *mat=(MATRIXINTERNAL*)matrix;
if(!mat)return;
if(mat->eM12!=0.0 || mat->eM21!=0.0){
flags|=MatrixUnknown;
} else if(mat->eM11==1.0 && mat->eM22==1.0){
flags|=MatrixIdentity;
} else {
flags|=MatrixScaling;
}
if(mat->eDx!=0.0 || mat->eDy!=0.0){
flags|=MatrixTranslation;
}
if((mat->flags&MatrixIdentity)!=(flags&MatrixIdentity)||
((flags&MatrixScaling)&&!(mat->flags&(MatrixScaling|MatrixIdentity)))||
((flags&MatrixTranslation)&&!(mat->flags&MatrixTranslation))
){
}
}
BOOL MatrixEquals(MATRIX *matrix, MATRIX *matrix2){
MATRIXINTERNAL *mat1=(MATRIXINTERNAL*)matrix;
MATRIXINTERNAL *mat2=(MATRIXINTERNAL*)matrix2;
if(mat1==mat2)return TRUE;
if(!mat1||!mat2)return FALSE;
return (mat1->eM11==mat2->eM11&&
mat1->eM12==mat2->eM12&&
mat1->eM21==mat2->eM21&&
mat1->eM22==mat2->eM22&&
mat1->eDx==mat2->eDx&&
mat1->eDy==mat2->eDy);
}
BOOL MatrixAreClose(MATRIX *matrix, MATRIX *matrix2){
MATRIXINTERNAL *mat1=(MATRIXINTERNAL*)matrix;
MATRIXINTERNAL *mat2=(MATRIXINTERNAL*)matrix2;
if(mat1==mat2)return TRUE;
if(!mat1||!mat2)return FALSE;
return (abs(mat1->eM11-mat2->eM11)<=1e-12&&
abs(mat1->eM12-mat2->eM12)<=1e-12&&
abs(mat1->eM21-mat2->eM21)<=1e-12&&
abs(mat1->eM22-mat2->eM22)<=1e-12&&
abs(mat1->eDx-mat2->eDx)<=1e-12&&
abs(mat1->eDy-mat2->eDy)<=1e-12);
}
void MatrixTransformPoints(MATRIX *matrix, double *pts, DWORD numpts){
DWORD i;
MATRIXINTERNAL *mat=(MATRIXINTERNAL*)matrix;
if(!mat||mat->flags==MatrixIdentity)return;
for(i=0;i<numpts;i++){
if(mat->flags==MatrixTranslation){
pts[0]+=mat->eDx;
pts[1]+=mat->eDy;
} else if(mat->flags==MatrixScaling){
pts[0]*=mat->eM11;
pts[1]*=mat->eM22;
} else if(mat->flags==(MatrixScaling|MatrixTranslation)){
pts[0]*=mat->eM11;
pts[1]*=mat->eM22;
pts[0]+=mat->eDx;
pts[1]+=mat->eDy;
} else {
double x,y;
x=mat->eM11*pts[0]+mat->eM21*pts[1]+mat->eDx;
y=mat->eM12*pts[0]+mat->eM22*pts[1]+mat->eDy;
pts[0]=x;
pts[1]=y;
}
pts+=2;
}
}
void MatrixTransformVectors(MATRIX *matrix, double *pts, DWORD numpts){
DWORD i;
MATRIXINTERNAL *mat=(MATRIXINTERNAL*)matrix;
if(!mat||(mat->flags&MatrixIdentity))return;
for(i=0;i<numpts;i++){
if(mat->flags&MatrixScaling){
pts[0]*=mat->eM11;
pts[1]*=mat->eM22;
} else {
double x,y;
x=mat->eM11*pts[0]+mat->eM21*pts[1];
y=mat->eM12*pts[0]+mat->eM22*pts[1];
pts[0]=x;
pts[1]=y;
}
pts+=2;
}
}
double MatrixDeterminant(MATRIX *matrix){
MATRIXINTERNAL *mat=(MATRIXINTERNAL*)matrix;
if(!mat)return 0.0;
ASSERTMATRIX(mat);
if(mat->flags&MatrixIdentity){
return 1.0;
} else if(mat->flags&MatrixScaling){
return mat->eM11*mat->eM22;
} else {
return mat->eM11*mat->eM22-mat->eM12*mat->eM21;
}
}
static inline void MatrixUpdate(MATRIX *matrix){
MATRIXINTERNAL *mat=(MATRIXINTERNAL*)matrix;
mat->flags=0;
if(mat->eM12!=0.0 || mat->eM21!=0.0){
mat->flags|=MatrixUnknown;
} else if(mat->eM11==1.0 && mat->eM22==1.0){
mat->flags|=MatrixIdentity;
} else {
mat->flags|=MatrixScaling;
}
if(mat->eDx!=0.0 || mat->eDy!=0.0){
mat->flags|=MatrixTranslation;
}
}
double MatrixGetValue(MATRIX *matrix, MATRIXELEMENT elem){
double *values=(double*)matrix;
if(!values||elem<0||elem>=6)return 0.0;
return values[elem];
}
BOOL MatrixSetValue(MATRIX *matrix, MATRIXELEMENT elem, double value){
double *values=(double*)matrix;
if(!values||elem<0||elem>=6)return FALSE;
values[elem]=value;
MatrixUpdate(matrix);
return TRUE;
}
BOOL MatrixGetValueArray(MATRIX *matrix, double *values){
MATRIXINTERNAL *mat=(MATRIXINTERNAL *)matrix;
if(!matrix||!values)return FALSE;
values[0]=mat->eM11;
values[1]=mat->eM12;
values[2]=mat->eM21;
values[3]=mat->eM22;
values[4]=mat->eDx;
values[5]=mat->eDy;
return TRUE;
}
BOOL MatrixSetValueArray(MATRIX *matrix, double *values){
MATRIXINTERNAL *mat=(MATRIXINTERNAL *)matrix;
if(!matrix||!values)return FALSE;
mat->eM11=values[0];
mat->eM12=values[1];
mat->eM21=values[2];
mat->eM22=values[3];
mat->eDx=values[4];
mat->eDy=values[5];
MatrixUpdate(mat);
return TRUE;
}
BOOL MatrixIsInvertible(MATRIX *matrix){
double det;
det=MatrixDeterminant(matrix);
return !(det>-1e-12&&det<1e-12);
}
BOOL MatrixInvert(MATRIX *matrixDst, MATRIX *matrixSrc){
double det;
MATRIXINTERNAL tmp;
MATRIXINTERNAL *matDst=(MATRIXINTERNAL*)matrixDst;
MATRIXINTERNAL *matSrc=(MATRIXINTERNAL*)matrixSrc;
ASSERTMATRIX(matDst);
ASSERTMATRIX(matSrc);
if(matSrc->flags==MatrixIdentity){
MatrixSetIdentity(matDst);
return TRUE;
}
det=MatrixDeterminant(matSrc);
if(det>-1e-12&&det<1e-12){
MatrixSetIdentity(matDst);
return FALSE;
}
tmp.eM11=matSrc->eM22/det;
tmp.eM12=-matSrc->eM12/det;
tmp.eM21=-matSrc->eM21/det;
tmp.eM22=matSrc->eM11/det;
tmp.eDx=-matSrc->eDx*tmp.eM11-matSrc->eDy*tmp.eM21;
tmp.eDy=-matSrc->eDx*tmp.eM12-matSrc->eDy*tmp.eM22;
MatrixUpdate(&tmp);
*(MATRIXINTERNAL *)matDst=tmp;
return TRUE;
}
BOOL MatrixIsIdentity(MATRIX *matrix){
MATRIXINTERNAL *mat=(MATRIXINTERNAL*)matrix;
if(!mat)return FALSE;
ASSERTMATRIX(mat);
return (mat->flags==MatrixIdentity||(
mat->eM11==1.0&&mat->eM22==1.0&&mat->eM12==0.0&&
mat->eM21==0.0&&mat->eDx==0.0&&mat->eDy==0.0
));
}
void MatrixSetIdentity(MATRIX *matrix){
MATRIXINTERNAL *mat=(MATRIXINTERNAL*)matrix;
mat->eM11=1.0;
mat->eM12=0.0;
mat->eM21=0.0;
mat->eM22=1.0;
mat->eDx=0.0;
mat->eDy=0.0;
mat->flags=MatrixIdentity;
}
void MatrixSetValues(MATRIX *matrix, double m11, double m12, double m21, double m22, double dx, double dy){
MATRIXINTERNAL *mat=(MATRIXINTERNAL*)matrix;
mat->eM11=m11;
mat->eM12=m12;
mat->eM21=m21;
mat->eM22=m22;
mat->eDx=dx;
mat->eDy=dy;
MatrixUpdate(mat);
}
static void DebugOutMatrixInternal(MATRIXINTERNAL *mat){
DebugOut("%f %f %f %f %f %f: flags=%04X",mat->eM11,mat->eM12,mat->eM21,mat->eM22,mat->eDx,mat->eDy,mat->flags);
}
void MatrixMultiply(MATRIX *matrixResult, MATRIX *matrixLeft, MATRIX *matrixRight){
MATRIXINTERNAL mat;
MATRIXINTERNAL *matResult=(MATRIXINTERNAL*)matrixResult;
MATRIXINTERNAL *matRight=(MATRIXINTERNAL*)matrixRight;
MATRIXINTERNAL *matLeft=(MATRIXINTERNAL*)matrixLeft;
if(!matResult||!matRight||!matLeft)
return;
ASSERTMATRIX(matRight);
ASSERTMATRIX(matLeft);
if(matLeft->flags!=MatrixIdentity){
if(matRight->flags==MatrixIdentity){
mat=*matLeft;
} else if(matRight->flags&MatrixIdentity){
mat.eM11=matLeft->eM11;
mat.eM12=matLeft->eM12;
mat.eM21=matLeft->eM21;
mat.eM22=matLeft->eM22;
mat.eDx=matLeft->eDx+matRight->eDx;
mat.eDy=matLeft->eDy+matRight->eDy;
MatrixUpdate(&mat);
} else if(matLeft->flags&MatrixIdentity){
mat.eM11=matRight->eM11;
mat.eM12=matRight->eM12;
mat.eM21=matRight->eM21;
mat.eM22=matRight->eM22;
mat.eDx=matLeft->eDx*matRight->eM11+matLeft->eDy*matRight->eM21+matRight->eDx;
mat.eDy=matLeft->eDx*matRight->eM12+matLeft->eDy*matRight->eM22+matRight->eDy;
MatrixUpdate(&mat);
} else if(matRight->flags==MatrixScaling && matLeft->flags==MatrixScaling){
mat.eM21=mat.eM12=mat.eDx=mat.eDy=0.0;
mat.eM11=matRight->eM11*matLeft->eM11;
mat.eM22=matRight->eM22*matLeft->eM22;
mat.flags=MatrixScaling;
} else if(matRight->flags&MatrixScaling){
mat.eM11=matRight->eM11*matLeft->eM11;
mat.eM21=matRight->eM11*matLeft->eM21;
mat.eM12=matRight->eM22*matLeft->eM12;
mat.eM22=matRight->eM22*matLeft->eM22;
if(matLeft->flags&MatrixTranslation){
mat.eDx=matLeft->eDx*matRight->eM11+matRight->eDx;
mat.eDy=matLeft->eDy*matRight->eM22+matRight->eDy;
} else {
mat.eDx=matRight->eDx;
mat.eDy=matRight->eDy;
}
MatrixUpdate(&mat);
} else if(matLeft->flags&MatrixScaling){
mat.eM11=matRight->eM11*matLeft->eM11;
mat.eM21=matRight->eM21*matLeft->eM22;
mat.eM12=matRight->eM12*matLeft->eM11;
mat.eM22=matRight->eM22*matLeft->eM22;
if(matLeft->flags&MatrixTranslation){
mat.eDx=matLeft->eDx*matRight->eM11+matLeft->eDy*matRight->eM21+matRight->eDx;
mat.eDy=matLeft->eDx*matRight->eM12+matLeft->eDy*matRight->eM22+matRight->eDy;
} else {
mat.eDx=matRight->eDx;
mat.eDy=matRight->eDy;
}
MatrixUpdate(&mat);
} else {
mat.eM11=matRight->eM11*matLeft->eM11+matRight->eM21*matLeft->eM12;
mat.eM21=matRight->eM11*matLeft->eM21+matRight->eM21*matLeft->eM22;
mat.eM12=matRight->eM12*matLeft->eM11+matRight->eM22*matLeft->eM12;
mat.eM22=matRight->eM12*matLeft->eM21+matRight->eM22*matLeft->eM22;
if(matLeft->flags&MatrixTranslation){
mat.eDx=matLeft->eDx*matRight->eM11+matLeft->eDy*matRight->eM21+matRight->eDx;
mat.eDy=matLeft->eDx*matRight->eM12+matLeft->eDy*matRight->eM22+matRight->eDy;
} else {
mat.eDx=matRight->eDx;
mat.eDy=matRight->eDy;
}
MatrixUpdate(&mat);
}
} else {
mat=*matRight;
}
*matResult=mat;
}
static void MatrixApply(MATRIX *matrix, MATRIXINTERNAL *matSrc, MATRIXMODE mode){
if(mode==MatrixSet || ((MATRIXINTERNAL*)matrix)->flags==MatrixIdentity){
*(MATRIXINTERNAL*)matrix=*matSrc;
} else if(mode==MatrixAppend){
MatrixMultiply(matrix,matrix,(MATRIX*)matSrc);
} else if(mode==MatrixPrepend){
MatrixMultiply(matrix,(MATRIX*)matSrc,matrix);
}
}
void MatrixTranslate(MATRIX *matrix, double x, double y, MATRIXMODE mode){
MATRIXINTERNAL *mat=(MATRIXINTERNAL*)matrix;
ASSERTMATRIX(mat);
if(mode==MatrixSet || mat->flags==MatrixIdentity){
mat->eM11=1.0;
mat->eM12=0.0;
mat->eM21=0.0;
mat->eM22=1.0;
mat->eDx=x;
mat->eDy=y;
mat->flags=MatrixTranslation|MatrixIdentity;
} else if(mode==MatrixAppend){
mat->eDx+=x;
mat->eDy+=y;
mat->flags|=MatrixTranslation;
} else if(mode==MatrixPrepend){
if(mat->flags&MatrixIdentity){
mat->eDx+=x;
mat->eDy+=y;
} else if(mat->flags&MatrixScaling){
mat->eDx+=x*mat->eM11;
mat->eDy+=y*mat->eM22;
} else {
mat->eDx+=x*mat->eM11+y*mat->eM21;
mat->eDy+=x*mat->eM12+y*mat->eM22;
}
mat->flags|=MatrixTranslation;
}
}
void MatrixRotate(MATRIX *matrix, double degrees, MATRIXMODE mode){
MATRIXINTERNAL mat;
double rad,sinrad,cosrad;
if(!matrix)return;
ASSERTMATRIX(matrix);
rad=degrees*DEGTORAD;
sinrad=sin(rad);
cosrad=cos(rad);
mat.eM11=cosrad;
mat.eM12=sinrad;
mat.eM21=-sinrad;
mat.eM22=cosrad;
mat.eDx=0.0;
mat.eDy=0.0;
mat.flags=MatrixUnknown;
MatrixApply(matrix,&mat,mode);
}
void MatrixRotateAt(MATRIX *matrix, double degrees, double x, double y, MATRIXMODE mode){
MATRIXINTERNAL mat;
double rad,sinrad,cosrad;
ASSERTMATRIX(matrix);
rad=degrees*DEGTORAD;
sinrad=sin(rad);
cosrad=cos(rad);
mat.eM11=cosrad;
mat.eM12=sinrad;
mat.eM21=-sinrad;
mat.eM22=cosrad;
mat.eDx=-x*cosrad+y*sinrad+x;
mat.eDy=-x*sinrad-y*cosrad+y;
mat.flags=MatrixUnknown|MatrixTranslation;
MatrixApply(matrix,&mat,mode);
}
void MatrixScale(MATRIX *matrix, double x, double y, MATRIXMODE mode){
MATRIXINTERNAL mat;
ASSERTMATRIX(matrix);
mat.eM11=x;
mat.eM12=0.0;
mat.eM21=0.0;
mat.eM22=y;
mat.eDx=0.0;
mat.eDy=0.0;
mat.flags=MatrixScaling;
MatrixApply(matrix,&mat,mode);
}
void MatrixScaleAt(MATRIX *matrix, double x, double y, double centerX, double centerY, MATRIXMODE mode){
MATRIXINTERNAL mat;
ASSERTMATRIX(matrix);
mat.eM11=x;
mat.eM12=0.0;
mat.eM21=0.0;
mat.eM22=y;
mat.eDx=centerX-(x*centerX);
mat.eDy=centerY-(y*centerY);
mat.flags=MatrixScaling|MatrixTranslation;
MatrixApply(matrix,&mat,mode);
}
void MatrixSkew(MATRIX *matrix, double angleX, double angleY, MATRIXMODE mode){
MATRIXINTERNAL mat;
if(!matrix)return;
ASSERTMATRIX(matrix);
mat.eM11=1.0;
mat.eM12=tan(angleY*DEGTORAD);
mat.eM21=tan(angleX*DEGTORAD);
mat.eM22=1.0;
mat.eDx=0.0;
mat.eDy=0.0;
mat.flags=MatrixUnknown;
MatrixApply(matrix,&mat,mode);
}
void MatrixShear(MATRIX *matrix, double shearX, double shearY, MATRIXMODE mode){
MATRIXINTERNAL mat;
if(!matrix)return;
ASSERTMATRIX(matrix);
mat.eM11=1.0;
mat.eM12=shearY;
mat.eM21=shearX;
mat.eM22=1.0;
mat.eDx=0.0;
mat.eDy=0.0;
mat.flags=MatrixUnknown;
MatrixApply(matrix,&mat,mode);
}
void MatrixParallelogram(MATRIX *matrix, double *rect, double *points, MATRIXMODE mode){
MATRIXINTERNAL mat;
if(!matrix||!rect||!points)return;
ASSERTMATRIX(matrix);
if(rect[2]==0 || rect[3]==0){
mat.eM11=mat.eM22=1.0;
mat.eM12=mat.eM21=0.0;
mat.eDx=mat.eDx=0.0;
mat.flags=MatrixIdentity;
} else {
mat.eM11 = (points[2] - points[0]) / rect[2];
mat.eM12 = (points[3] - points[1]) / rect[2];
mat.eM21 = (points[4] - points[0]) / rect[3];
mat.eM22 = (points[5] - points[1]) / rect[3];
mat.eDx=-rect[0]*mat.eM11-rect[1]*mat.eM21+points[0];
mat.eDy=-rect[0]*mat.eM12-rect[1]*mat.eM22+points[1];
mat.flags=MatrixUnknown|MatrixTranslation;
}
MatrixApply(matrix,&mat,mode);
}
#if 0
#define ASSERT2(x,file,line) if(!(x)){\
DebugOut("Assertion failed (%s line %d): %s",file,line,#x); }
#define AME(m,a,b,c,d,e,f) \
AssertMatEqual(m,a,b,c,d,e,f,__FILE__,__LINE__)
static BOOL AreCloseAdv(double value1, double value2){
if (value1 == value2)return TRUE;
double num = ((abs(value1) + abs(value2)) + 10.0) * 2.2204460492503131E-16;
double num2 = value1 - value2;
return ((-num < num2) && (num > num2));
}
static BOOL AreClose(double x, double y){
return abs(x-y)<=0.0001;
}
static void AssertMatEqual(MATRIX *matrix,
double m11, double m12, double m21, double m22, double dx, double dy,
char *file, int line){
ASSERT2(AreClose(GetM11(matrix),m11),file,line);
ASSERT2(AreClose(GetM12(matrix),m12),file,line);
ASSERT2(AreClose(GetM21(matrix),m21),file,line);
ASSERT2(AreClose(GetM22(matrix),m22),file,line);
ASSERT2(AreClose(GetDx(matrix),dx),file,line);
ASSERT2(AreClose(GetDy(matrix),dy),file,line);
}
#endif