Last active
June 27, 2023 14:05
-
-
Save dotcypress/fa4b5c739b02f4374402 to your computer and use it in GitHub Desktop.
Revisions
-
dotcypress revised this gist
Nov 6, 2015 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -22,8 +22,8 @@ public extension CGPath { case .Line: CGPathAddLineToPoint(path, nil, command.point.x, command.point.y) case .QuadCurve: CGPathAddQuadCurveToPoint(path, nil, command.control1.x, command.control1.y, command.point.x, command.point.y) case .CubeCurve: CGPathAddCurveToPoint(path, nil, command.control1.x, command.control1.y, -
dotcypress renamed this gist
Nov 6, 2015 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
dotcypress created this gist
Nov 6, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,293 @@ // // SVGPath.swift // SVGPath // // Created by Tim Wood on 1/21/15. // Updated by Vitaly Domnikov 10/6/2015 // Copyright (c) 2015 Tim Wood, Vitaly Domnikov. All rights reserved. import Foundation import CoreGraphics public extension CGPath { // Convert SVG path to CGPath static func fromSvgPath(svgPath: String) -> CGPath? { let path = CGPathCreateMutable() CGPathMoveToPoint(path, nil, 0, 0) let commands = SVGPath(svgPath).commands for command in commands { switch command.type { case .Move: CGPathMoveToPoint(path, nil, command.point.x, command.point.y) case .Line: CGPathAddLineToPoint(path, nil, command.point.x, command.point.y) case .QuadCurve: CGPathAddQuadCurveToPoint(path, nil, command.control1.x, command.control1.y, command.point.x, command.point.y) case .CubeCurve: CGPathAddCurveToPoint(path, nil, command.control1.x, command.control1.y, command.control2.x, command.control2.y, command.point.x, command.point.y) case .Close: CGPathCloseSubpath(path) } } return path } } // MARK: Enums private enum Coordinates { case Absolute case Relative } // MARK: Class public class SVGPath { public var commands: [SVGCommand] = [] private var builder: SVGCommandBuilder = moveTo private var coords: Coordinates = .Absolute private var stride: Int = 2 private var numbers = "" public init(_ string: String) { commands.reserveCapacity(200) for char in string.characters { switch char { case "M": use(.Absolute, 2, moveTo) case "m": use(.Relative, 2, moveTo) case "L": use(.Absolute, 2, lineTo) case "l": use(.Relative, 2, lineTo) case "V": use(.Absolute, 1, lineToVertical) case "v": use(.Relative, 1, lineToVertical) case "H": use(.Absolute, 1, lineToHorizontal) case "h": use(.Relative, 1, lineToHorizontal) case "Q": use(.Absolute, 4, quadBroken) case "q": use(.Relative, 4, quadBroken) case "T": use(.Absolute, 2, quadSmooth) case "t": use(.Relative, 2, quadSmooth) case "C": use(.Absolute, 6, cubeBroken) case "c": use(.Relative, 6, cubeBroken) case "S": use(.Absolute, 4, cubeSmooth) case "s": use(.Relative, 4, cubeSmooth) case "Z": use(.Absolute, 0, close) case "z": use(.Relative, 0, close) default: numbers.append(char) } } finishLastCommand() } private func use(coords: Coordinates, _ stride: Int, _ builder: SVGCommandBuilder) { finishLastCommand() self.builder = builder self.coords = coords self.stride = stride } private func finishLastCommand() { for command in take(SVGPath.parseNumbers(numbers), stride: stride, coords: coords, last: commands.last, callback: builder) { commands.append(coords == .Relative ? command.relativeTo(commands.last) : command) } numbers = "" } } // MARK: Numbers private let numberSet = NSCharacterSet(charactersInString: "-.0123456789eE") private let numberFormatter = NSNumberFormatter() public extension SVGPath { class func parseNumbers(numbers: String) -> [CGFloat] { numberFormatter.numberStyle = .DecimalStyle numberFormatter.allowsFloats = true numberFormatter.decimalSeparator = "." var all: [String] = [] var curr = "" var last = "" for char in numbers.unicodeScalars { let next = String(char) if next == "-" && last != "" && last != "E" && last != "e" { if curr.utf16.count > 0 { all.append(curr) } curr = next } else if numberSet.longCharacterIsMember(char.value) { curr += next } else if curr.utf16.count > 0 { all.append(curr) curr = "" } last = next } all.append(curr) return all .filter { numberFormatter.numberFromString($0) != nil } .map { CGFloat((numberFormatter.numberFromString($0)?.floatValue)!) } } } // MARK: Commands public struct SVGCommand { public var point: CGPoint public var control1: CGPoint public var control2: CGPoint public var type: Kind public enum Kind { case Move case Line case CubeCurve case QuadCurve case Close } public init() { let point = CGPoint() self.init(point, point, point, type: .Close) } public init(_ x: CGFloat, _ y: CGFloat, type: Kind) { let point = CGPoint(x: x, y: y) self.init(point, point, point, type: type) } public init(_ cx: CGFloat, _ cy: CGFloat, _ x: CGFloat, _ y: CGFloat) { let control = CGPoint(x: cx, y: cy) self.init(control, control, CGPoint(x: x, y: y), type: .QuadCurve) } public init(_ cx1: CGFloat, _ cy1: CGFloat, _ cx2: CGFloat, _ cy2: CGFloat, _ x: CGFloat, _ y: CGFloat) { self.init(CGPoint(x: cx1, y: cy1), CGPoint(x: cx2, y: cy2), CGPoint(x: x, y: y), type: .CubeCurve) } public init(_ control1: CGPoint, _ control2: CGPoint, _ point: CGPoint, type: Kind) { self.point = point self.control1 = control1 self.control2 = control2 self.type = type } private func relativeTo(other: SVGCommand?) -> SVGCommand { if let otherPoint = other?.point { return SVGCommand(control1 + otherPoint, control2 + otherPoint, point + otherPoint, type: type) } return self } } // MARK: CGPoint helpers private func +(a: CGPoint, b: CGPoint) -> CGPoint { return CGPoint(x: a.x + b.x, y: a.y + b.y) } private func -(a: CGPoint, b: CGPoint) -> CGPoint { return CGPoint(x: a.x - b.x, y: a.y - b.y) } // MARK: Command Builders private typealias SVGCommandBuilder = ([CGFloat], SVGCommand?, Coordinates) -> SVGCommand private func take(numbers: [CGFloat], stride: Int, coords: Coordinates, last: SVGCommand?, callback: SVGCommandBuilder) -> [SVGCommand] { var out: [SVGCommand] = [] var lastCommand: SVGCommand? = last var nums: [CGFloat] = [0, 0, 0, 0, 0, 0]; if stride == 0 { lastCommand = callback(nums, lastCommand, coords) out.append(lastCommand!) } else { let count = (numbers.count / stride) * stride for var i = 0; i < count; i += stride { for var j = 0; j < stride; j++ { nums[j] = numbers[i + j] } lastCommand = callback(nums, lastCommand, coords) out.append(lastCommand!) } } return out } // MARK: Mm - Move private func moveTo(numbers: [CGFloat], last: SVGCommand?, coords: Coordinates) -> SVGCommand { return SVGCommand(numbers[0], numbers[1], type: .Move) } // MARK: Ll - Line private func lineTo(numbers: [CGFloat], last: SVGCommand?, coords: Coordinates) -> SVGCommand { return SVGCommand(numbers[0], numbers[1], type: .Line) } // MARK: Vv - Vertical Line private func lineToVertical(numbers: [CGFloat], last: SVGCommand?, coords: Coordinates) -> SVGCommand { return SVGCommand(coords == .Absolute ? last?.point.x ?? 0 : 0, numbers[0], type: .Line) } // MARK: Hh - Horizontal Line private func lineToHorizontal(numbers: [CGFloat], last: SVGCommand?, coords: Coordinates) -> SVGCommand { return SVGCommand(numbers[0], coords == .Absolute ? last?.point.y ?? 0 : 0, type: .Line) } // MARK: Qq - Quadratic Curve To private func quadBroken(numbers: [CGFloat], last: SVGCommand?, coords: Coordinates) -> SVGCommand { return SVGCommand(numbers[0], numbers[1], numbers[2], numbers[3]) } // MARK: Tt - Smooth Quadratic Curve To private func quadSmooth(numbers: [CGFloat], last: SVGCommand?, coords: Coordinates) -> SVGCommand { var lastControl = last?.control1 ?? CGPoint() let lastPoint = last?.point ?? CGPoint() if (last?.type ?? .Line) != .QuadCurve { lastControl = lastPoint } var control = lastPoint - lastControl if coords == .Absolute { control = control + lastPoint } return SVGCommand(control.x, control.y, numbers[0], numbers[1]) } // MARK: Cc - Cubic Curve To private func cubeBroken(numbers: [CGFloat], last: SVGCommand?, coords: Coordinates) -> SVGCommand { return SVGCommand(numbers[0], numbers[1], numbers[2], numbers[3], numbers[4], numbers[5]) } // MARK: Ss - Smooth Cubic Curve To private func cubeSmooth(numbers: [CGFloat], last: SVGCommand?, coords: Coordinates) -> SVGCommand { var lastControl = last?.control2 ?? CGPoint() let lastPoint = last?.point ?? CGPoint() if (last?.type ?? .Line) != .CubeCurve { lastControl = lastPoint } var control = lastPoint - lastControl if coords == .Absolute { control = control + lastPoint } return SVGCommand(control.x, control.y, numbers[0], numbers[1], numbers[2], numbers[3]) } // MARK: Zz - Close Path private func close(numbers: [CGFloat], last: SVGCommand?, coords: Coordinates) -> SVGCommand { return SVGCommand() }