Files
IJSVG/source/IJSVGTransform.m
Curtis Hard 6d4eb76803 Squashed commit of the following:
commit ffb55b0f31
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Mar 2 13:28:40 2018 +0000

    Fixes viewBox origin translate

commit a4c032afa2
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Feb 28 21:29:11 2018 +0000

    Added clip to viewport

commit abe8f4cba5
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Feb 28 21:00:20 2018 +0000

    Fixes drawInRect not obeying origin

commit a16842271b
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 26 13:46:41 2018 +0000

    Resolves namespaces correctly + added common HTML list to be parsed as groups

commit 58126e06e4
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 25 21:58:49 2018 +0000

    Fixes!

commit 5af5e054ab
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Feb 23 22:37:53 2018 +0000

    Fixed defNode being removed

commit edd3aa1f33
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Feb 23 16:29:54 2018 +0000

    excluded various elements from diff

commit 3366bc4fa5
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Feb 23 14:05:03 2018 +0000

    Better optimaztion

commit 104002183b
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Feb 22 22:17:50 2018 +0000

    Added rudimenatry inline styles -> stylesheet

commit 7010b7ea50
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Feb 22 13:33:32 2018 +0000

    Correct order of cleanup

commit 5266a8c07a
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Feb 22 08:22:17 2018 +0000

    corrent length of string

commit caf55e8bdf
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Feb 21 20:45:17 2018 +0000

    removes useless def if required

commit 8160d05eba
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Feb 20 14:12:02 2018 +0000

    Refactor of a few methods

commit a6d6a06521
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Feb 20 09:40:21 2018 +0000

    Added collpasing of gradients

commit 103a4d71f6
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 19:07:30 2018 +0000

    Reduced floats even more

commit 98874b1d2c
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 18:53:02 2018 +0000

    More compression goodness

commit e742db31e0
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 14:02:15 2018 +0000

    Added intermediateParent

commit 198fd09f07
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 11:51:37 2018 +0000

    Fixes! and performance increases

commit 3493194b1b
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 08:20:02 2018 +0000

    Scale computation

commit 0016775eaf
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 18 22:33:26 2018 +0000

    More goodness

commit 304a04cc22
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 18 22:32:04 2018 +0000

    Vastly improved the exporter

commit e4fd0af582
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 18 15:20:28 2018 +0000

    This is insanely important!

commit 69a2a0c97e
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 4 22:02:19 2018 +0000

    Refactor

commit 5299bb0479
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 4 21:58:21 2018 +0000

    will continue to use CoreAnimation for the time being

commit 4f1943cad1
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 4 21:57:46 2018 +0000

    Fixes gradient strokes

commit f02d186293
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 4 11:15:04 2018 +0000

    trying to get masks to work

commit 3291718cfb
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Feb 2 22:35:22 2018 +0000

    Beginning of quartz renderer

commit 6fbaaf5884
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 29 22:32:08 2018 +0000

    Removed useless log

commit abc65797ea
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 29 21:37:45 2018 +0000

    I think gradients work :D

commit af5a1c2718
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 28 22:18:02 2018 +0000

    Possible fx and fy things…

commit fb9a5282b9
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 28 19:22:40 2018 +0000

    Even more gradient fixes

commit d83933a103
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 28 14:21:50 2018 +0000

    Various improvements

commit bd7a0d5021
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 22:26:33 2018 +0000

    Start to linear

commit a9a038568c
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 21:14:25 2018 +0000

    This kind of actually works...

commit 77fbb38b6f
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 20:31:36 2018 +0000

    I guess this could be a good start?

    Posssible start of fixes?

commit 12c3191569
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 15:32:15 2018 +0000

    Added method for findind absolute position

commit e3e9626ef7
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 14:25:03 2018 +0000

    Fixes crash due to parentNode on temp groups being released

commit 1160d89f16
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Jan 26 22:29:22 2018 +0000

    Fixes use statements with transforms

commit 1575cbfde8
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Jan 26 18:16:03 2018 +0000

    Moved color tree over to modern syntax

commit 5c4c2eee91
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Jan 25 18:23:53 2018 +0000

    Added HSL/HSLA support

commit 087b13e58f
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 22:24:43 2018 +0000

    Various image loading issues resolved from base64 images

commit 1183e167aa
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 21:21:41 2018 +0000

    Rect issue fix

commit 4dbfc59437
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 21:19:24 2018 +0000

    Moved transforms over from being concatinated to seperate calls

commit 409bd509fa
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 20:30:38 2018 +0000

    Fixes color issue

