#define EXPOSE_NSAffineTransform_IVARS 1
#import "Foundation/NSArray.h"
#import "Foundation/NSException.h"
#import "Foundation/NSString.h"
#import "Foundation/NSAffineTransform.h"
#import "Foundation/NSCoder.h"
#import "Foundation/NSDebug.h"
/* Private definitions */
#define A _matrix.m11
#define B _matrix.m12
#define C _matrix.m21
#define D _matrix.m22
#define TX _matrix.tX
#define TY _matrix.tY
/* A Postscript matrix looks like this:
/ a b 0 \
| c d 0 |
\ tx ty 1 /
*/
static const CGFloat pi = 3.1415926535897932384626434;
#if 0
#define valid(o) NSAssert((o->_isIdentity && o->A==1.0 && o->B==0.0 && o->C==0.0 && o->D==1.0) || (o->_isFlipY && o->A==1.0 && o->B==0.0 && o->C==0.0 && o->D==-1.0) || !(o->_isIdentity||o->_isFlipY), NSInternalInconsistencyException)
#define check() valid(self)
#else
#define valid(o)
#define check()
#endif
/* Quick function to multiply two coordinate matrices. C = AB */
static inline NSAffineTransformStruct
matrix_multiply (NSAffineTransformStruct MA, NSAffineTransformStruct MB)
{
NSAffineTransformStruct MC;
MC.m11 = MA.m11 * MB.m11 + MA.m12 * MB.m21;
MC.m12 = MA.m11 * MB.m12 + MA.m12 * MB.m22;
MC.m21 = MA.m21 * MB.m11 + MA.m22 * MB.m21;
MC.m22 = MA.m21 * MB.m12 + MA.m22 * MB.m22;
MC.tX = MA.tX * MB.m11 + MA.tY * MB.m21 + MB.tX;
MC.tY = MA.tX * MB.m12 + MA.tY * MB.m22 + MB.tY;
return MC;
}
/*
MC.m11 = MA->A * MB->A + MA->B * MB->C;
MC.m12 = MA->A * MB->B + MA->B * MB->D;
MC.m21 = MA->C * MB->A + MA->D * MB->C;
MC.m22 = MA->C * MB->B + MA->D * MB->D;
MC.tX = MA->TX * MB->A + MA->TY * MB->C + MB->TX;
MC.tY = MA->TX * MB->B + MA->TY * MB->D + MB->TY;
*/
@implementation NSAffineTransform
static NSAffineTransformStruct identityTransform = {
1.0, 0.0, 0.0, 1.0, 0.0, 0.0
};
/**
* Return an autoreleased instance of this class.
*/
+ (NSAffineTransform*) transform
{
NSAffineTransform *t;
t = (NSAffineTransform*)NSAllocateObject(self, 0, NSDefaultMallocZone());
t->_matrix = identityTransform;
t->_isIdentity = YES;
return AUTORELEASE(t);
}
/**
* Return an autoreleased instance of this class.
*/
+ (id) new
{
NSAffineTransform *t;
t = (NSAffineTransform*)NSAllocateObject(self, 0, NSDefaultMallocZone());
t->_matrix = identityTransform;
t->_isIdentity = YES;
return t;
}
/**
* Appends the transform matrix to the receiver. This is done by performing a
* matrix multiplication of the receiver with aTransform so that aTransform
* is the first transform applied to the user coordinate. The new
* matrix then replaces the receiver's matrix.
*/
- (void) appendTransform: (NSAffineTransform*)aTransform
{
valid(aTransform);
if (aTransform->_isIdentity)
{
TX += aTransform->TX;
TY += aTransform->TY;
check();
return;
}
if (aTransform->_isFlipY)
{
B = -B;
D = -D;
TX = aTransform->TX + TX;
TY = aTransform->TY - TY;
if (_isIdentity)
{
_isFlipY = YES;
_isIdentity = NO;
}
else if (_isFlipY)
{
_isFlipY = NO;
_isIdentity = YES;
}
check();
return;
}
if (_isIdentity)
{
A = aTransform->A;
B = aTransform->B;
C = aTransform->C;
D = aTransform->D;
TX = TX * aTransform->A + TY * aTransform->C + aTransform->TX;
TY = TX * aTransform->B + TY * aTransform->D + aTransform->TY;
_isIdentity = NO; // because aTransform is not an identity transform.
_isFlipY = aTransform->_isFlipY;
check();
return;
}
if (_isFlipY)
{
A = aTransform->A;
B = aTransform->B;
C = -aTransform->C;
D = -aTransform->D;
TX = TX * aTransform->A + TY * aTransform->C + aTransform->TX;
TY = TX * aTransform->B + TY * aTransform->D + aTransform->TY;
_isIdentity = NO;
_isFlipY = NO;
check();
return;
}
_matrix = matrix_multiply(_matrix, aTransform->_matrix);
_isIdentity = NO;
_isFlipY = NO;
check();
}
- (NSString*) description
{
return [NSString stringWithFormat:
@"NSAffineTransform ((%f, %f) (%f, %f) (%f, %f))", A, B, C, D, TX, TY];
}
/**
* Initialize the transformation matrix instance to the identity matrix.
* The identity matrix transforms a point to itself.
*/
- (id) init
{
_matrix = identityTransform;
_isIdentity = YES;
return self;
}
/**
* Initialize the receiever's instance with the instance represented
* by aTransform.
*/
- (id) initWithTransform: (NSAffineTransform*)aTransform
{
_matrix = aTransform->_matrix;
_isIdentity = aTransform->_isIdentity;
_isFlipY = aTransform->_isFlipY;
return self;
}
/**
* Calculates the inverse of the receiver's matrix and replaces the
* receiever's matrix with it.
*/
- (void) invert
{
CGFloat newA, newB, newC, newD, newTX, newTY;
CGFloat det;
if (_isIdentity)
{
TX = -TX;
TY = -TY;
return;
}
if (_isFlipY)
{
TX = -TX;
return;
}
det = A * D - B * C;
if (det == 0)
{
NSLog (@"error: determinant of matrix is 0!");
return;
}
newA = D / det;
newB = -B / det;
newC = -C / det;
newD = A / det;
newTX = (-D * TX + C * TY) / det;
newTY = (B * TX - A * TY) / det;
NSDebugLLog(@"NSAffineTransform",
@"inverse of matrix ((%f, %f) (%f, %f) (%f, %f))\n"
@"is ((%f, %f) (%f, %f) (%f, %f))",
A, B, C, D, TX, TY,
newA, newB, newC, newD, newTX, newTY);
A = newA; B = newB;
C = newC; D = newD;
TX = newTX; TY = newTY;
}
/**
* Prepends the transform matrix to the receiver. This is done by performing a
* matrix multiplication of the receiver with aTransform so that aTransform
* is the last transform applied to the user coordinate. The new
* matrix then replaces the receiver's matrix.
*/
- (void) prependTransform: (NSAffineTransform*)aTransform
{
valid(aTransform);
if (aTransform->_isIdentity)
{
TX = aTransform->TX * A + aTransform->TY * C + TX;
TY = aTransform->TX * B + aTransform->TY * D + TY;
check();
return;
}
if (aTransform->_isFlipY)
{
TX = aTransform->TX * A + aTransform->TY * C + TX;
TY = aTransform->TX * B + aTransform->TY * D + TY;
C = -C;
D = -D;
if (_isIdentity)
{
_isFlipY = YES;
_isIdentity = NO;
}
else if (_isFlipY)
{
_isFlipY = NO;
_isIdentity = YES;
}
check();
return;
}
if (_isIdentity)
{
A = aTransform->A;
B = aTransform->B;
C = aTransform->C;
D = aTransform->D;
TX += aTransform->TX;
TY += aTransform->TY;
_isIdentity = NO;
_isFlipY = aTransform->_isFlipY;
check();
return;
}
if (_isFlipY)
{
A = aTransform->A;
B = -aTransform->B;
C = aTransform->C;
D = -aTransform->D;
TX += aTransform->TX;
TY -= aTransform->TY;
_isIdentity = NO;
_isFlipY = NO;
check();
return;
}
_matrix = matrix_multiply(aTransform->_matrix, _matrix);
_isIdentity = NO;
_isFlipY = NO;
check();
}
/**
* Applies the rotation specified by angle in degrees. Points transformed
* with the transformation matrix of the receiver are rotated counter-clockwise
* by the number of degrees specified by angle.
*/
- (void) rotateByDegrees: (CGFloat)angle
{
[self rotateByRadians: pi * angle / 180];
}
/**
* Applies the rotation specified by angle in radians. Points transformed
* with the transformation matrix of the receiver are rotated counter-clockwise
* by the number of radians specified by angle.
*/
- (void) rotateByRadians: (CGFloat)angleRad
{
if (angleRad != 0.0)
{
CGFloat sine;
CGFloat cosine;
NSAffineTransformStruct rotm;
sine = sin (angleRad);
cosine = cos (angleRad);
rotm.m11 = cosine;
rotm.m12 = sine;
rotm.m21 = -sine;
rotm.m22 = cosine;
rotm.tX = rotm.tY = 0;
_matrix = matrix_multiply(rotm, _matrix);
_isIdentity = NO;
_isFlipY = NO;
check();
}
}
/**
* Scales the transformation matrix of the reciever by the factor specified
* by scale.
*/
- (void) scaleBy: (CGFloat)scale
{
NSAffineTransformStruct scam = identityTransform;
scam.m11 = scale;
scam.m22 = scale;
_matrix = matrix_multiply(scam, _matrix);
_isIdentity = NO;
_isFlipY = NO;
check();
}
/**
* Scales the X axis of the receiver's transformation matrix
* by scaleX and the Y axis of the transformation matrix by scaleY.
*/
- (void) scaleXBy: (CGFloat)scaleX yBy: (CGFloat)scaleY
{
if (_isIdentity && scaleX == 1.0)
{
if (scaleY == 1.0)
{
return; // no scaling
}
if (scaleY == -1.0)
{
D = -1.0;
_isFlipY = YES;
_isIdentity = NO;
return;
}
}
if (_isFlipY && scaleX == 1.0)
{
if (scaleY == 1.0)
{
return; // no scaling
}
if (scaleY == -1.0)
{
D = 1.0;
_isFlipY = NO;
_isIdentity = YES;
return;
}
}
A *= scaleX;
B *= scaleX;
C *= scaleY;
D *= scaleY;
_isIdentity = NO;
_isFlipY = NO;
}
/**
*
* Sets the structure which represents the matrix of the reciever.
* The struct is of the form:
* {m11, m12, m21, m22, tX, tY}
*/
- (void) setTransformStruct: (NSAffineTransformStruct)val
{
_matrix = val;
_isIdentity = NO;
_isFlipY = NO;
if (A == 1.0 && B == 0.0 && C == 0.0)
{
if (D == 1.0)
{
_isIdentity = YES;
}
else if (D == -1.0)
{
_isFlipY = YES;
}
}
check();
}
/**
* Transforms a single point based on the transformation matrix.
* Returns the resulting point.
*/
- (NSPoint) transformPoint: (NSPoint)aPoint
{
NSPoint new;
if (_isIdentity)
{
new.x = TX + aPoint.x;
new.y = TY + aPoint.y;
}
else if (_isFlipY)
{
new.x = TX + aPoint.x;
new.y = TY - aPoint.y;
}
else
{
new.x = A * aPoint.x + C * aPoint.y + TX;
new.y = B * aPoint.x + D * aPoint.y + TY;
}
return new;
}
/**
* Transforms the NSSize represented by aSize using the reciever's
* transformation matrix. Returns the resulting NSSize.
* NB. A transform can result in negative size components ... so calling
* code should check for and deal with that situation.
*/
- (NSSize) transformSize: (NSSize)aSize
{
if (_isIdentity)
{
return aSize;
}
else
{
NSSize new;
if (_isFlipY)
{
new.width = aSize.width;
new.height = -aSize.height;
}
else
{
new.width = A * aSize.width + C * aSize.height;
new.height = B * aSize.width + D * aSize.height;
}
return new;
}
}
/**
*
* Returns the NSAffineTransformStruct
structure
* which represents the matrix of the reciever.
* The struct is of the form:
* {m11, m12, m21, m22, tX, tY}
*/
- (NSAffineTransformStruct) transformStruct
{
return _matrix;
}
/**
* Applies the translation specified by tranX and tranY to the receiver's
* matrix.
* Points transformed by the reciever's matrix after this operation will
* be shifted in position based on the specified translation.
*/
- (void) translateXBy: (CGFloat)tranX yBy: (CGFloat)tranY
{
if (_isIdentity)
{
TX += tranX;
TY += tranY;
}
else if (_isFlipY)
{
TX += tranX;
TY -= tranY;
}
else
{
TX += A * tranX + C * tranY;
TY += B * tranX + D * tranY;
}
check();
}
- (id) copyWithZone: (NSZone*)zone
{
return NSCopyObject(self, 0, zone);
}
- (BOOL) isEqual: (id)anObject
{
if ([anObject class] == isa)
{
NSAffineTransform *o = anObject;
if (A == o->A && B == o->B && C == o->C
&& D == o->D && TX == o->TX && TY == o->TY)
return YES;
}
return NO;
}
- (id) initWithCoder: (NSCoder*)aCoder
{
NSAffineTransformStruct replace;
[aCoder decodeArrayOfObjCType: @encode(CGFloat)
count: 6
at: (CGFloat*)&replace];
[self setTransformStruct: replace];
return self;
}
- (void) encodeWithCoder: (NSCoder*)aCoder
{
NSAffineTransformStruct replace;
replace = [self transformStruct];
[aCoder encodeArrayOfObjCType: @encode(CGFloat)
count: 6
at: (CGFloat*)&replace];
}
@end /* NSAffineTransform */