From 4804fe4db34e4b2f8d7e3df15453208e548855ca Mon Sep 17 00:00:00 2001 From: Curtis Hard Date: Mon, 13 Apr 2015 19:13:52 +0100 Subject: [PATCH] Added elliptical arc command (finally) Various other fixes and tweaks --- .../UserInterfaceState.xcuserstate | Bin 41841 -> 41842 bytes source/IJSVG.m | 7 +- source/IJSVGCommand.h | 7 ++ source/IJSVGCommand.m | 26 +++++ source/IJSVGCommandArc.m | 91 ++++++++++++++---- source/IJSVGCommandClose.m | 1 + .../IJSVGCommandCommandSmoothQuadraticCurve.m | 1 + source/IJSVGCommandCurve.m | 1 + source/IJSVGCommandHorizontalLine.m | 1 + source/IJSVGCommandLineTo.m | 1 + source/IJSVGCommandMove.m | 2 + source/IJSVGCommandQuadraticCurve.m | 1 + source/IJSVGCommandSmoothCurve.m | 1 + source/IJSVGCommandVerticalLine.m | 1 + source/IJSVGParser.m | 16 ++- source/IJSVGUtils.h | 6 ++ source/IJSVGUtils.m | 33 +++++-- 17 files changed, 163 insertions(+), 33 deletions(-) diff --git a/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate b/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate index 42d23894f06c6f28dc5be79d6e8e5b19e19f7bd2..da8f5d3e11d5a8e86b6f4845e1d01e5f9d52d387 100644 GIT binary patch delta 11469 zcma)i2Xs``^ZzXiDcd*Ov;AeW1qp#9^b#PH03m@ufKWn076>GiB=oZP0ZBka(Pu$= zLPtat1?gA-=^#}^K}0}oND~$0ci(#n?0)r}|DSW*+;=}Sb7$tx%)4`+zaK<*52A(B zgLz;mm<*b>d;CpZ#`~Yr%yWk$U4<3Mr;1BQ!{0aVo5?Bw)VGPtl z9W+5Jv_X3iGB6H$p$|5M2{0L^z!tC#Yzy1LPOvNN0W)Avm;?L4{%|PFhofOJEP-QS zDVzf3wp@4+qbefSaF0e8aB z;TP}{{2Jbbx8RTPHVoc@kKkkY1pb2%!YBeoA{mk+HPRqGG9V+eAO~`yhA06wLW!s` zYJ=LMb|@9KM_o`?l#Vh{7V3)zqai2{jX)z&5h_6wP!Lt11?W}u26`JUL)EATtwryk zE$Ab(1MNkhqXXzLI*!hui$Qc1eTS~0+vq3sC;AKhjUJ;X=pU@WO02?atidr@i_JI| zJFyEV;6^wRH^wb+OPq##;NCa~_rrtnFkFPk;t6;fUVvZ5ui%CFRs0&}@ay<3ya=zy z8}LT_4&H>{#hdYNya(^a`|y7J3H}rhJcPf%hw*WI8ehN{@fG|%zK(yuKjPc?C;S_} zhwtMD_#ys7B9|y6N{LFMmS`j~60Jlh(MwDci^MK*N!*eKlE#u2l9rNIl8%xzNhe8$ zq^Bfv>lw7Y9!%usN6wLE5=0`1g=CVHt>&mtq02(7jkzWuf=Abl2F_=ztDl94=TiU)fkk2)gRtNJ``u1gIMP=prW6DLStVjNou@lM% zr+3TF>5`RSS{5ixD2X})qChl|f_zX6ycK2kqMn{uk@-F#|d`?ohQ!+g_R5qxA z_L-I$V+#X$T>@i@CY0r=V(4^QV0iw7;_|dW!GsZl39V#ITyC+TaG%mZpiax#paOWS z!5reN29?B5h40gv62XgLK@FG>ULp-hW0G7){0evtXllSh@G5zMG^_y}c%3AWMkJAo z)H#C#o@v9R&h`TS-|FcZ2;vvkL41 zd%-@^inJzeR)J6GK7B^o(w!-y<{#4M_W_4NN;Nn_+7V?P<8g2fc-Mgw;3PN&PE&83 z0cS}nX-_(kjwFqABAwTP^WXxw2)+WBz}Mh1=|Z}aUL-*JlHp_oRc^XrA!ks!d^f?* zK(iX$0zZP=;3tw!x{>ar$7=8k_>~6D9g;zM(m=_gs=&U}xNnqJ-8t|#cud3a3HS#* zg#bc`zK&C&vPmK7O>()(s%AN`K1f*uBVZ(qg3(Y)`jGx)AQ?>Zs2NZJl~lwf zsD_%{^uoZH@}l8IfzrO|V+sS4sg60M-#{Vwd(!YrC{g(Y$3s0da6Yvi*FYoZQM-bo z7-$CLp@s~gF6)<5Hg-bkuz(8GE&&|^Hi*ai3KHu8w*U;`fk6V$X>xwaxZ*$^?icW( z|A99GQkV!E!zNCcG_Y{&un8qp|GvF@3@9%u3D7Jq8Al4pFaq*aEjV>$61TE*{U}lY zrmz_|rj5Q{4Qvit@uS-Lmar8U&@_atIZhMkl1k;bhe@y_Y}~JBc0xgZSzsXjTN)_N zFE5%HAb^Y{1){*tuuI>b*>vq1*qM(`S{`6JjTTybMse+8%!~@wh78Pvy@9s|X3+>O zAc=8%a8PLACJWx6&zq~H7VF4UY zgBA{hg)l%$Nf{|$1xHY46_E+lSqp`rEyx}V7F5G=Wa6_Nhh=aYjagU@C%}nt5}XXD zz^P;snM|gTsbm_NPG+ow)8Py_69yrHESyDVk|3!fFOdc0W%7y;v+I2eAgvUu;mhz9 zxDdWd2w}-AGJ7@T;Ol^aZ<0Brg2rp55U*2*?7ZCeP+{j`{ih|q;7D%Ld`1pGNSqN8 z!f++5rXidJSCbdXTpGTB%oF{%7No2s%9!9&@!uIJ?)ygA1SXOB{04KU)7o%3rcTc1 zJGN2So%y1KZM+Y*lUGSHx7%b3!d-CRzdW~}dhS!gQO~_Y5=FKH{01H*uRlw7co?4k zf4c5Vcm|$@=iqtjx{Kru@+MhKHjri1bsI@ap2{?iGb!ome`LWX-c0K=u8LChsz`ob(HS->DkZrQ%Z|6<6}9SexIq2$Bd$ z4UdFnH;NK~)jSZI-OcLK8z}^Wwf{>%abcTLjIhn?#XN2$%%@2^H)K5sM(&$?T zbtE5GgYlFigY zqJd;D*+&xl_XrFx&nXH_Vp3;`Z^^o4hoWI&EQKgQJ|Uly!~wmFMvScE=`Xxl>v%?) z&}iUYxss%uMPtxdG!977cvOnYP&qfm(%o^AoF)gzh4W}4nna<=XbPGNlPJjyn1NZDH>Vl34N*4YK}I6lp5-sQtFh` zL@6=_YyE=W7yKe}eMxw))N*a(<8eDVQ^)b#k)>z`J?8JB%K$3yJUJWU#=2UFK6_RR z&poSu!V3o-M2GTJ+-Ei~u0daL`)p0{5pm^?`RS1{h+5F_z$ZHP(|~s>#V$vkwPEUOmW z^K6yrcn*EN;u&}*4q}2?JPXfeAuTDGg%TFlV_|(3MzAn)9j>5#6z!$(JiZ#UFp8F9 z+D5T3TCB>{-F&Yyd0aj{$QGuT2TEwyWT#WvQ%VZBi;R*}#7VhRj0ZZo(~N=Z7ncDU zI?{8Aaq=7?Q}7#L!Ag>xRp)}Y@k%}%7vm*(DPD$`;}tBFu~5!J1q+odRIyOK7Wr`v z?Hd@p1~6m?OJAtDd<)uuDwo1Z-PJAL!ynO%wpTo3XvCu&ZS!idWpKI@F8T<;Qtj3q{ z*Z4C2hJ{WR#<9@DLhrMJjj!VGpoWDm7BZw?zs5}+CA6bbscK6wzJYJ@u$zVPq+kC& zBWagi=xD5}SBHPjmuPATU!&{h?uc_fl8Z<`?y+Y~-XjSD-VOLqn*M*|$M^~U2S1en z2`v~2S6D(paCCTNqs4JN@5OuunHj{0*+9C5g%$}LlQ5cMM8YROMEPBQ(J5$FMtKB zBn>4A5~^og7Pe<$+OwJ~X(CAx3TaGjvn^>Vp&GVhVJa<99)DbHYe~D%N-If~PyroS z*pcGH{r1?-lJqdVo1{AnJF&1c#aB}PEJ-%47m{9*-Yo3O!t@$Gu=s$Yc3<&N;tCsd zPaH29Eh(0iNXAIUO2+Z!ly^)&lFQy| zb1IFDTUaU4Jy%HRbXj;XPt}ymZ}I|X>?jLi9wjn=!K@QJ#ny(BF%gn6cjp5h#x6AfdF$I|P#(Dh~u>zzpJ5MuCLVb0#EZOGgL zVXnASZitnyiJ~v&sT%WE>goMjxbU|GCTr(bAxuj}rptBOuIwDDZMBGg%Qc?URu{Hg zYenQLccN#qF3f~?MC6*V`^n)NY!R{RwBK!N>|0(`ToxW3AMrRR?;5J%PLbzUo~j9N zmLV&@d}KJ+K7ngx*VZ9^{#@kxnfkdAXHAa^G5N3v{hFt0E=0qLjuG6L^oS7V6C%YO zYIge&#xvsd-8@wn%9zSoyM>CqD5CdyvHWGtm~NpWzY)=gwLuo<%WFKw{n9NbM1`9o z{a=FqdwbOn&y}AAj-}nZhG_S@$nj5}s=uJ!=1jT0VOD8ic+up9al^tEd?=72{@S5+ z_~!jBB1qW0l(5Z<03s&g?sUu2gl>66Js#oMj1JG$iHH`dBlA@41$Qj3im-K@P&zRX zws#Rqku3Uo{M8E!v;yi)bj!HO>mjwTLF73?pwHUr~-_8J=%mL^BaJbKhmQHHK;1T0|^) zszl1?vG(rNJ}skDLP=pr5r}9n64`3^J|xH@I*X7aPnFECYpT7{58d5}?mP|W?428; zQI^OT$Cn*$Pw&VOXZ01K_|UQuCNEczXUPt=KTl-x=cy6|J;J?MD55XaqTJ)`&+Wt$Os*v;9GLJgog&*rwiR`n%A#qteA@;Jd_kUXP*+|(mPNB_ z!JI*9ywDeGg~kpR9P=`LEFLUqO7Bg%ed6#mZ`0Ec zT1+VmhVzuLj+MZbSTdgX4leTAkY7A1FlE@AR4fqMH^~$+?pro_%7vnk|#)d zpANHz3d4`+aMjR0{8Br3f}w)^Pw0@}xKsJvc;oLTqvx6O;~9vce7CN+ztBJJRt1kueFF| zxIoDFxBbN7Lpt0z{NL6`{T6jcwEhu`{;Xa0(Qwzl+!XaB>K~EtZx%hSC2Tvw`LC^s zmPX3}6Rlv;KP-Amw+7QLlAALk;op?z=vYc=Wietg<|!qV^1_I0>OUMwhlwM*QHS7Y z7Sm=x2==OxytlBDf~QCF-ok1YYidEJNC5Q|EGZH|Ba2P7;64%Ld$OoenY<7u9S#}w zLi_){43AzOy@IM$&0>bdalBe?k|N&c4ja|$Kkjq6qobPg0)13q#ArSkFn=SGH@c^i z4jc2#J9esOGP@seJ#$RlxzP--coIvm8$_$>74zwW`LAAC#r)1;zD%qVhpBY9s7cnG&;Oi!*wIa4b$SpHn*2I)pY=Cbly znOv(0&(U4d-2ysb!tPq$<`bWz$E7C(G-u*;w|ILerCoWo^b(7M;R<~#y&_QlF>$gW zr|~nJ?nr+ZkS&v@)ya6w%Xq@#7sF)8pbQC=S(9gm^(`{tjKN)6rre{8mQNi_*~Bm7vR1OzwbxDGPi;>@_q6r&_rx?a?a+RlR-)rCYiJaH z%;Fs^z3ITaXiu8Bb@X%tlt!_5Ka0Pmmu&PVe#RKS$$By)7AAA1nF)N~8PEMX%f=PW ze3uF|1l!Tye!(s^W;jdZz4gLC0zyEX9vv$?L+;;Dv z1jH50X7nvuHcnFA7Wz8bU$n0Sa{B)fBy&qYR@BH?E>7{T*0gvVa8}>m7p^_$Fo~Lr*tis~Q zb!fM$kdvQWJhk9|xE0+ygqO%E!^&)k(dXsvydW-r4^@r+h>W|e2)Th+v z)fa>6OX|z&Tk6~Ddm4?#plPbT zX0c|eX1V4a&BvP0H77KuG+%1Y#;9UUF^yu9W4gs;$IOhG9kV%RSIqU8-(&vN*3(94 zqqI`3R%_PUwO(xlZ9{D%Z5M62Hb*;98yu|7(-vxnYe#BFX(wxEXkXO6qJ34%X%}f1 zYnN)5Yu9T(((cyo)$Z4Rsy(bdsy(hfsXeWIqSNUb>YD0W=vwL8=(_0!=mzQr>+*E@ zx?#G2ZiH^4ZnAEwZn|!!j_79T=IAPQoNk3~r|yLAwmwp?)yM0T^sV)6^{GL92Ys48 zL!YVdrO(#))%Vkv>fhF{)1TB|)PJYHrGKJ-YJdi8sAq^Us0?aDjKN~?85$TG8X6gj z4O0x2hW8A+4SNmy4WAm08crBa8NM|9Xn1INVt8tVMr@2W%8Ux5%BV5M8f`|0(PeC8 zY->z4b}*(HI~%(igWZfhj025>jd{j=<1k~uIKo(D9Br&JzHVG*Tw$y>t}?DMZZ>W) zeqj92xXt*n@e|_-<3-~y#@~#;8}At(82>NS9VabvI?0`k3-f!%P9w2vdn^s%g4uriqvqm=>Fs22IOND^2g3cA9pZ_L}ya{xN&a zDduM87UovwG;b82UK5JWR zsu1&j)n# z*4dVB>u$@i4Y!T7jj|Qn#@J@tDr{A@dA9ks1-3VBD{O~tCv1=G(C)FnVDD`Y=GhDE zh4vBlBKv52xqXU#ntg^nXn)E6s-3gHVP9-tYF}<&X|J|#v~RcXw(qs?w;!+{vLCh| zwV$^>%jat1YUfIGb#`@iWwRKyH>f@xHh^rxi-7DxPp6J`(2;94!91v4!h2~ zF1RkbF1haUpYoV`Oe7P{$QTo2VQh?(VHh{lgh^qVF)f+aOk1Wq)04?!vYEb2Kc$&fF=*8ap-bin> zx1qPGH^V#5JHxxs%X#1MF7ht%F85Y@S9{laH+uJY_jy0{9`JtQJ>os?J>~r}=soAX z;Jxds?=$;+zV^PpzVW_F-!k7?-#Xt$-&Ws;zU{vKzE6Cg`HuOH`%d}3@?G+MC=c)!=*(4Xi}@~8N_`g{3@_=oue{t^CS|5$&i zzuZ5|Ki|K|zs$eFU+rJ(w{Gxn@^AKk;y>cQ;J@s@;=ks<>HpFHlmC|n(gqE;dR|=s zm2V}zowOurf70b-oa{@^-1^yTjB51D6$>l4iZ?6Xu2@>JqN1i^O~v|(jTPG~c2w-D z*jw>;Wn^V^rL0m}*`=~iWlm-P%7K+!<;u#M%GH(YD(_dpDqK~sDzd6?RY6r@)$ppK ns!dfps&-ZFsoFmm&sELU%+<~{% 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 diff --git a/source/IJSVG.m b/source/IJSVG.m index 45dbd38..b012a0f 100644 --- a/source/IJSVG.m +++ b/source/IJSVG.m @@ -420,6 +420,10 @@ static NSColor * _baseColor = nil; { CGContextSaveGState(ref); { + // for this to work, we need to add the clip so when + // drawing occurs, it doesnt go outside the path bounds + [path.path addClip]; + // apply any gradient fill transforms for( IJSVGTransform * transform in path.gradientTransforms ) { @@ -441,7 +445,8 @@ static NSColor * _baseColor = nil; [radGrad.gradient drawInBezierPath:path.path relativeCenterPosition:NSZeroPoint]; } - } CGContextRestoreGState(ref); + } + CGContextRestoreGState(ref); } else { // no gradient specified // just use the color instead diff --git a/source/IJSVGCommand.h b/source/IJSVGCommand.h index 54fb30a..df943ee 100644 --- a/source/IJSVGCommand.h +++ b/source/IJSVGCommand.h @@ -21,6 +21,7 @@ typedef NS_ENUM( NSInteger, IJSVGCommandType ) { @required + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path; @@ -39,6 +40,7 @@ typedef NS_ENUM( NSInteger, IJSVGCommandType ) { NSInteger requiredParameters; IJSVGCommandType type; IJSVGCommand * previousCommand; + NSInteger _currentIndex; Class commandClass; } @@ -63,4 +65,9 @@ typedef NS_ENUM( NSInteger, IJSVGCommandType ) { + (NSDictionary *)registeredCommandClasses; + (Class)commandClassForCommandLetter:(NSString *)str; +- (CGFloat)readFloat; +- (NSPoint)readPoint; +- (BOOL)readBOOL; +- (void)resetRead; + @end diff --git a/source/IJSVGCommand.m b/source/IJSVGCommand.m index 60eaafa..289baf2 100644 --- a/source/IJSVGCommand.m +++ b/source/IJSVGCommand.m @@ -39,6 +39,7 @@ static NSMutableDictionary * _classes = nil; if( ( self = [super init] ) != nil ) { // work out the basics + _currentIndex = 0; subCommands = [[NSMutableArray alloc] init]; command = [[str substringToIndex:1] copy]; type = [IJSVGUtils typeForCommandString:self.command]; @@ -125,4 +126,29 @@ static NSMutableDictionary * _classes = nil; return NSClassFromString(command); } +- (CGFloat)readFloat +{ + CGFloat f = parameters[_currentIndex]; + _currentIndex++; + return f; +} + +- (NSPoint)readPoint +{ + CGFloat x = parameters[_currentIndex]; + CGFloat y = parameters[_currentIndex+1]; + _currentIndex+=2; + return NSMakePoint( x, y ); +} + +- (BOOL)readBOOL +{ + return [self readFloat] == 1; +} + +- (void)resetRead +{ + _currentIndex = 0; +} + @end diff --git a/source/IJSVGCommandArc.m b/source/IJSVGCommandArc.m index 1ed1c9e..4e01404 100644 --- a/source/IJSVGCommandArc.m +++ b/source/IJSVGCommandArc.m @@ -7,6 +7,7 @@ // #import "IJSVGCommandArc.h" +#import "IJSVGUtils.h" @implementation IJSVGCommandArc @@ -23,31 +24,79 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path { -// NSPoint point = [path currentPoint]; -// -// CGFloat x1 = point.x; -// CGFloat y1 = point.y; -// -// NSPoint radii = [[self class] readCoordinatePair:params -// index:0]; -// -// CGFloat rX = fabs(radii.x); -// CGFloat rY = fabs(radii.y); -// -// CGFloat phi = params[2]; -// phi *= M_PI/180; -// -// BOOL largeArcFlag = params[3] != 0.f; -// BOOL sweepFlag = params[4] != 0.f; -// -// NSPoint endPoint = NSMakePoint( params[5], params[6]); -// -// CGFloat x2 = point.x + endPoint.x; -// CGFloat y2 = point.y + endPoint.y; + + // command was taken from: https://github.com/jmenter/JAMSVGImage/blob/89b375c0c3203355a0c693e3b805458415bf4e29/Classes/JAMSVGImage/Path%20and%20Parser/JAMStyledBezierPathFactory.m + // and modified to purpose inside this to be converted into degrees and not radians + + CGPoint radii = CGPointZero; + CGPoint arcEndPoint = CGPointZero; + CGPoint arcStartPoint = path.currentPoint; + CGFloat xAxisRotation = 0; + BOOL largeArcFlag = 0; + BOOL sweepFlag = 0; + + radii = [currentCommand readPoint]; + xAxisRotation = [currentCommand readFloat]; + largeArcFlag = [currentCommand readBOOL]; + sweepFlag = [currentCommand readBOOL]; + arcEndPoint = [currentCommand readPoint]; + + if ( type == IJSVGCommandTypeRelative ) { + arcEndPoint.x += path.currentPoint.x; + arcEndPoint.y += path.currentPoint.y; + } + + xAxisRotation *= M_PI / 180.f; + CGPoint currentPoint = CGPointMake(cos(xAxisRotation) * (arcStartPoint.x - arcEndPoint.x) / 2.0 + sin(xAxisRotation) * (arcStartPoint.y - arcEndPoint.y) / 2.0, -sin(xAxisRotation) * (arcStartPoint.x - arcEndPoint.x) / 2.0 + cos(xAxisRotation) * (arcStartPoint.y - arcEndPoint.y) / 2.0); + + CGFloat radiiAdjustment = pow(currentPoint.x, 2) / pow(radii.x, 2) + pow(currentPoint.y, 2) / pow(radii.y, 2); + radii.x *= (radiiAdjustment > 1) ? sqrt(radiiAdjustment) : 1; + radii.y *= (radiiAdjustment > 1) ? sqrt(radiiAdjustment) : 1; + + CGFloat sweep = (largeArcFlag == sweepFlag ? -1 : 1) * sqrt(((pow(radii.x, 2) * pow(radii.y, 2)) - (pow(radii.x, 2) * pow(currentPoint.y, 2)) - (pow(radii.y, 2) * pow(currentPoint.x, 2))) / (pow(radii.x, 2) * pow(currentPoint.y, 2) + pow(radii.y, 2) * pow(currentPoint.x, 2))); + sweep = (sweep != sweep) ? 0 : sweep; + CGPoint preCenterPoint = CGPointMake(sweep * radii.x * currentPoint.y / radii.y, sweep * -radii.y * currentPoint.x / radii.x); + + CGPoint centerPoint = CGPointMake((arcStartPoint.x + arcEndPoint.x) / 2.0 + cos(xAxisRotation) * preCenterPoint.x - sin(xAxisRotation) * preCenterPoint.y, (arcStartPoint.y + arcEndPoint.y) / 2.0 + sin(xAxisRotation) * preCenterPoint.x + cos(xAxisRotation) * preCenterPoint.y); + + CGFloat startAngle = angle(CGPointMake(1, 0), CGPointMake((currentPoint.x-preCenterPoint.x)/radii.x, + (currentPoint.y-preCenterPoint.y)/radii.y)); + + CGPoint deltaU = CGPointMake((currentPoint.x - preCenterPoint.x) / radii.x, + (currentPoint.y - preCenterPoint.y) / radii.y); + CGPoint deltaV = CGPointMake((-currentPoint.x - preCenterPoint.x) / radii.x, + (-currentPoint.y - preCenterPoint.y) / radii.y); + CGFloat angleDelta = (deltaU.x * deltaV.y < deltaU.y * deltaV.x ? -1 : 1) * acos(ratio(deltaU, deltaV)); + + angleDelta = (ratio(deltaU, deltaV) <= -1) ? M_PI : (ratio(deltaU, deltaV) >= 1) ? 0 : angleDelta; + + CGFloat radius = MAX(radii.x, radii.y); + CGPoint scale = (radii.x > radii.y) ? CGPointMake(1, radii.y / radii.x) : CGPointMake(radii.x / radii.y, 1); + + NSAffineTransform * trans = [NSAffineTransform transform]; + [trans translateXBy:-centerPoint.x yBy:-centerPoint.y]; + [trans rotateByRadians:-xAxisRotation]; + [trans scaleXBy:(1/scale.x) yBy:(1/scale.y)]; + + [path.currentSubpath transformUsingAffineTransform:trans]; + [path.currentSubpath appendBezierPathWithArcWithCenter:NSZeroPoint + radius:radius + startAngle:radians_to_degrees(startAngle) + endAngle:radians_to_degrees(startAngle + angleDelta) + clockwise:!sweepFlag]; + + trans = [NSAffineTransform transform]; + [trans translateXBy:centerPoint.x yBy:centerPoint.y]; + [trans rotateByRadians:xAxisRotation]; + [trans scaleXBy:scale.x yBy:scale.y]; + [path.currentSubpath transformUsingAffineTransform:trans]; + + } @end diff --git a/source/IJSVGCommandClose.m b/source/IJSVGCommandClose.m index a0ac856..7483443 100644 --- a/source/IJSVGCommandClose.m +++ b/source/IJSVGCommandClose.m @@ -23,6 +23,7 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path diff --git a/source/IJSVGCommandCommandSmoothQuadraticCurve.m b/source/IJSVGCommandCommandSmoothQuadraticCurve.m index 9278779..f15d768 100644 --- a/source/IJSVGCommandCommandSmoothQuadraticCurve.m +++ b/source/IJSVGCommandCommandSmoothQuadraticCurve.m @@ -24,6 +24,7 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path diff --git a/source/IJSVGCommandCurve.m b/source/IJSVGCommandCurve.m index cf8076b..faf840d 100644 --- a/source/IJSVGCommandCurve.m +++ b/source/IJSVGCommandCurve.m @@ -23,6 +23,7 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path diff --git a/source/IJSVGCommandHorizontalLine.m b/source/IJSVGCommandHorizontalLine.m index 3f4aa4b..d1c6f6c 100644 --- a/source/IJSVGCommandHorizontalLine.m +++ b/source/IJSVGCommandHorizontalLine.m @@ -23,6 +23,7 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path diff --git a/source/IJSVGCommandLineTo.m b/source/IJSVGCommandLineTo.m index 1650e62..f15bb06 100644 --- a/source/IJSVGCommandLineTo.m +++ b/source/IJSVGCommandLineTo.m @@ -23,6 +23,7 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path diff --git a/source/IJSVGCommandMove.m b/source/IJSVGCommandMove.m index 3b1eac0..4970fac 100644 --- a/source/IJSVGCommandMove.m +++ b/source/IJSVGCommandMove.m @@ -25,6 +25,7 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path @@ -35,6 +36,7 @@ { [IJSVGCommandLineTo runWithParams:params paramCount:count + command:currentCommand previousCommand:command type:type path:path]; diff --git a/source/IJSVGCommandQuadraticCurve.m b/source/IJSVGCommandQuadraticCurve.m index e8d4115..0c9a972 100644 --- a/source/IJSVGCommandQuadraticCurve.m +++ b/source/IJSVGCommandQuadraticCurve.m @@ -23,6 +23,7 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path diff --git a/source/IJSVGCommandSmoothCurve.m b/source/IJSVGCommandSmoothCurve.m index de46beb..1c3aff1 100644 --- a/source/IJSVGCommandSmoothCurve.m +++ b/source/IJSVGCommandSmoothCurve.m @@ -25,6 +25,7 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path diff --git a/source/IJSVGCommandVerticalLine.m b/source/IJSVGCommandVerticalLine.m index 24ac369..c507654 100644 --- a/source/IJSVGCommandVerticalLine.m +++ b/source/IJSVGCommandVerticalLine.m @@ -23,6 +23,7 @@ + (void)runWithParams:(CGFloat *)params paramCount:(NSInteger)count + command:(IJSVGCommand *)currentCommand previousCommand:(IJSVGCommand *)command type:(IJSVGCommandType)type path:(IJSVGPath *)path diff --git a/source/IJSVGParser.m b/source/IJSVGParser.m index 0e803aa..b31a972 100644 --- a/source/IJSVGParser.m +++ b/source/IJSVGParser.m @@ -238,9 +238,11 @@ if( fillAttribute != nil ) { NSString * defID = [IJSVGUtils defURL:[fillAttribute stringValue]]; - if( defID != nil ) - node.fillGradient = (IJSVGGradient *)[node defForID:defID]; - else { + if( defID != nil ) { + IJSVGGradient * grad = (IJSVGGradient *)[node defForID:defID]; + node.fillGradient = grad; + node.gradientTransforms = grad.gradientTransforms; + } else { // change the fill color over if its allowed node.fillColor = [IJSVGColor colorFromString:[fillAttribute stringValue]]; if( node.fillOpacity != 1.f ) @@ -324,8 +326,11 @@ if( [fill isKindOfClass:[NSString class]] ) { NSString * defID = [IJSVGUtils defURL:(NSString *)fill]; - if( defID != nil ) - node.fillGradient = (IJSVGGradient *)[node defForID:defID]; + if( defID != nil ) { + IJSVGGradient * grad = (IJSVGGradient *)[node defForID:defID]; + node.fillGradient = grad; + node.gradientTransforms = grad.gradientTransforms; + } } else { if( [IJSVGColor computeColor:fill] != nil ) node.fillColor = fill; @@ -775,6 +780,7 @@ { [subCommand.commandClass runWithParams:subCommand.parameters paramCount:subCommand.parameterCount + command:subCommand previousCommand:preCommand type:subCommand.type path:path]; diff --git a/source/IJSVGUtils.h b/source/IJSVGUtils.h index 2d4efe5..f829d08 100644 --- a/source/IJSVGUtils.h +++ b/source/IJSVGUtils.h @@ -11,6 +11,12 @@ @interface IJSVGUtils : NSObject +CGFloat magnitude(CGPoint point); +CGFloat ratio( CGPoint a, CGPoint b ); +CGFloat angle( CGPoint a, CGPoint b ); +CGFloat radians_to_degrees( CGFloat radians); +CGFloat degrees_to_radians( CGFloat degrees ); + + (IJSVGCommandType)typeForCommandString:(NSString *)string; + (NSRegularExpression *)commandNameRegex; + (NSRegularExpression *)commandRegex; diff --git a/source/IJSVGUtils.m b/source/IJSVGUtils.m index e82a908..96af5ee 100644 --- a/source/IJSVGUtils.m +++ b/source/IJSVGUtils.m @@ -12,6 +12,30 @@ #define FLOAT_EXP @"[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?" +CGFloat angle( CGPoint a, CGPoint b ) { + return [IJSVGUtils angleBetweenPointA:a + pointb:b]; +} + +CGFloat ratio( CGPoint a, CGPoint b ) { + return (a.x * b.x + a.y * b.y) / (magnitude(a) * magnitude(b)); +} + +CGFloat magnitude(CGPoint point) +{ + return sqrtf(powf(point.x, 2) + powf(point.y, 2)); +} + +CGFloat radians_to_degrees(CGFloat radians) +{ + return ((radians) * (180.0 / M_PI)); +} + +CGFloat degrees_to_radians( CGFloat degrees ) +{ + return ( ( degrees ) / 180.0 * M_PI ); +} + + (IJSVGCommandType)typeForCommandString:(NSString *)string { return [string isEqualToString:[string uppercaseString]] ? IJSVGCommandTypeAbsolute : IJSVGCommandTypeRelative; @@ -200,13 +224,10 @@ return [string floatValue]; } -+ (CGFloat)angleBetweenPointA:(NSPoint)startingPoint - pointb:(NSPoint)endingPoint ++ (CGFloat)angleBetweenPointA:(NSPoint)point1 + pointb:(NSPoint)point2 { - CGPoint originPoint = CGPointMake(endingPoint.x - startingPoint.x, endingPoint.y - startingPoint.y); - CGFloat bearingRadians = atan2f(originPoint.y, originPoint.x); - CGFloat bearingDegrees = bearingRadians * (180.0 / M_PI); - return (bearingDegrees > 0.0 ? bearingDegrees : (360.0 + bearingDegrees)); + return (point1.x * point2.y < point1.y * point2.x ? -1 : 1) * acosf(ratio(point1, point2)); } @end