6d4eb76803
commitffb55b0f31Author: Curtis Hard <curtishard@me.com> Date: Fri Mar 2 13:28:40 2018 +0000 Fixes viewBox origin translate commita4c032afa2Author: Curtis Hard <curtishard@me.com> Date: Wed Feb 28 21:29:11 2018 +0000 Added clip to viewport commitabe8f4cba5Author: Curtis Hard <curtishard@me.com> Date: Wed Feb 28 21:00:20 2018 +0000 Fixes drawInRect not obeying origin commita16842271bAuthor: 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 commit58126e06e4Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 25 21:58:49 2018 +0000 Fixes! commit5af5e054abAuthor: Curtis Hard <curtishard@me.com> Date: Fri Feb 23 22:37:53 2018 +0000 Fixed defNode being removed commitedd3aa1f33Author: Curtis Hard <curtishard@me.com> Date: Fri Feb 23 16:29:54 2018 +0000 excluded various elements from diff commit3366bc4fa5Author: Curtis Hard <curtishard@me.com> Date: Fri Feb 23 14:05:03 2018 +0000 Better optimaztion commit104002183bAuthor: Curtis Hard <curtishard@me.com> Date: Thu Feb 22 22:17:50 2018 +0000 Added rudimenatry inline styles -> stylesheet commit7010b7ea50Author: Curtis Hard <curtishard@me.com> Date: Thu Feb 22 13:33:32 2018 +0000 Correct order of cleanup commit5266a8c07aAuthor: Curtis Hard <curtishard@me.com> Date: Thu Feb 22 08:22:17 2018 +0000 corrent length of string commitcaf55e8bdfAuthor: Curtis Hard <curtishard@me.com> Date: Wed Feb 21 20:45:17 2018 +0000 removes useless def if required commit8160d05ebaAuthor: Curtis Hard <curtishard@me.com> Date: Tue Feb 20 14:12:02 2018 +0000 Refactor of a few methods commita6d6a06521Author: Curtis Hard <curtishard@me.com> Date: Tue Feb 20 09:40:21 2018 +0000 Added collpasing of gradients commit103a4d71f6Author: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 19:07:30 2018 +0000 Reduced floats even more commit98874b1d2cAuthor: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 18:53:02 2018 +0000 More compression goodness commite742db31e0Author: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 14:02:15 2018 +0000 Added intermediateParent commit198fd09f07Author: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 11:51:37 2018 +0000 Fixes! and performance increases commit3493194b1bAuthor: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 08:20:02 2018 +0000 Scale computation commit0016775eafAuthor: Curtis Hard <curtishard@me.com> Date: Sun Feb 18 22:33:26 2018 +0000 More goodness commit304a04cc22Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 18 22:32:04 2018 +0000 Vastly improved the exporter commite4fd0af582Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 18 15:20:28 2018 +0000 This is insanely important! commit69a2a0c97eAuthor: Curtis Hard <curtishard@me.com> Date: Sun Feb 4 22:02:19 2018 +0000 Refactor commit5299bb0479Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 4 21:58:21 2018 +0000 will continue to use CoreAnimation for the time being commit4f1943cad1Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 4 21:57:46 2018 +0000 Fixes gradient strokes commitf02d186293Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 4 11:15:04 2018 +0000 trying to get masks to work commit3291718cfbAuthor: Curtis Hard <curtishard@me.com> Date: Fri Feb 2 22:35:22 2018 +0000 Beginning of quartz renderer commit6fbaaf5884Author: Curtis Hard <curtishard@me.com> Date: Mon Jan 29 22:32:08 2018 +0000 Removed useless log commitabc65797eaAuthor: Curtis Hard <curtishard@me.com> Date: Mon Jan 29 21:37:45 2018 +0000 I think gradients work :D commitaf5a1c2718Author: Curtis Hard <curtishard@me.com> Date: Sun Jan 28 22:18:02 2018 +0000 Possible fx and fy things… commitfb9a5282b9Author: Curtis Hard <curtishard@me.com> Date: Sun Jan 28 19:22:40 2018 +0000 Even more gradient fixes commitd83933a103Author: Curtis Hard <curtishard@me.com> Date: Sun Jan 28 14:21:50 2018 +0000 Various improvements commitbd7a0d5021Author: Curtis Hard <curtishard@me.com> Date: Sat Jan 27 22:26:33 2018 +0000 Start to linear commita9a038568cAuthor: Curtis Hard <curtishard@me.com> Date: Sat Jan 27 21:14:25 2018 +0000 This kind of actually works... commit77fbb38b6fAuthor: 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? commit12c3191569Author: Curtis Hard <curtishard@me.com> Date: Sat Jan 27 15:32:15 2018 +0000 Added method for findind absolute position commite3e9626ef7Author: Curtis Hard <curtishard@me.com> Date: Sat Jan 27 14:25:03 2018 +0000 Fixes crash due to parentNode on temp groups being released commit1160d89f16Author: Curtis Hard <curtishard@me.com> Date: Fri Jan 26 22:29:22 2018 +0000 Fixes use statements with transforms commit1575cbfde8Author: Curtis Hard <curtishard@me.com> Date: Fri Jan 26 18:16:03 2018 +0000 Moved color tree over to modern syntax commit5c4c2eee91Author: Curtis Hard <curtishard@me.com> Date: Thu Jan 25 18:23:53 2018 +0000 Added HSL/HSLA support commit087b13e58fAuthor: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 22:24:43 2018 +0000 Various image loading issues resolved from base64 images commit1183e167aaAuthor: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 21:21:41 2018 +0000 Rect issue fix commit4dbfc59437Author: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 21:19:24 2018 +0000 Moved transforms over from being concatinated to seperate calls commit409bd509faAuthor: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 20:30:38 2018 +0000 Fixes color issue commit8ae1d1b4e0Author: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 18:41:08 2018 +0000 Removed check as its not needed here commit7243fbe5ffAuthor: 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 commit51b9a5e85fAuthor: 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) commit40098589deAuthor: 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 :-)) commit7cb96b21f2Author: 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 commit1bff7c6970Author: Curtis Hard <curtishard@me.com> Date: Mon Jan 22 21:48:07 2018 +0000 Various fixes… still going…
570 lines
20 KiB
Objective-C
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
|