commit 8ae1d1b4e0
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 18:41:08 2018 +0000

    Removed check as its not needed here

commit 7243fbe5ff
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 18:35:43 2018 +0000

    Added excludeAttributes list to parseCommonAttributes

    added x and y to that list for rect

commit 51b9a5e85f
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Jan 23 19:53:04 2018 +0000

    Added isSubcommand to IJSVGCommand

    - Fixes move command going awol when preceding move commands are not subcommands (woah)

commit 40098589de
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 22 22:04:48 2018 +0000

    removed reverseObjectEnumerator

    IJSVGTransform already deals with this at parse stage (was a test from earlier), spec states transforms are applied in reverse order (which parser already dealth with :-))

commit 7cb96b21f2
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 22 22:01:56 2018 +0000

    Removed use of origin here as its computed in apply defaults

commit 1bff7c6970
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 22 21:48:07 2018 +0000

    Various fixes… still going…
2018-03-02 13:51:49 +00:00

570 lines
20 KiB
Objective-C

//
// IJSVGTransform.m
// IconJar
//
// Created by Curtis Hard on 01/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGTransform.h"
#import "IJSVGMath.h"
@implementation IJSVGTransform
@synthesize command;
@synthesize parameters;
@synthesize parameterCount;
@synthesize sort;
- (void)dealloc
{
free(parameters);
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
IJSVGTransform * trans = [[[self class] alloc] init];
trans.command = self.command;
trans.parameters = (CGFloat*)malloc(sizeof(CGFloat)*self.parameterCount);
trans.sort = sort;
trans.parameterCount = self.parameterCount;
memcpy( trans.parameters, self.parameters, sizeof(CGFloat)*self.parameterCount);
return trans;
}
NSString * IJSVGDebugAffineTransform(CGAffineTransform transform)
{
NSMutableArray * strings = [[[NSMutableArray alloc] init] autorelease];
[strings addObjectsFromArray:[IJSVGTransform affineTransformToSVGTransformAttributeString:transform]];
return [strings componentsJoinedByString:@" "];
}
NSString * IJSVGDebugTransforms(NSArray<IJSVGTransform *> * transforms)
{
NSMutableArray * strings = [[[NSMutableArray alloc] init] autorelease];
IJSVGApplyTransform(transforms, ^(IJSVGTransform *transform) {
[strings addObjectsFromArray:[IJSVGTransform affineTransformToSVGTransformAttributeString:transform.CGAffineTransform]];
});
return [strings componentsJoinedByString:@" "];
}
CGAffineTransform IJSVGConcatTransforms(NSArray<IJSVGTransform *> * transforms)
{
__block CGAffineTransform trans = CGAffineTransformIdentity;
IJSVGApplyTransform(transforms, ^(IJSVGTransform *transform) {
trans = CGAffineTransformConcat(trans, transform.CGAffineTransform);
});
return trans;
}
void IJSVGApplyTransform(NSArray<IJSVGTransform *> * transforms, IJSVGTransformApplyBlock block)
{
for(IJSVGTransform * transform in transforms) {
block(transform);
}
};
+ (IJSVGTransform *)transformByTranslatingX:(CGFloat)x
y:(CGFloat)y
{
IJSVGTransform * transform = [[[self alloc] init] autorelease];
transform.command = IJSVGTransformCommandTranslate;
transform.parameterCount = 2;
CGFloat * params = (CGFloat *)malloc(sizeof(CGFloat)*2);
params[0] = x;
params[1] = y;
transform.parameters = params;
return transform;
}
- (void)recalculateWithBounds:(CGRect)bounds
{
CGFloat max = bounds.size.width>bounds.size.height?bounds.size.width:bounds.size.height;
switch (self.command) {
case IJSVGTransformCommandRotate: {
if( self.parameterCount == 1 )
return;
self.parameters[1] = self.parameters[1]*max;
self.parameters[2] = self.parameters[2]*max;
}
default:
return;
}
}
+ (IJSVGTransformCommand)commandForCommandString:(NSString *)str
{
str = str.lowercaseString;
if( [str isEqualToString:@"matrix"] )
return IJSVGTransformCommandMatrix;
if( [str isEqualToString:@"translate"] )
return IJSVGTransformCommandTranslate;
if( [str isEqualToString:@"scale"] )
return IJSVGTransformCommandScale;
if( [str isEqualToString:@"skewx"])
return IJSVGTransformCommandSkewX;
if( [str isEqualToString:@"skewy"])
return IJSVGTransformCommandSkewY;
if( [str isEqualToString:@"rotate"] )
return IJSVGTransformCommandRotate;
return IJSVGTransformCommandNotImplemented;
}
+ (NSInteger)sortForTransformCommand:(IJSVGTransformCommand)command
{
switch (command) {
case IJSVGTransformCommandScale:
return 0;
case IJSVGTransformCommandRotate:
return 1;
case IJSVGTransformCommandMatrix:
return 2;
case IJSVGTransformCommandTranslate:
return -1;
default:
return 10;
}
return 10;
}
+ (NSArray *)transformsForString:(NSString *)string
{
static NSRegularExpression * _reg = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_reg = [[NSRegularExpression alloc] initWithPattern:@"([a-zA-Z]+)\\((.*?)\\)"
options:0
error:nil];
});
NSMutableArray * transforms = [[[NSMutableArray alloc] init] autorelease];
@autoreleasepool {
[_reg enumerateMatchesInString:string
options:0
range:NSMakeRange( 0, string.length )
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
{
NSString * command = [string substringWithRange:[result rangeAtIndex:1]];
IJSVGTransformCommand commandType = [[self class] commandForCommandString:command];
if( commandType == IJSVGTransformCommandNotImplemented ) {
return;
}
// create the transform
NSString * params = [string substringWithRange:[result rangeAtIndex:2]];
IJSVGTransform * transform = [[[[self class] alloc] init] autorelease];
NSInteger count = 0;
transform.command = commandType;
transform.parameters = [IJSVGUtils commandParameters:params
count:&count];
transform.parameterCount = count;
transform.sort = [[self class] sortForTransformCommand:commandType];
[transforms addObject:transform];
}];
}
return transforms;
}
+ (NSBezierPath *)transformedPath:(IJSVGPath *)path
{
if( path.transforms.count == 0 )
return path.path;
NSBezierPath * cop = [[path.path copy] autorelease];
for( IJSVGTransform * transform in path.transforms )
{
NSAffineTransform * at = [NSAffineTransform transform];
switch( transform.command )
{
// matrix
case IJSVGTransformCommandMatrix: {
at.transformStruct = (NSAffineTransformStruct) {
.m11 = transform.parameters[0],
.m12 = transform.parameters[1],
.m21 = transform.parameters[2],
.m22 = transform.parameters[3],
.tX = transform.parameters[4],
.tY = transform.parameters[5],
};
break;
}
// skewX
case IJSVGTransformCommandSkewX: {
CGFloat degrees = transform.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
at.transformStruct = (NSAffineTransformStruct) {
.m11 = 1.f,
.m12 = 0.f,
.m21 = tan(radians),
.m22 = 1.f,
.tX = 0.f,
.tY = 0.f
};
break;
}
// skewX
case IJSVGTransformCommandSkewY: {
CGFloat degrees = transform.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
at.transformStruct = (NSAffineTransformStruct) {
.m11 = 1.f,
.m12 = tan(radians),
.m21 = 0.f,
.m22 = 1.f,
.tX = 0.f,
.tY = 0.f
};
break;
}
// translate
case IJSVGTransformCommandTranslate: {
if( transform.parameterCount == 1 )
[at translateXBy:transform.parameters[0]
yBy:0];
else
[at translateXBy:transform.parameters[0]
yBy:transform.parameters[1]];
break;
}
// scale
case IJSVGTransformCommandScale: {
if( transform.parameterCount == 1 )
[at scaleBy:transform.parameters[0]];
else
[at scaleXBy:transform.parameters[0]
yBy:transform.parameters[1]];
break;
}
// rotate
case IJSVGTransformCommandRotate: {
if( transform.parameterCount == 1 )
[at rotateByDegrees:transform.parameters[0]];
else {
CGFloat centerX = transform.parameters[1];
CGFloat centerY = transform.parameters[2];
CGFloat angle = transform.parameters[0]*(M_PI/180.f);
[at translateXBy:centerX
yBy:centerY];
[at rotateByRadians:angle];
[at translateXBy:-1.f*centerX
yBy:-1.f*centerY];
}
break;
}
// do nothing
case IJSVGTransformCommandNotImplemented: {
}
}
[cop transformUsingAffineTransform:at];
}
return cop;
}
- (CGAffineTransform)CGAffineTransform
{
return [self CGAffineTransformWithModifier:nil];
}
- (CGAffineTransform)stackIdentity:(CGAffineTransform)identity
{
switch(self.command) {
// translate
case IJSVGTransformCommandTranslate: {
if(self.parameterCount == 1) {
return CGAffineTransformTranslate(identity, self.parameters[0], 0.f);
}
return CGAffineTransformTranslate(identity, self.parameters[0], self.parameters[1]);
}
// rotate
case IJSVGTransformCommandRotate: {
if(self.parameterCount == 1) {
return CGAffineTransformRotate(identity, (self.parameters[0]/180) * M_PI);
}
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
CGFloat angle = p0*(M_PI/180.f);
identity = CGAffineTransformTranslate(identity, p1, p2);
identity = CGAffineTransformRotate(identity, angle);
return CGAffineTransformTranslate(identity, -1.f*p1, -1.f*p2);
}
// scale
case IJSVGTransformCommandScale: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
if(self.parameterCount == 1) {
return CGAffineTransformScale(identity, p0, p0);
}
return CGAffineTransformScale(identity, p0, p1);
}
// matrix
case IJSVGTransformCommandMatrix: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
CGFloat p3 = self.parameters[3];
CGFloat p4 = self.parameters[4];
CGFloat p5 = self.parameters[5];
return CGAffineTransformMake(p0, p1, p2, p3, p4, p5);
}
// skewX
case IJSVGTransformCommandSkewX: {
CGFloat degrees = self.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
return CGAffineTransformMake( 1.f, 0.f, tan(radians), 1.f, 0.f, 0.f);
}
// skewY
case IJSVGTransformCommandSkewY: {
CGFloat degrees = self.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
return CGAffineTransformMake( 1.f, tan(radians), 0.f, 1.f, 0.f, 0.f);
}
case IJSVGTransformCommandNotImplemented: {
return CGAffineTransformIdentity;
}
}
return CGAffineTransformIdentity;
}
- (CGAffineTransform)CGAffineTransformWithModifier:(IJSVGTransformParameterModifier)modifier
{
switch(self.command)
{
// matrix
case IJSVGTransformCommandMatrix: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
CGFloat p3 = self.parameters[3];
CGFloat p4 = self.parameters[4];
CGFloat p5 = self.parameters[5];
if(modifier != nil)
{
p0 = modifier(0,p0);
p1 = modifier(1,p1);
p2 = modifier(2,p2);
p3 = modifier(2,p3);
p4 = modifier(2,p4);
p5 = modifier(2,p5);
}
return CGAffineTransformMake(p0, p1, p2, p3, p4, p5);
}
// translate
case IJSVGTransformCommandTranslate: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
if(modifier != nil)
{
p0 = modifier(0,p0);
p1 = modifier(1,p1);
}
if(self.parameterCount == 1)
return CGAffineTransformMakeTranslation( p0, 0 );
return CGAffineTransformMakeTranslation(p0, p1);
}
// scale
case IJSVGTransformCommandScale: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
if(modifier != nil)
{
p0 = modifier(0,p0);
p1 = modifier(1,p1);
}
if(self.parameterCount == 1)
return CGAffineTransformMakeScale( p0, p0);
return CGAffineTransformMakeScale( p0, p1);
}
// skewX
case IJSVGTransformCommandSkewX: {
CGFloat degrees = self.parameters[0];
if(modifier != nil) {
degrees = modifier(0,degrees);
}
CGFloat radians = degrees * M_PI / 180.f;
return CGAffineTransformMake( 1.f, 0.f, tan(radians), 1.f, 0.f, 0.f);
}
// skewY
case IJSVGTransformCommandSkewY: {
CGFloat degrees = self.parameters[0];
if(modifier != nil) {
degrees = modifier(0,degrees);
}
CGFloat radians = degrees * M_PI / 180.f;
return CGAffineTransformMake( 1.f, tan(radians), 0.f, 1.f, 0.f, 0.f);
}
// rotate
case IJSVGTransformCommandRotate: {
if(self.parameterCount == 1)
return CGAffineTransformMakeRotation((self.parameters[0]/180) * M_PI);
else {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
if(modifier != nil)
{
p0 = modifier(0,p0);
p1 = modifier(1,p1);
p2 = modifier(2,p2);
}
CGFloat angle = p0*(M_PI/180.f);
CGAffineTransform def = CGAffineTransformIdentity;
def = CGAffineTransformTranslate(def, p1, p2);
def = CGAffineTransformRotate(def, angle);
def = CGAffineTransformTranslate(def, -1.f*p1, -1.f*p2);
return def;
}
break;
}
// do nothing
case IJSVGTransformCommandNotImplemented: {
return CGAffineTransformIdentity;
}
}
return CGAffineTransformIdentity;
}
+ (NSArray<IJSVGTransform *> *)transformsFromAffineTransform:(CGAffineTransform)affineTransform
{
NSArray * strings = [self affineTransformToSVGTransformAttributeString:affineTransform];
return [self transformsForString:[strings componentsJoinedByString:@" "]];
}
+ (NSString *)affineTransformToSVGMatrixString:(CGAffineTransform)transform
{
return [NSString stringWithFormat:@"matrix(%g,%g,%g,%g,%g,%g)",
transform.a, transform.b, transform.c, transform.d,
transform.tx, transform.ty];
}
// this is an Object-C version of the matrixToTransform method from SVGO
+ (NSArray<NSString *> *)affineTransformToSVGTransformAttributeString:(CGAffineTransform)affineTransform
{
const CGFloat data[6] = {
affineTransform.a,
affineTransform.b,
affineTransform.c,
affineTransform.d,
affineTransform.tx,
affineTransform.ty
};
CGFloat sx = sqrtf(data[0]*data[0] + data[1] * data[1]);
CGFloat sy = (data[0]*data[3] - data[1]*data[2])/sx;
CGFloat colSum = data[0]*data[2] + data[1]*data[3];
CGFloat rowSum = data[0]*data[1] + data[2]*data[3];
BOOL scaleBefore = rowSum != 0.f || (sx == sy);
NSMutableArray * trans = [[[NSMutableArray alloc] init] autorelease];
// translate
if(data[4] != 0.f || data[5] != 0.f) {
NSString * str = [NSString stringWithFormat:@"translate(%g, %g)",data[4],data[5]];
[trans addObject:str];
}
// skewX
if(data[1] == 0.f && data[2] != 0.f) {
NSString * str = [NSString stringWithFormat:@"skewX(%g)",IJSVGMathAtan(data[2]/sy)];
[trans addObject:str];
// skewY
} else if(data[1] != 0.f && data[2] == 0.f) {
NSString * str = [NSString stringWithFormat:@"skewY(%g)",IJSVGMathAtan(data[1]/data[0])];
[trans addObject:str];
sx = data[0];
sy = data[3];
} else if(colSum == 0.f || (sx == 1.f && sy == 1.f) || scaleBefore == NO) {
if(scaleBefore == NO) {
sx = (data[0] < 0.f ? -1.f : 1.f) * sqrtf(data[0] * data[0] + data[2] * data[2]);
sy = (data[3] < 0.f ? -1.f : 1.f) * sqrtf(data[1] * data[1] + data[3] * data[3]);
NSString * str = nil;
if(sx == sy) {
str = [NSString stringWithFormat:@"scale(%g)",sx];
} else {
str = [NSString stringWithFormat:@"scale(%g, %g)",sx,sy];
}
[trans addObject:str];
}
// rotate
CGFloat rotate = IJSVGMathAcos(data[0]/sx)*(data[1]*sy < 0.f ? -1.f : 1.f);
NSString * rotateString = nil;
if(rotate != 0.f) {
rotateString = [NSString stringWithFormat:@"rotate(%g)",rotate];
}
// skewX
if(rowSum != 0.f && colSum != 0.f) {
NSString * str = [NSString stringWithFormat:@"skewX(%g)",IJSVGMathAtan(colSum/(sx * sx))];
[trans addObject:str];
}
// rotate around center
if(rotate != 0.f && (data[4] != 0.f || data[5] != 0.f)) {
[trans removeObjectAtIndex:0];
CGFloat cos = data[0]/sx;
CGFloat sin = data[1]/(scaleBefore ? sx : sy);
CGFloat x = data[4] * (scaleBefore ? 1.f : sy);
CGFloat y = data[5] * (scaleBefore ? 1.f : sx);
CGFloat denom = (powf(1.f - cos, 2.f) + powf(sin,2.f)) * (scaleBefore ? 1.f : sx * sy);
CGFloat r1 = rotate;
CGFloat r2 = ((1.f - cos) * x - sin * y) / denom;
CGFloat r3 = ((1.f - cos) * y + sin * x) / denom;
rotateString = [NSString stringWithFormat:@"rotate(%g, %g, %g)",r1,r2,r3];
}
if(rotateString != nil) {
[trans addObject:rotateString];
}
}
// scale
if((scaleBefore && (sx != 1.f || sy != 1.f)) || trans.count == 0.f) {
NSString * str = nil;
if(sx == sy) {
str = [NSString stringWithFormat:@"scale(%g)",sx];
} else {
str = [NSString stringWithFormat:@"scale(%g, %g)",sx, sy];
}
[trans addObject:str];
}
return trans;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ %@",[super description],
[self.class affineTransformToSVGTransformAttributeString:self.CGAffineTransform]];
}
@end