From f07e8f041caf258cb5bfb6abf27e316d312b2554 Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Sat, 30 Dec 2017 03:35:58 +0200 Subject: [PATCH] Allow TextPath to reference any RNSVGNode instead of only RNSVGPath. Remove unused RNSVGTextAnchor --- ios/Elements/RNSVGPath.h | 2 - ios/Elements/RNSVGPath.m | 251 ------------------------- ios/RNSVG.xcodeproj/project.pbxproj | 2 - ios/Text/RNSVGTSpan.m | 2 +- ios/Text/RNSVGText.h | 1 - ios/Text/RNSVGTextPath.h | 3 +- ios/Text/RNSVGTextPath.m | 267 +++++++++++++++++++++++++-- ios/Utils/BezierElement.h | 4 + ios/Utils/BezierElement.m | 52 ++++++ ios/Utils/RCTConvert+RNSVG.h | 2 - ios/Utils/RCTConvert+RNSVG.m | 7 - ios/Utils/RNSVGPathParser.h | 1 - ios/Utils/RNSVGPathParser.m | 38 ---- ios/Utils/RNSVGTextAnchor.h | 14 -- ios/ViewManagers/RNSVGTSpanManager.m | 1 - 15 files changed, 312 insertions(+), 335 deletions(-) delete mode 100644 ios/Utils/RNSVGTextAnchor.h diff --git a/ios/Elements/RNSVGPath.h b/ios/Elements/RNSVGPath.h index ea8e2431..2caf060e 100644 --- a/ios/Elements/RNSVGPath.h +++ b/ios/Elements/RNSVGPath.h @@ -14,6 +14,4 @@ @property (nonatomic, strong) RNSVGPathParser *d; -- (void)getPathLength:(CGFloat*)length lineCount:(NSInteger*)lineCount lengths:(NSArray* __strong *)lengths lines:(NSArray* __strong *)lines isClosed:(BOOL*)isClosed; - @end diff --git a/ios/Elements/RNSVGPath.m b/ios/Elements/RNSVGPath.m index 95eeb199..fc77c23f 100644 --- a/ios/Elements/RNSVGPath.m +++ b/ios/Elements/RNSVGPath.m @@ -7,256 +7,10 @@ */ #import "RNSVGPath.h" -#import "BezierElement.h" - -/* Bezier logic from PerformanceBezier */ -/* - - ## License - - Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 United States License. - - For attribution, please include: - - 1. Mention original author "Adam Wulf for Loose Leaf app" - 2. Link to https://getlooseleaf.com/opensource/ - 3. Link to https://github.com/adamwulf/PerformanceBezier - - */ -static CGFloat idealFlatness = .01; - -/** - * returns the distance between two points - */ -CGFloat distance(CGPoint p1, CGPoint p2) -{ - CGFloat dx = p2.x - p1.x; - CGFloat dy = p2.y - p1.y; - - return sqrt(dx*dx + dy*dy); -} - -/** - * returns the dot product of two coordinates - */ -CGFloat dotProduct(const CGPoint p1, const CGPoint p2) { - return p1.x * p2.x + p1.y * p2.y; -} - -/** - * returns the shortest distance from a point to a line - */ -CGFloat distanceOfPointToLine(CGPoint point, CGPoint start, CGPoint end){ - CGPoint v = CGPointMake(end.x - start.x, end.y - start.y); - CGPoint w = CGPointMake(point.x - start.x, point.y - start.y); - CGFloat c1 = dotProduct(w, v); - CGFloat c2 = dotProduct(v, v); - CGFloat d; - if (c1 <= 0) { - d = distance(point, start); - } - else if (c2 <= c1) { - d = distance(point, end); - } - else { - CGFloat b = c1 / c2; - CGPoint Pb = CGPointMake(start.x + b * v.x, start.y + b * v.y); - d = distance(point, Pb); - } - return d; -} - -/** - * calculate the point on a bezier at time t - * where 0 < t < 1 - */ -CGPoint bezierPointAtT(const CGPoint bez[4], CGFloat t) -{ - CGPoint q; - CGFloat mt = 1 - t; - - CGPoint bez1[4]; - CGPoint bez2[4]; - - q.x = mt * bez[1].x + t * bez[2].x; - q.y = mt * bez[1].y + t * bez[2].y; - bez1[1].x = mt * bez[0].x + t * bez[1].x; - bez1[1].y = mt * bez[0].y + t * bez[1].y; - bez2[2].x = mt * bez[2].x + t * bez[3].x; - bez2[2].y = mt * bez[2].y + t * bez[3].y; - - bez1[2].x = mt * bez1[1].x + t * q.x; - bez1[2].y = mt * bez1[1].y + t * q.y; - bez2[1].x = mt * q.x + t * bez2[2].x; - bez2[1].y = mt * q.y + t * bez2[2].y; - - bez1[3].x = bez2[0].x = mt * bez1[2].x + t * bez2[1].x; - bez1[3].y = bez2[0].y = mt * bez1[2].y + t * bez2[1].y; - - return CGPointMake(bez1[3].x, bez1[3].y); -} - -// Subdivide a Bézier (specific division) -/* - * (c) 2004 Alastair J. Houghton - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. The name of the author of this software may not be used to endorse - * or promote products derived from the software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -void subdivideBezierAtT(const CGPoint bez[4], CGPoint bez1[4], CGPoint bez2[4], CGFloat t) -{ - CGPoint q; - CGFloat mt = 1 - t; - - bez1[0].x = bez[0].x; - bez1[0].y = bez[0].y; - bez2[3].x = bez[3].x; - bez2[3].y = bez[3].y; - - q.x = mt * bez[1].x + t * bez[2].x; - q.y = mt * bez[1].y + t * bez[2].y; - bez1[1].x = mt * bez[0].x + t * bez[1].x; - bez1[1].y = mt * bez[0].y + t * bez[1].y; - bez2[2].x = mt * bez[2].x + t * bez[3].x; - bez2[2].y = mt * bez[2].y + t * bez[3].y; - - bez1[2].x = mt * bez1[1].x + t * q.x; - bez1[2].y = mt * bez1[1].y + t * q.y; - bez2[1].x = mt * q.x + t * bez2[2].x; - bez2[1].y = mt * q.y + t * bez2[2].y; - - bez1[3].x = bez2[0].x = mt * bez1[2].x + t * bez2[1].x; - bez1[3].y = bez2[0].y = mt * bez1[2].y + t * bez2[1].y; -} - -void addLine(CGPoint *last, const CGPoint *next, NSMutableArray *lines, CGFloat *length, NSMutableArray *lengths) { - NSArray *line = @[[NSValue valueWithCGPoint:*last], [NSValue valueWithCGPoint:*next]]; - [lines addObject:line]; - *length += distance(*last, *next); - [lengths addObject:[NSNumber numberWithDouble:*length]]; - *last = *next; -} @implementation RNSVGPath { CGPathRef _path; - NSMutableArray *lengths; - NSMutableArray *lines; - NSInteger lineCount; - CGFloat length; - BOOL isClosed; - BOOL cached; -} - -- (void)getPathLength:(CGFloat*)lengthP lineCount:(NSInteger*)lineCountP lengths:(NSArray* __strong *)lengthsP lines:(NSArray* __strong *)linesP isClosed:(BOOL*)isClosedP -{ - if (!cached) { - CGPoint origin = CGPointMake (0.0, 0.0); - CGPoint last = CGPointMake (0.0, 0.0); - lengths = [NSMutableArray array]; - lines = [NSMutableArray array]; - isClosed = NO; - lineCount = 0; - length = 0; - - NSArray *elements = [_d getBezierCurves]; - for (BezierElement *element in elements) { - switch (element.elementType) - { - case kCGPathElementMoveToPoint: - origin = last = element.point; - break; - - case kCGPathElementAddLineToPoint: { - CGPoint next = element.point; - addLine(&last, &next, lines, &length, lengths); - lineCount++; - break; - } - case kCGPathElementAddQuadCurveToPoint: - case kCGPathElementAddCurveToPoint: - { - // handle both curve types gracefully - CGPoint curveTo = element.point; - CGPoint ctrl1 = element.controlPoint1; - CGPoint ctrl2 = element.elementType == kCGPathElementAddQuadCurveToPoint ? ctrl1 : element.controlPoint2; - - // this is the bezier for our current element - CGPoint bezier[4] = { last, ctrl1, ctrl2, curveTo }; - NSValue *arr = [NSValue valueWithBytes:&bezier objCType:@encode(CGPoint[4])]; - NSMutableArray *curves = [NSMutableArray arrayWithObjects:arr, nil]; - - for (NSInteger curveIndex = 0; curveIndex >= 0; curveIndex--) { - CGPoint bez[4]; - [curves[curveIndex] getValue:&bez]; - [curves removeLastObject]; - - // calculate the error rate of the curve vs - // a line segement between the start and end points - CGPoint onCurve = bezierPointAtT(bez, .5); - CGPoint next = bez[3]; - CGFloat error = distanceOfPointToLine(onCurve, last, next); - - // if the error is less than our accepted level of error - // then add a line, else, split the curve in half - if (error <= idealFlatness) { - addLine(&last, &next, lines, &length, lengths); - lineCount++; - } else { - CGPoint bez1[4], bez2[4]; - subdivideBezierAtT(bez, bez1, bez2, .5); - [curves addObject:[NSValue valueWithBytes:&bez2 objCType:@encode(CGPoint[4])]]; - [curves addObject:[NSValue valueWithBytes:&bez1 objCType:@encode(CGPoint[4])]]; - curveIndex += 2; - } - } - last = curveTo; - break; - } - - case kCGPathElementCloseSubpath: { - CGPoint next = origin; - addLine(&last, &next, lines, &length, lengths); - lineCount++; - isClosed = YES; - break; - } - - default: - break; - } - } - cached = YES; - } - *lineCountP = lineCount; - *isClosedP = isClosed; - *lengthsP = lengths; - *lengthP = length; - *linesP = lines; } - (void)setD:(RNSVGPathParser *)d @@ -267,9 +21,6 @@ void addLine(CGPoint *last, const CGPoint *next, NSMutableArray *lines, CGFloat [self invalidate]; _d = d; - lines = nil; - lengths = nil; - cached = false; CGPathRelease(_path); _path = CGPathRetain([d getPath]); } @@ -281,8 +32,6 @@ void addLine(CGPoint *last, const CGPoint *next, NSMutableArray *lines, CGFloat - (void)dealloc { - lines = nil; - lengths = nil; CGPathRelease(_path); } diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj index 88570bcd..6bfe28ac 100644 --- a/ios/RNSVG.xcodeproj/project.pbxproj +++ b/ios/RNSVG.xcodeproj/project.pbxproj @@ -248,7 +248,6 @@ 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGTextPath.m; path = Text/RNSVGTextPath.m; sourceTree = ""; }; 7F08CE9E1E23479700650F83 /* RNSVGTSpan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTSpan.h; path = Text/RNSVGTSpan.h; sourceTree = ""; }; 7F08CE9F1E23479700650F83 /* RNSVGTSpan.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGTSpan.m; path = Text/RNSVGTSpan.m; sourceTree = ""; }; - 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextAnchor.h; path = Utils/RNSVGTextAnchor.h; sourceTree = ""; }; 7F69160D1E3703D800DA6EDC /* RNSVGUnits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGUnits.h; path = Utils/RNSVGUnits.h; sourceTree = ""; }; 7F9CDAF81E1F809C00E0C805 /* RNSVGPathParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGPathParser.h; path = Utils/RNSVGPathParser.h; sourceTree = ""; }; 7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPathParser.m; path = Utils/RNSVGPathParser.m; sourceTree = ""; }; @@ -501,7 +500,6 @@ 7F69160D1E3703D800DA6EDC /* RNSVGUnits.h */, 10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */, 10ABC7371D439779006CCF6E /* RNSVGCGFCRule.h */, - 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */, 7FC260CC1E3499BC00A39833 /* RNSVGViewBox.h */, 7FC260CD1E3499BC00A39833 /* RNSVGViewBox.m */, 1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */, diff --git a/ios/Text/RNSVGTSpan.m b/ios/Text/RNSVGTSpan.m index 1d549ccc..486699dd 100644 --- a/ios/Text/RNSVGTSpan.m +++ b/ios/Text/RNSVGTSpan.m @@ -870,7 +870,7 @@ CGFloat getTextAnchorOffset(enum TextAnchor textAnchor, CGFloat width) [self traverseTextSuperviews:^(__kindof RNSVGText *node) { if ([node class] == [RNSVGTextPath class]) { textPath = (RNSVGTextPath*) node; - [[textPath getPath] getPathLength:&_pathLength lineCount:&lineCount lengths:&lengths lines:&lines isClosed:&isClosed]; + [textPath getPathLength:&_pathLength lineCount:&lineCount lengths:&lengths lines:&lines isClosed:&isClosed]; return NO; } return YES; diff --git a/ios/Text/RNSVGText.h b/ios/Text/RNSVGText.h index 42f66e40..47dddf2e 100644 --- a/ios/Text/RNSVGText.h +++ b/ios/Text/RNSVGText.h @@ -8,7 +8,6 @@ #import #import "RNSVGGroup.h" -#import "RNSVGTextAnchor.h" #import "AlignmentBaseline.h" @interface RNSVGText : RNSVGGroup diff --git a/ios/Text/RNSVGTextPath.h b/ios/Text/RNSVGTextPath.h index ec08b7ad..4105dc6f 100644 --- a/ios/Text/RNSVGTextPath.h +++ b/ios/Text/RNSVGTextPath.h @@ -19,6 +19,7 @@ @property (nonatomic, strong) NSString *spacing; @property (nonatomic, strong) NSString *startOffset; -- (RNSVGPath *)getPath; +- (void)getPathLength:(CGFloat*)length lineCount:(NSInteger*)lineCount lengths:(NSArray* __strong *)lengths lines:(NSArray* __strong *)lines isClosed:(BOOL*)isClosed; + @end diff --git a/ios/Text/RNSVGTextPath.m b/ios/Text/RNSVGTextPath.m index 310a6b0e..db633b60 100644 --- a/ios/Text/RNSVGTextPath.m +++ b/ios/Text/RNSVGTextPath.m @@ -8,28 +8,267 @@ #import "RNSVGTextPath.h" +#import "BezierElement.h" + +/* Bezier logic from PerformanceBezier */ +/* + + ## License + + Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 United States License. + + For attribution, please include: + + 1. Mention original author "Adam Wulf for Loose Leaf app" + 2. Link to https://getlooseleaf.com/opensource/ + 3. Link to https://github.com/adamwulf/PerformanceBezier + + */ +static CGFloat idealFlatness = .01; + +/** + * returns the distance between two points + */ +CGFloat distance(CGPoint p1, CGPoint p2) +{ + CGFloat dx = p2.x - p1.x; + CGFloat dy = p2.y - p1.y; + + return sqrt(dx*dx + dy*dy); +} + +/** + * returns the dot product of two coordinates + */ +CGFloat dotProduct(const CGPoint p1, const CGPoint p2) { + return p1.x * p2.x + p1.y * p2.y; +} + +/** + * returns the shortest distance from a point to a line + */ +CGFloat distanceOfPointToLine(CGPoint point, CGPoint start, CGPoint end){ + CGPoint v = CGPointMake(end.x - start.x, end.y - start.y); + CGPoint w = CGPointMake(point.x - start.x, point.y - start.y); + CGFloat c1 = dotProduct(w, v); + CGFloat c2 = dotProduct(v, v); + CGFloat d; + if (c1 <= 0) { + d = distance(point, start); + } + else if (c2 <= c1) { + d = distance(point, end); + } + else { + CGFloat b = c1 / c2; + CGPoint Pb = CGPointMake(start.x + b * v.x, start.y + b * v.y); + d = distance(point, Pb); + } + return d; +} + +/** + * calculate the point on a bezier at time t + * where 0 < t < 1 + */ +CGPoint bezierPointAtT(const CGPoint bez[4], CGFloat t) +{ + CGPoint q; + CGFloat mt = 1 - t; + + CGPoint bez1[4]; + CGPoint bez2[4]; + + q.x = mt * bez[1].x + t * bez[2].x; + q.y = mt * bez[1].y + t * bez[2].y; + bez1[1].x = mt * bez[0].x + t * bez[1].x; + bez1[1].y = mt * bez[0].y + t * bez[1].y; + bez2[2].x = mt * bez[2].x + t * bez[3].x; + bez2[2].y = mt * bez[2].y + t * bez[3].y; + + bez1[2].x = mt * bez1[1].x + t * q.x; + bez1[2].y = mt * bez1[1].y + t * q.y; + bez2[1].x = mt * q.x + t * bez2[2].x; + bez2[1].y = mt * q.y + t * bez2[2].y; + + bez1[3].x = bez2[0].x = mt * bez1[2].x + t * bez2[1].x; + bez1[3].y = bez2[0].y = mt * bez1[2].y + t * bez2[1].y; + + return CGPointMake(bez1[3].x, bez1[3].y); +} + +// Subdivide a Bézier (specific division) +/* + * (c) 2004 Alastair J. Houghton + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author of this software may not be used to endorse + * or promote products derived from the software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +void subdivideBezierAtT(const CGPoint bez[4], CGPoint bez1[4], CGPoint bez2[4], CGFloat t) +{ + CGPoint q; + CGFloat mt = 1 - t; + + bez1[0].x = bez[0].x; + bez1[0].y = bez[0].y; + bez2[3].x = bez[3].x; + bez2[3].y = bez[3].y; + + q.x = mt * bez[1].x + t * bez[2].x; + q.y = mt * bez[1].y + t * bez[2].y; + bez1[1].x = mt * bez[0].x + t * bez[1].x; + bez1[1].y = mt * bez[0].y + t * bez[1].y; + bez2[2].x = mt * bez[2].x + t * bez[3].x; + bez2[2].y = mt * bez[2].y + t * bez[3].y; + + bez1[2].x = mt * bez1[1].x + t * q.x; + bez1[2].y = mt * bez1[1].y + t * q.y; + bez2[1].x = mt * q.x + t * bez2[2].x; + bez2[1].y = mt * q.y + t * bez2[2].y; + + bez1[3].x = bez2[0].x = mt * bez1[2].x + t * bez2[1].x; + bez1[3].y = bez2[0].y = mt * bez1[2].y + t * bez2[1].y; +} + +void addLine(CGPoint *last, const CGPoint *next, NSMutableArray *lines, CGFloat *length, NSMutableArray *lengths) { + NSArray *line = @[[NSValue valueWithCGPoint:*last], [NSValue valueWithCGPoint:*next]]; + [lines addObject:line]; + *length += distance(*last, *next); + [lengths addObject:[NSNumber numberWithDouble:*length]]; + *last = *next; +} @implementation RNSVGTextPath +{ + CGPathRef _path; + NSMutableArray *lengths; + NSMutableArray *lines; + NSInteger lineCount; + CGFloat length; + BOOL isClosed; +} + +- (void)getPathLength:(CGFloat*)lengthP lineCount:(NSInteger*)lineCountP lengths:(NSArray* __strong *)lengthsP lines:(NSArray* __strong *)linesP isClosed:(BOOL*)isClosedP +{ + RNSVGSvgView *svg = [self getSvgView]; + RNSVGNode *template = [svg getDefinedTemplate:self.href]; + CGPathRef path = [template getPath:nil]; + + if (_path != path) { + _path = path; + CGPoint origin = CGPointMake (0.0, 0.0); + CGPoint last = CGPointMake (0.0, 0.0); + lengths = [NSMutableArray array]; + lines = [NSMutableArray array]; + isClosed = NO; + lineCount = 0; + length = 0; + + NSArray *elements = [BezierElement elementsFromCGPath:path]; + for (BezierElement *element in elements) { + switch (element.elementType) + { + case kCGPathElementMoveToPoint: + origin = last = element.point; + break; + + case kCGPathElementAddLineToPoint: { + CGPoint next = element.point; + addLine(&last, &next, lines, &length, lengths); + lineCount++; + break; + } + case kCGPathElementAddQuadCurveToPoint: + case kCGPathElementAddCurveToPoint: + { + // handle both curve types gracefully + CGPoint curveTo = element.point; + CGPoint ctrl1 = element.controlPoint1; + CGPoint ctrl2 = element.elementType == kCGPathElementAddQuadCurveToPoint ? ctrl1 : element.controlPoint2; + + // this is the bezier for our current element + CGPoint bezier[4] = { last, ctrl1, ctrl2, curveTo }; + NSValue *arr = [NSValue valueWithBytes:&bezier objCType:@encode(CGPoint[4])]; + NSMutableArray *curves = [NSMutableArray arrayWithObjects:arr, nil]; + + for (NSInteger curveIndex = 0; curveIndex >= 0; curveIndex--) { + CGPoint bez[4]; + [curves[curveIndex] getValue:&bez]; + [curves removeLastObject]; + + // calculate the error rate of the curve vs + // a line segement between the start and end points + CGPoint onCurve = bezierPointAtT(bez, .5); + CGPoint next = bez[3]; + CGFloat error = distanceOfPointToLine(onCurve, last, next); + + // if the error is less than our accepted level of error + // then add a line, else, split the curve in half + if (error <= idealFlatness) { + addLine(&last, &next, lines, &length, lengths); + lineCount++; + } else { + CGPoint bez1[4], bez2[4]; + subdivideBezierAtT(bez, bez1, bez2, .5); + [curves addObject:[NSValue valueWithBytes:&bez2 objCType:@encode(CGPoint[4])]]; + [curves addObject:[NSValue valueWithBytes:&bez1 objCType:@encode(CGPoint[4])]]; + curveIndex += 2; + } + } + last = curveTo; + break; + } + + case kCGPathElementCloseSubpath: { + CGPoint next = origin; + addLine(&last, &next, lines, &length, lengths); + lineCount++; + isClosed = YES; + break; + } + + default: + break; + } + } + } + + *lineCountP = lineCount; + *isClosedP = isClosed; + *lengthsP = lengths; + *lengthP = length; + *linesP = lines; +} - (void)renderLayerTo:(CGContextRef)context { [self renderGroupTo:context]; } -- (RNSVGPath *)getPath -{ - RNSVGSvgView *svg = [self getSvgView]; - RNSVGNode *template = [svg getDefinedTemplate:self.href]; - - if ([template class] != [RNSVGPath class]) { - // warning about this. - return nil; - } - - RNSVGPath *path = (RNSVGPath *)template; - return path; -} - - (CGPathRef)getPath:(CGContextRef)context { return [self getGroupPath:context]; diff --git a/ios/Utils/BezierElement.h b/ios/Utils/BezierElement.h index a78c8721..5d59efc0 100644 --- a/ios/Utils/BezierElement.h +++ b/ios/Utils/BezierElement.h @@ -17,5 +17,9 @@ @property (nonatomic, assign) CGPoint controlPoint1; @property (nonatomic, assign) CGPoint controlPoint2; +// Instance creation ++ (instancetype) elementWithPathElement: (CGPathElement) element; ++ (NSArray *) elementsFromCGPath:(CGPathRef)path; + @end; diff --git a/ios/Utils/BezierElement.m b/ios/Utils/BezierElement.m index 73a8eb50..98bc65d1 100644 --- a/ios/Utils/BezierElement.m +++ b/ios/Utils/BezierElement.m @@ -21,5 +21,57 @@ } return self; } + ++ (instancetype) elementWithPathElement: (CGPathElement) element +{ + BezierElement *newElement = [[self alloc] init]; + newElement.elementType = element.type; + + switch (newElement.elementType) + { + case kCGPathElementCloseSubpath: + break; + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: + { + newElement.point = element.points[0]; + break; + } + case kCGPathElementAddQuadCurveToPoint: + { + newElement.point = element.points[1]; + newElement.controlPoint1 = element.points[0]; + break; + } + case kCGPathElementAddCurveToPoint: + { + newElement.point = element.points[2]; + newElement.controlPoint1 = element.points[0]; + newElement.controlPoint2 = element.points[1]; + break; + } + default: + break; + } + + return newElement; +} + +// Convert one element to BezierElement and save to array +void GetBezierElements(void *info, const CGPathElement *element) +{ + NSMutableArray *bezierElements = (__bridge NSMutableArray *)info; + if (element) + [bezierElements addObject:[BezierElement elementWithPathElement:*element]]; +} + +// Retrieve array of component elements ++ (NSArray *) elementsFromCGPath:(CGPathRef)path +{ + NSMutableArray *elements = [NSMutableArray array]; + CGPathApply(path, (__bridge void *)elements, GetBezierElements); + return elements; +} + @end diff --git a/ios/Utils/RCTConvert+RNSVG.h b/ios/Utils/RCTConvert+RNSVG.h index 0b266d45..49500fd6 100644 --- a/ios/Utils/RCTConvert+RNSVG.h +++ b/ios/Utils/RCTConvert+RNSVG.h @@ -13,7 +13,6 @@ #import #import "RNSVGCGFCRule.h" #import "RNSVGVBMOS.h" -#import "RNSVGTextAnchor.h" #import "RNSVGUnits.h" #import "RNSVGPathParser.h" @@ -21,7 +20,6 @@ @interface RCTConvert (RNSVG) -+ (RNSVGTextAnchor)RNSVGTextAnchor:(id)json; + (RNSVGCGFCRule)RNSVGCGFCRule:(id)json; + (RNSVGVBMOS)RNSVGVBMOS:(id)json; + (RNSVGUnits)RNSVGUnits:(id)json; diff --git a/ios/Utils/RCTConvert+RNSVG.m b/ios/Utils/RCTConvert+RNSVG.m index d3554ed0..53655148 100644 --- a/ios/Utils/RCTConvert+RNSVG.m +++ b/ios/Utils/RCTConvert+RNSVG.m @@ -26,13 +26,6 @@ RCT_ENUM_CONVERTER(RNSVGVBMOS, (@{ @"none": @(kRNSVGVBMOSNone) }), kRNSVGVBMOSMeet, intValue) -RCT_ENUM_CONVERTER(RNSVGTextAnchor, (@{ - @"auto": @(kRNSVGTextAnchorAuto), - @"start": @(kRNSVGTextAnchorStart), - @"middle": @(kRNSVGTextAnchorMiddle), - @"end": @(kRNSVGTextAnchorEnd) - }), kRNSVGTextAnchorAuto, intValue) - RCT_ENUM_CONVERTER(RNSVGUnits, (@{ @"objectBoundingBox": @(kRNSVGUnitsObjectBoundingBox), @"userSpaceOnUse": @(kRNSVGUnitsUserSpaceOnUse), diff --git a/ios/Utils/RNSVGPathParser.h b/ios/Utils/RNSVGPathParser.h index da53782b..5ea68b82 100644 --- a/ios/Utils/RNSVGPathParser.h +++ b/ios/Utils/RNSVGPathParser.h @@ -13,6 +13,5 @@ - (instancetype) initWithPathString:(NSString *)d; - (CGPathRef)getPath; -- (NSArray *)getBezierCurves; @end diff --git a/ios/Utils/RNSVGPathParser.m b/ios/Utils/RNSVGPathParser.m index 76d78c71..1c434cde 100644 --- a/ios/Utils/RNSVGPathParser.m +++ b/ios/Utils/RNSVGPathParser.m @@ -16,7 +16,6 @@ NSString* _d; NSString* _originD; NSRegularExpression* _pathRegularExpression; - NSMutableArray* _bezierCurves; float _penX; float _penY; float _penDownX; @@ -42,7 +41,6 @@ { CGMutablePathRef path = CGPathCreateMutable(); NSArray* results = [_pathRegularExpression matchesInString:_d options:0 range:NSMakeRange(0, [_d length])]; - _bezierCurves = [[NSMutableArray alloc] init]; unsigned long count = [results count]; if (count) { @@ -121,15 +119,6 @@ return (CGPathRef)CFAutorelease(path); } -- (NSArray *)getBezierCurves -{ - if (!_bezierCurves) { - CGPathRelease([self getPath]); - } - - return [_bezierCurves copy]; -} - - (NSString *)getNextValue:(NSTextCheckingResult *)result { if (!result) { @@ -158,11 +147,6 @@ _pivotX = _penX = x; _pivotY = _penY = y; CGPathMoveToPoint(path, nil, x, y); - - BezierElement *newElement = [[BezierElement alloc] init]; - newElement.elementType = kCGPathElementMoveToPoint; - newElement.point = CGPointMake(x, y); - [_bezierCurves addObject:newElement]; } - (void)line:(CGMutablePathRef)path x:(float)x y:(float)y @@ -175,11 +159,6 @@ _pivotX = _penX = x; _pivotY = _penY = y; CGPathAddLineToPoint(path, nil, x, y); - - BezierElement *newElement = [[BezierElement alloc] init]; - newElement.elementType = kCGPathElementAddLineToPoint; - newElement.point = CGPointMake(x, y); - [_bezierCurves addObject:newElement]; } - (void)curve:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey @@ -205,13 +184,6 @@ _penX = ex; _penY = ey; CGPathAddCurveToPoint(path, nil, c1x, c1y, c2x, c2y, ex, ey); - - BezierElement *newElement = [[BezierElement alloc] init]; - newElement.elementType = kCGPathElementAddCurveToPoint; - newElement.controlPoint1 = CGPointMake(c1x, c1y); - newElement.controlPoint2 = CGPointMake(c2x, c2y); - newElement.point = CGPointMake(ex, ey); - [_bezierCurves addObject:newElement]; } - (void)smoothCurve:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y ex:(float)ex ey:(float)ey @@ -379,13 +351,6 @@ float ex = cx + xx * x + yx * y; float ey = cy + xy * x + yy * y; CGPathAddCurveToPoint(path, nil, c1x, c1y, c2x, c2y, ex, ey); - - BezierElement *newElement = [[BezierElement alloc] init]; - newElement.elementType = kCGPathElementAddCurveToPoint; - newElement.controlPoint1 = CGPointMake(c1x, c1y); - newElement.controlPoint2 = CGPointMake(c2x, c2y); - newElement.point = CGPointMake(ex, ey); - [_bezierCurves addObject:newElement]; } } @@ -396,9 +361,6 @@ _penY = _penDownY; _penDownSet = NO; CGPathCloseSubpath(path); - BezierElement *newElement = [[BezierElement alloc] init]; - newElement.elementType = kCGPathElementCloseSubpath; - [_bezierCurves addObject:newElement]; } } diff --git a/ios/Utils/RNSVGTextAnchor.h b/ios/Utils/RNSVGTextAnchor.h deleted file mode 100644 index 06eded9d..00000000 --- a/ios/Utils/RNSVGTextAnchor.h +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) 2015-present, Horcrux. - * All rights reserved. - * - * This source code is licensed under the MIT-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -typedef CF_ENUM(int32_t, RNSVGTextAnchor) { - kRNSVGTextAnchorAuto, - kRNSVGTextAnchorStart, - kRNSVGTextAnchorMiddle, - kRNSVGTextAnchorEnd -}; diff --git a/ios/ViewManagers/RNSVGTSpanManager.m b/ios/ViewManagers/RNSVGTSpanManager.m index d9ddc646..fc5ca79d 100644 --- a/ios/ViewManagers/RNSVGTSpanManager.m +++ b/ios/ViewManagers/RNSVGTSpanManager.m @@ -9,7 +9,6 @@ #import "RNSVGTSpanManager.h" #import "RNSVGTSpan.h" -#import "RNSVGTextAnchor.h" #import "RCTConvert+RNSVG.h" @implementation RNSVGTSpanManager