diff --git a/SwiftSVG/SVG/Elements/SVGPath.swift b/SwiftSVG/SVG/Elements/SVGPath.swift index 2ae5a09..e2bf97f 100644 --- a/SwiftSVG/SVG/Elements/SVGPath.swift +++ b/SwiftSVG/SVG/Elements/SVGPath.swift @@ -90,8 +90,7 @@ final class SVGPath: SVGShapeElement, ParsesAsynchronously, DelaysApplyingAttrib let parsePathClosure = { var previousCommand: PreviousCommand? = nil for thisPathCommand in PathDLexer(pathString: workingString) { - thisPathCommand.execute(on: pathDPath, previousCommand: previousCommand) - previousCommand = thisPathCommand + previousCommand = thisPathCommand.execute(on: pathDPath, previousCommand: previousCommand) } } diff --git a/SwiftSVG/SVG/Iterators/PathCommand.swift b/SwiftSVG/SVG/Iterators/PathCommand.swift index 9e918b5..dabc73e 100644 --- a/SwiftSVG/SVG/Iterators/PathCommand.swift +++ b/SwiftSVG/SVG/Iterators/PathCommand.swift @@ -69,7 +69,7 @@ internal protocol PathCommand: PreviousCommand { - Parameter path: The path to append a new path to - Parameter previousCommand: An optional previous command. Used primarily with the shortcut cubic and quadratic Bezier types */ - func execute(on path: UIBezierPath, previousCommand: PreviousCommand?) + func execute(on path: UIBezierPath, previousCommand: PreviousCommand?) -> PreviousCommand? } /** @@ -162,7 +162,7 @@ internal struct MoveTo: PathCommand { /** This will move the current point to `CGPoint(self.coordinateBuffer[0], self.coordinateBuffer[1])`. - Sequential MoveTo commands should be treated as LineTos. + Sequential implicit MoveTo commands should be treated as LineTos. From Docs (https://www.w3.org/TR/SVG2/paths.html#PathDataMovetoCommands): @@ -170,17 +170,16 @@ internal struct MoveTo: PathCommand { Start a new sub-path at the given (x,y) coordinates. M (uppercase) indicates that absolute coordinates will follow; m (lowercase) indicates that relative coordinates will follow. If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit lineto commands. Hence, implicit lineto commands will be relative if the moveto is relative, and absolute if the moveto is absolute. ``` */ - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { - + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { if previousCommand is MoveTo { var implicitLineTo = LineTo(pathType: self.pathType) implicitLineTo.coordinateBuffer = [self.coordinateBuffer[0], self.coordinateBuffer[1]] - implicitLineTo.execute(on: path) - return + return implicitLineTo.execute(on: path) } let point = self.pointForPathType(CGPoint(x: self.coordinateBuffer[0], y: self.coordinateBuffer[1]), relativeTo: path.currentPoint) path.move(to: point) + return self } } @@ -206,8 +205,9 @@ internal struct ClosePath: PathCommand { /** Closes the current path */ - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { path.close() + return self } } @@ -234,9 +234,10 @@ internal struct LineTo: PathCommand { /** Creates a line from the `path.currentPoint` to point `CGPoint(self.coordinateBuffer[0], coordinateBuffer[1])` */ - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { let point = self.pointForPathType(CGPoint(x: self.coordinateBuffer[0], y: self.coordinateBuffer[1]), relativeTo: path.currentPoint) path.addLine(to: point) + return self } } @@ -262,10 +263,11 @@ internal struct HorizontalLineTo: PathCommand { /** Adds a horizontal line from the currentPoint to `CGPoint(self.coordinateBuffer[0], path.currentPoint.y)` */ - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { let x = self.coordinateBuffer[0] let point = (self.pathType == .absolute ? CGPoint(x: CGFloat(x), y: path.currentPoint.y) : CGPoint(x: path.currentPoint.x + CGFloat(x), y: path.currentPoint.y)) path.addLine(to: point) + return self } } @@ -291,10 +293,11 @@ internal struct VerticalLineTo: PathCommand { /** Adds a vertical line from the currentPoint to `CGPoint(path.currentPoint.y, self.coordinateBuffer[0])` */ - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { let y = self.coordinateBuffer[0] let point = (self.pathType == .absolute ? CGPoint(x: path.currentPoint.x, y: CGFloat(y)) : CGPoint(x: path.currentPoint.x, y: path.currentPoint.y + CGFloat(y))) path.addLine(to: point) + return self } } @@ -320,11 +323,12 @@ internal struct CurveTo: PathCommand { /** Adds a cubic Bezier curve to `path`. The path will end up at `CGPoint(self.coordinateBuffer[4], self.coordinateBuffer[5])`. The control point for `path.currentPoint` will be `CGPoint(self.coordinateBuffer[0], self.coordinateBuffer[1])`. Then controle point for the end point will be CGPoint(self.coordinateBuffer[2], self.coordinateBuffer[3]) */ - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { let startControl = self.pointForPathType(CGPoint(x: self.coordinateBuffer[0], y: self.coordinateBuffer[1]), relativeTo: path.currentPoint) let endControl = self.pointForPathType(CGPoint(x: self.coordinateBuffer[2], y: self.coordinateBuffer[3]), relativeTo: path.currentPoint) let point = self.pointForPathType(CGPoint(x: self.coordinateBuffer[4], y: self.coordinateBuffer[5]), relativeTo: path.currentPoint) path.addCurve(to: point, controlPoint1: startControl, controlPoint2: endControl) + return self } } @@ -350,7 +354,7 @@ internal struct SmoothCurveTo: PathCommand { /** Shortcut cubic Bezier curve to that add a new path ending up at `CGPoint(self.coordinateBuffer[0], self.coordinateBuffer[1])` with a single control point in the middle. */ - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { let point = self.pointForPathType(CGPoint(x: self.coordinateBuffer[2], y: self.coordinateBuffer[3]), relativeTo: path.currentPoint) let controlEnd = self.pointForPathType(CGPoint(x: self.coordinateBuffer[0], y: self.coordinateBuffer[1]), relativeTo: path.currentPoint) @@ -394,6 +398,7 @@ internal struct SmoothCurveTo: PathCommand { controlStart = path.currentPoint } path.addCurve(to: point, controlPoint1: controlStart, controlPoint2: controlEnd) + return self } } @@ -416,10 +421,11 @@ internal struct QuadraticCurveTo: PathCommand { self.pathType = pathType } - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { let controlPoint = self.pointForPathType(CGPoint(x: self.coordinateBuffer[0], y: self.coordinateBuffer[1]), relativeTo: path.currentPoint) let point = self.pointForPathType(CGPoint(x: self.coordinateBuffer[2], y: self.coordinateBuffer[3]), relativeTo: path.currentPoint) path.addQuadCurve(to: point, controlPoint: controlPoint) + return self } } @@ -445,7 +451,7 @@ internal struct SmoothQuadraticCurveTo: PathCommand { self.pathType = pathType } - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { let point = self.pointForPathType(CGPoint(x: self.coordinateBuffer[0], y: self.coordinateBuffer[1]), relativeTo: path.currentPoint) @@ -471,6 +477,7 @@ internal struct SmoothQuadraticCurveTo: PathCommand { controlPoint = path.currentPoint } path.addQuadCurve(to: point, controlPoint: controlPoint) + return self } } @@ -495,7 +502,8 @@ internal struct EllipticalArc: PathCommand { } /// :nodoc: - internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) { + internal func execute(on path: UIBezierPath, previousCommand: PreviousCommand? = nil) -> PreviousCommand? { assert(false, "Needs Implementation") + return self } } diff --git a/SwiftSVGTests/CGPathPoints.swift b/SwiftSVGTests/CGPathPoints.swift index 74b93e9..0a717a7 100644 --- a/SwiftSVGTests/CGPathPoints.swift +++ b/SwiftSVGTests/CGPathPoints.swift @@ -93,7 +93,7 @@ extension PathCommand { init(parameters: [Double], pathType: PathType, path: UIBezierPath, previousCommand: PreviousCommand? = nil) { self.init(pathType: pathType) self.coordinateBuffer = parameters - self.execute(on: path, previousCommand: previousCommand) + _ = self.execute(on: path, previousCommand: previousCommand) } } diff --git a/SwiftSVGTests/PathDLexerTests.swift b/SwiftSVGTests/PathDLexerTests.swift index 02f3939..6c9e43d 100644 --- a/SwiftSVGTests/PathDLexerTests.swift +++ b/SwiftSVGTests/PathDLexerTests.swift @@ -36,7 +36,7 @@ class PathDLexerTests: XCTestCase { let testString = "M80,45z" let testPath = UIBezierPath() for thisCommand in PathDLexer(pathString: testString) { - thisCommand.execute(on: testPath, previousCommand: nil) + _ = thisCommand.execute(on: testPath, previousCommand: nil) } XCTAssert(testPath.cgPath.pointsAndTypes[0].1 == .moveToPoint, "Expected MoveTo, got \(type(of: testPath.cgPath.pointsAndTypes[0].1))") XCTAssert(testPath.currentPoint.x == 80 && testPath.currentPoint.y == 45, "Expected {80, 45}, got \(testPath.currentPoint)") @@ -46,7 +46,7 @@ class PathDLexerTests: XCTestCase { let testString = "M80-45z" let testPath = UIBezierPath() for thisCommand in PathDLexer(pathString: testString) { - thisCommand.execute(on: testPath, previousCommand: nil) + _ = thisCommand.execute(on: testPath, previousCommand: nil) } XCTAssert(testPath.cgPath.pointsAndTypes[0].1 == .moveToPoint, "Expected MoveTo, got \(type(of: testPath.cgPath.pointsAndTypes[0].1))") XCTAssert(testPath.currentPoint.x == 80 && testPath.currentPoint.y == -45, "Expected {80, -45}, got \(testPath.currentPoint)") @@ -56,7 +56,7 @@ class PathDLexerTests: XCTestCase { let testString = "M80 -45z" let testPath = UIBezierPath() for thisCommand in PathDLexer(pathString: testString) { - thisCommand.execute(on: testPath, previousCommand: nil) + _ = thisCommand.execute(on: testPath, previousCommand: nil) } XCTAssert(testPath.cgPath.pointsAndTypes[0].1 == .moveToPoint, "Expected MoveTo, got \(type(of: testPath.cgPath.pointsAndTypes[0].1))") XCTAssert(testPath.currentPoint.x == 80 && testPath.currentPoint.y == -45, "Expected {80, -45}, got \(testPath.currentPoint)") @@ -66,7 +66,7 @@ class PathDLexerTests: XCTestCase { let testString = "M 47 -127 z" let testPath = UIBezierPath() for thisCommand in PathDLexer(pathString: testString) { - thisCommand.execute(on: testPath, previousCommand: nil) + _ = thisCommand.execute(on: testPath, previousCommand: nil) } XCTAssert(testPath.cgPath.pointsAndTypes[0].1 == .moveToPoint, "Expected MoveTo, got \(type(of: testPath.cgPath.pointsAndTypes[0].1))") XCTAssert(testPath.currentPoint.x == 47 && testPath.currentPoint.y == -127, "Expected {47, -127}, got \(testPath.currentPoint)") @@ -76,7 +76,7 @@ class PathDLexerTests: XCTestCase { let testString = "M30-40L20,50z M10,20L80,90z" let testPath = UIBezierPath() for thisCommand in PathDLexer(pathString: testString) { - thisCommand.execute(on: testPath, previousCommand: nil) + _ = thisCommand.execute(on: testPath, previousCommand: nil) } XCTAssert(testPath.cgPath.pointsAndTypes[2].1 == .closeSubpath, "Expected .closeSubpath, got \(String(describing: testPath.cgPath.pointsAndTypes[2].1))") XCTAssert(testPath.cgPath.pointsAndTypes.last!.1 == .closeSubpath, "Expected .closeSubpath, got \(String(describing: testPath.cgPath.pointsAndTypes.last!.1))") @@ -86,7 +86,7 @@ class PathDLexerTests: XCTestCase { let testString = "m193.2 162.44c4.169 0 8.065 0.631 12.327 0.631 3.391 0 7.062-0.404 10.427 0 0.881 0.105 3.056 0.187 3.793 0.633 1.551 0.939 1.013 0.261 1.265 2.527 0.164 1.476 0 3.078 0 4.565 0 1.211-0.826 6.618-0.315 7.129 1.238 1.238 5.366 2.607 7.269 3.476 2.513 1.146 4.488 2.562 6.952 3.793 2.435 1.217 5.177 1.804 4.426 4.424-0.364 1.271-1.808 2.668-2.529 3.793-0.438 0.684-2.528 3.339-2.528 4.109 0 0.606-13.593-2.429-15.17-2.846-2.26-0.598-5.387-0.692-6.004-3.16-0.631-2.525-0.661-5.73-0.949-8.217-0.358-3.095 1.059-6.32-2.844-6.32-2.102 0-5.384 0.628-7.269-0.316-1.008-0.505-2.737 0.105-3.476-0.632-0.979-0.977-1.59-1.802-2.214-3.478-1.23-3.25-2.25-6.73-3.17-10.1" let testPath = UIBezierPath() for thisCommand in PathDLexer(pathString: testString) { - thisCommand.execute(on: testPath, previousCommand: nil) + _ = thisCommand.execute(on: testPath, previousCommand: nil) } } @@ -94,11 +94,24 @@ class PathDLexerTests: XCTestCase { let testString = "M1.5.5z" let testPath = UIBezierPath() for thisCommand in PathDLexer(pathString: testString) { - thisCommand.execute(on: testPath, previousCommand: nil) + _ = thisCommand.execute(on: testPath, previousCommand: nil) } XCTAssert(testPath.cgPath.pointsAndTypes[0].1 == .moveToPoint, "Expected MoveTo, got \(type(of: testPath.cgPath.pointsAndTypes[0].1))") XCTAssert(testPath.currentPoint.x == 1.5 && testPath.currentPoint.y == 0.5, "Expected {1.5, 0.5}, got \(testPath.currentPoint)") } + + func testMultipleMoveTo() { + let testString = "m40,40 40,0 m0,40 0,-40" + let testPath = UIBezierPath() + var previousCommand: PreviousCommand? = nil + for thisCommand in PathDLexer(pathString: testString) { + previousCommand = thisCommand.execute(on: testPath, previousCommand: previousCommand) + } + XCTAssert(testPath.cgPath.pointsAndTypes[0].1 == .moveToPoint, "Expected MoveTo, got \(type(of: testPath.cgPath.pointsAndTypes[0].1))") + XCTAssert(testPath.cgPath.pointsAndTypes[1].1 == .addLineToPoint, "Expected addLineToPoint, got \(type(of: testPath.cgPath.pointsAndTypes[1].1))") + XCTAssert(testPath.cgPath.pointsAndTypes[2].1 == .moveToPoint, "Expected moveToPoint, got \(type(of: testPath.cgPath.pointsAndTypes[2].1))") + XCTAssert(testPath.cgPath.pointsAndTypes[3].1 == .addLineToPoint, "Expected addLineToPoint, got \(type(of: testPath.cgPath.pointsAndTypes[3].1))") + } }