├── .gitignore ├── Images ├── demo.gif └── dependency.png ├── LICENSE ├── README.md ├── monad 心得.md ├── monad 心得_2.md ├── monad 心得_3.md ├── one ├── Pretty.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Pretty │ ├── AppDelegate.swift │ ├── Controller │ └── ViewController.swift │ ├── Extension │ ├── Array+Extension.swift │ ├── CharacterSet+Extension.swift │ ├── Color+Difference.swift │ ├── NSPoint+Extension.swift │ ├── NSRect+Dependency.swift │ └── PrettyRelation+Extension.swift │ ├── Info.plist │ ├── Model │ ├── DependencyNode.swift │ └── PrettyRelation.swift │ ├── Parser │ ├── Parser+Extension.swift │ ├── Parser+Runes.swift │ ├── Parser.swift │ ├── PodLockFileParser.swift │ └── Runes.swift │ ├── Pretty.entitlements │ ├── RandomColors │ ├── ColorDefinition.swift │ ├── Hue.swift │ ├── Luminosity.swift │ ├── RandomColor.h │ └── RandomColor.swift │ ├── Resource │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_128x128.png │ │ │ ├── icon_128x128@2x.png │ │ │ ├── icon_16x16.png │ │ │ ├── icon_16x16@2x.png │ │ │ ├── icon_256x256.png │ │ │ ├── icon_256x256@2x.png │ │ │ ├── icon_32x32.png │ │ │ ├── icon_32x32@2x.png │ │ │ ├── icon_512x512.png │ │ │ └── icon_512x512@2x.png │ │ └── Contents.json │ └── Base.lproj │ │ └── Main.storyboard │ ├── Utils │ ├── ColorDifference.swift │ ├── ColorSpaceConversion.swift │ ├── DependencySort.swift │ └── HexColors.swift │ └── View │ ├── RelationItemView.swift │ └── RelationView.swift ├── three ├── Pretty.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Pretty │ ├── AppDelegate.swift │ ├── Controller │ └── ViewController.swift │ ├── Extension │ ├── Array+Extension.swift │ ├── CharacterSet+Extension.swift │ ├── NSPoint+Extension.swift │ └── NSRect+Dependency.swift │ ├── Info.plist │ ├── Pretty.entitlements │ ├── Resource │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_128x128.png │ │ │ ├── icon_128x128@2x.png │ │ │ ├── icon_16x16.png │ │ │ ├── icon_16x16@2x.png │ │ │ ├── icon_256x256.png │ │ │ ├── icon_256x256@2x.png │ │ │ ├── icon_32x32.png │ │ │ ├── icon_32x32@2x.png │ │ │ ├── icon_512x512.png │ │ │ └── icon_512x512@2x.png │ │ └── Contents.json │ ├── Base.lproj │ │ └── Main.storyboard │ └── one.json │ └── View │ └── RelationItemView.swift ├── two ├── Pretty.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Pretty │ ├── AppDelegate.swift │ ├── Controller │ └── ViewController.swift │ ├── Extension │ ├── Array+Extension.swift │ ├── CharacterSet+Extension.swift │ ├── Color+Difference.swift │ ├── NSPoint+Extension.swift │ ├── NSRect+Dependency.swift │ └── PrettyRelation+Extension.swift │ ├── Info.plist │ ├── Model │ ├── DependencyNode.swift │ └── PrettyRelation.swift │ ├── Parser │ └── Parser.swift │ ├── Pretty.entitlements │ ├── RandomColors │ ├── ColorDefinition.swift │ ├── Hue.swift │ ├── Luminosity.swift │ ├── RandomColor.h │ └── RandomColor.swift │ ├── Resource │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_128x128.png │ │ │ ├── icon_128x128@2x.png │ │ │ ├── icon_16x16.png │ │ │ ├── icon_16x16@2x.png │ │ │ ├── icon_256x256.png │ │ │ ├── icon_256x256@2x.png │ │ │ ├── icon_32x32.png │ │ │ ├── icon_32x32@2x.png │ │ │ ├── icon_512x512.png │ │ │ └── icon_512x512@2x.png │ │ └── Contents.json │ └── Base.lproj │ │ └── Main.storyboard │ ├── Utils │ ├── ColorDifference.swift │ ├── ColorSpaceConversion.swift │ ├── DependencySort.swift │ └── HexColors.swift │ └── View │ ├── RelationItemView.swift │ └── RelationView.swift └── 结构.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/xcode,macos,swift 3 | 4 | ### macOS ### 5 | *.DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | .com.apple.timemachine.donotpresent 23 | 24 | # Directories potentially created on remote AFP share 25 | .AppleDB 26 | .AppleDesktop 27 | Network Trash Folder 28 | Temporary Items 29 | .apdisk 30 | 31 | ### Swift ### 32 | # Xcode 33 | # 34 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 35 | 36 | ## Build generated 37 | build/ 38 | DerivedData/ 39 | 40 | ## Various settings 41 | *.pbxuser 42 | !default.pbxuser 43 | *.mode1v3 44 | !default.mode1v3 45 | *.mode2v3 46 | !default.mode2v3 47 | *.perspectivev3 48 | !default.perspectivev3 49 | xcuserdata/ 50 | 51 | ## Other 52 | *.moved-aside 53 | *.xccheckout 54 | *.xcscmblueprint 55 | 56 | ## Obj-C/Swift specific 57 | *.hmap 58 | *.ipa 59 | *.dSYM.zip 60 | *.dSYM 61 | 62 | ## Playgrounds 63 | timeline.xctimeline 64 | playground.xcworkspace 65 | 66 | # Swift Package Manager 67 | # 68 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 69 | # Packages/ 70 | # Package.pins 71 | .build/ 72 | 73 | # CocoaPods - Refactored to standalone file 74 | 75 | # Carthage - Refactored to standalone file 76 | 77 | # fastlane 78 | # 79 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 80 | # screenshots whenever they are needed. 81 | # For more information about the recommended setup visit: 82 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 83 | 84 | fastlane/report.xml 85 | fastlane/Preview.html 86 | fastlane/screenshots 87 | fastlane/test_output 88 | 89 | ### Xcode ### 90 | # Xcode 91 | # 92 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 93 | 94 | ## Build generated 95 | 96 | ## Various settings 97 | 98 | ## Other 99 | 100 | ### Xcode Patch ### 101 | *.xcodeproj/* 102 | !*.xcodeproj/project.pbxproj 103 | !*.xcodeproj/xcshareddata/ 104 | !*.xcworkspace/contents.xcworkspacedata 105 | /*.gcno 106 | 107 | 108 | # End of https://www.gitignore.io/api/xcode,macos,swift -------------------------------------------------------------------------------- /Images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/Images/demo.gif -------------------------------------------------------------------------------- /Images/dependency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/Images/dependency.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CopyCat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # podfile_lock_resolution 2 | 我叫邓江. , 有参考 octree/pretty 3 | 4 | 5 | 6 | 7 | #### 有参考 octree/pretty ( 这个库的精华,是数据解析部分 ) 8 | 9 | 10 | 一个根据 Podfile.lock 生成依赖图的 Mac App。 11 | 12 | 13 | > 和节点颜色相同的线连接子节点。 14 | 15 | 16 | > 被拖拽的节点,红色连接子节点,蓝色连接父节点。 17 | 18 | ## Demo 19 | 20 | 21 | 效果: 22 | 23 | 24 | ![image](https://github.com/octree/pretty/raw/master/Images/dependency.png) 25 | 26 | 27 | 使用: 28 | 29 | ![demo.gif](https://github.com/octree/pretty/raw/master/Images/demo.gif) 30 | 31 | ## How To Use 32 | 33 | > Dowload & Build 34 | -------------------------------------------------------------------------------- /monad 心得.md: -------------------------------------------------------------------------------- 1 | 理解 haskell, 2 | 3 | 4 | 就是不断的,函数展开 5 | 6 | 7 | 8 |
9 | 10 | m a, 11 | 12 | 即 Monadic a 13 | 14 | 15 | 16 |
17 | 18 | 19 | 初始化函数: 20 | 21 | ``` 22 | func or(_ other: Parser) -> Parser { 23 | 24 | return Parser(parseX: { 25 | input in 26 | return self.parseX(input) ?? other.parseX(input) 27 | }) 28 | } 29 | 30 | ``` 31 | 32 | 33 | 等价于 34 | 35 | 36 | ``` 37 | 38 | 39 | func or(_ other: Parser) -> Parser { 40 | 41 | return Parser { 42 | input in 43 | return self.parseX(input) ?? other.parseX(input) 44 | } 45 | } 46 | 47 | ``` 48 | 49 | 50 |
51 | 52 | 53 | 简化,就是显式类型, 54 | 55 | 降低抽样性 56 | -------------------------------------------------------------------------------- /monad 心得_2.md: -------------------------------------------------------------------------------- 1 | 不能确定类型,是因为不给足信息,可以存在多种解释 2 | 3 |
4 | 5 | closure, type define 6 | 7 | 8 | 签名非常的不直观 9 | 10 |
11 | 12 | 函数式编程, 13 | 14 | 就是瞎几把变化 15 | 16 | map 17 | 18 | transform 19 | 20 | 21 |
22 | 23 | 24 | 写得不好, 25 | 26 | 代码非常的冗余, 27 | 28 |
29 | 30 | 数据有一点变化, 31 | 32 | 33 | 调整为正常, 34 | 35 | 36 | 都要好几步 37 | -------------------------------------------------------------------------------- /monad 心得_3.md: -------------------------------------------------------------------------------- 1 | 见识了 2 | 3 | 函数式编程,语法糖 4 | 5 |
6 | 7 | 设计感 8 | 9 | 函数单元可靠,流畅 10 | 11 |
12 | 13 | 14 | // 定义数据处理的逻辑单元, 函数式编程 15 | 16 | 17 |
18 | 19 | 20 | 21 | 管道的劣势是, 22 | 23 | 为了满足管道传输, 24 | 25 | 26 | 一般数据都需要额外的处理 27 | 28 | 29 |
30 | 31 | 32 | 33 | 函数式编程,真有意思 34 | 35 | 知道输入的类型, 36 | 37 | 参数的函数签名, 38 | 39 | 求输出的类型 40 | 41 |
42 | 43 | 44 | 关键是要,摸清楚套路 45 | 46 |
47 | 48 | 49 | 展开可以省略流程 50 | 51 | 52 | 展开,可以简化 53 | -------------------------------------------------------------------------------- /one/Pretty.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /one/Pretty.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /one/Pretty/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | let OCTOpenFileNotification = "OCTOpenFileNotification" 12 | public var FileName = "" 13 | 14 | @NSApplicationMain 15 | class AppDelegate: NSObject, NSApplicationDelegate { 16 | 17 | func applicationDidFinishLaunching(_ aNotification: Notification) { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | func applicationWillTerminate(_ aNotification: Notification) { 22 | // Insert code here to tear down your application 23 | } 24 | 25 | 26 | 27 | func application(_ sender: NSApplication, openFile filename: String) -> Bool { 28 | 29 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: OCTOpenFileNotification) , object: filename) 30 | FileName = filename 31 | return true 32 | } 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /one/Pretty/Controller/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | @IBOutlet weak var scrollView: NSScrollView! 14 | 15 | private let relationView = RelationView() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | scrollView.hasVerticalScroller = true 21 | scrollView.hasHorizontalScroller = true 22 | scrollView.documentView = relationView 23 | 24 | NotificationCenter.default.addObserver(self, selector: #selector(self.handleOpenFile(notification:)), name: NSNotification.Name(rawValue: OCTOpenFileNotification), object: nil) 25 | // FileName = "/Users/jzd/Downloads/Lumiere/Podfile.lock" 26 | if FileName.count > 0 { 27 | 28 | updateRelationView(filename: FileName) 29 | } 30 | } 31 | 32 | override func viewDidLayout() { 33 | super.viewDidLayout() 34 | 35 | guard !relationView.frame.equalTo(CGRect()) else { 36 | return 37 | } 38 | 39 | let size = relationView.prettyRelation.preferredSize 40 | let parentSize = scrollView.frame.size 41 | relationView.frame = CGRect(x: 0, 42 | y: 0, 43 | width: max(size.width, parentSize.width), 44 | height: max(size.height, parentSize.height)) 45 | } 46 | 47 | 48 | @objc func handleOpenFile(notification: Notification) { 49 | 50 | guard let filename = notification.object as? String else { 51 | return 52 | } 53 | 54 | updateRelationView(filename: filename) 55 | } 56 | 57 | func updateRelationView(filename: String) { 58 | 59 | view.window?.title = filename 60 | if filename.hasSuffix(".lock") { 61 | updateWithLockFile(filename: filename) 62 | } else { 63 | updateWithDataFile(filename: filename) 64 | } 65 | } 66 | 67 | func updateWithLockFile(filename: String) { 68 | do { 69 | let string = try String(contentsOfFile: filename, encoding: .utf8) 70 | if let (dependency, _) = PodLockFileParser.parseX(Substring(string)) { 71 | // print(dependency) 72 | relationView.prettyRelation = PrettyRelation(dependency: dependency) 73 | } else { 74 | alert(title: "Error", msg: "Parse Error: Wrong Format") 75 | } 76 | } catch { 77 | 78 | alert(title: "Error", msg: error.localizedDescription) 79 | } 80 | } 81 | 82 | 83 | func updateWithDataFile(filename: String) { 84 | 85 | do { 86 | 87 | let url = URL(fileURLWithPath: filename) 88 | let data = try Data(contentsOf: url) 89 | let relation = try JSONDecoder().decode(PrettyRelation.self, from: data) 90 | relationView.prettyRelation = relation 91 | } catch { 92 | 93 | alert(title: "Error", msg: error.localizedDescription) 94 | } 95 | } 96 | 97 | 98 | func alert(title: String, msg: String) { 99 | 100 | 101 | let alert = NSAlert() 102 | 103 | 104 | alert.addButton(withTitle: "Ok") 105 | alert.messageText = title 106 | alert.informativeText = msg 107 | alert.alertStyle = .warning 108 | alert.runModal() 109 | } 110 | 111 | } 112 | 113 | -------------------------------------------------------------------------------- /one/Pretty/Extension/Array+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+Extension.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Array { 12 | 13 | func group(_ size: Int) -> [[Element]] { 14 | 15 | let rows = (count + size - 1) / size 16 | var groups = Array<[Element]>(repeating: [Element](), count: rows) 17 | 18 | for (index, elt) in enumerated() { 19 | 20 | let newIndex = index / size 21 | groups[newIndex].append(elt) 22 | } 23 | 24 | return groups 25 | } 26 | } 27 | 28 | 29 | extension Array where Element: Equatable { 30 | 31 | func contains(_ other: [Element]) -> Bool { 32 | 33 | for elt in other { 34 | if !contains(elt) { 35 | 36 | return false 37 | } 38 | } 39 | 40 | return true 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /one/Pretty/Extension/CharacterSet+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharacterSet+Extension.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension CharacterSet { 12 | 13 | func contains(_ c: Character) -> Bool { 14 | 15 | let scalars = String(c).unicodeScalars 16 | guard scalars.count == 1 else { 17 | return false 18 | } 19 | return contains(scalars.first!) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /one/Pretty/Extension/Color+Difference.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Difference.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if os(iOS) || os(watchOS) || os(tvOS) 12 | import UIKit 13 | public typealias OCTColor = UIColor 14 | #else 15 | import Cocoa 16 | public typealias OCTColor = NSColor 17 | #endif 18 | 19 | import GLKit 20 | 21 | extension OCTColor { 22 | 23 | 24 | /// 计算与另一个颜色的差异 25 | /// 26 | /// - Parameter other: NSColor Or UIColor 27 | /// - Returns: CIE2000 颜色差异值 28 | func colorDifference(_ other: OCTColor) -> Float { 29 | 30 | let red = UnsafeMutablePointer.allocate(capacity: 1) 31 | let green = UnsafeMutablePointer.allocate(capacity: 1) 32 | let blue = UnsafeMutablePointer.allocate(capacity: 1) 33 | let alpha = UnsafeMutablePointer.allocate(capacity: 1) 34 | 35 | var color = usingColorSpace(.deviceRGB)! 36 | color.getRed(red, green: green, blue: blue, alpha: alpha) 37 | let lab1 = GLKVector3Make(Float(red[0]), Float(green[0]), Float(blue[0])) 38 | 39 | color = other.usingColorSpace(.deviceRGB)! 40 | color.getRed(red, green: green, blue: blue, alpha: alpha) 41 | let lab2 = GLKVector3Make(Float(red[0]), Float(green[0]), Float(blue[0])) 42 | 43 | return CIE2000SquaredColorDifference(1, kC: 1, kH: 1)(lab1, lab2) 44 | } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /one/Pretty/Extension/NSPoint+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSPoint+Extension.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension NSPoint { 13 | 14 | func minus(_ other: NSPoint) -> NSPoint { 15 | 16 | return NSPoint(x: x - other.x, y: y - other.y) 17 | } 18 | 19 | func offset(_ p: NSPoint) -> NSPoint { 20 | 21 | return NSPoint(x: x + p.x, y: y + p.y) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /one/Pretty/Extension/NSRect+Dependency.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSRect+Dependency.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension NSRect { 13 | 14 | func offset(_ p: NSPoint) -> NSRect { 15 | 16 | return NSRect(origin: origin.offset(p), size: size) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /one/Pretty/Extension/PrettyRelation+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrettyRelation+Extension.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | private let kRelationHorizentalSpacing = 30 12 | private let kRelationVerticalSpacing = 100 13 | private let kRelationItemHeight = 28 14 | private let kRelationItemPerRow = 6 15 | private let kRelationViewPadding = 40 16 | 17 | private func widthForItem(_ text: String) -> Int { 18 | 19 | return 10 + 9 * text.count 20 | } 21 | 22 | 23 | private func preferredSize(_ groups: [[String: [String]]]) -> NSSize { 24 | 25 | let rows = groups.count 26 | let height = (kRelationItemHeight + kRelationVerticalSpacing) * rows + kRelationViewPadding * 2 27 | let width = groups.map { elt in 28 | return elt.keys.reduce(2 * kRelationViewPadding) { 29 | $0 + 10 + $1.count * 9 + kRelationHorizentalSpacing 30 | } 31 | }.reduce(0, max) 32 | 33 | return NSSize(width: width, height: height) 34 | } 35 | 36 | private func nodesForGroups(_ groups: [[String: [String]]]) -> [DependencyNode] { 37 | 38 | var nodes = [DependencyNode]() 39 | 40 | for (row, g) in groups.enumerated() { 41 | 42 | var x = kRelationViewPadding 43 | let y = kRelationViewPadding + row * (kRelationItemHeight + kRelationVerticalSpacing) 44 | for elt in g { 45 | 46 | let width = widthForItem(elt.key) 47 | let color = randomColor() 48 | let node = DependencyNode(name: elt.key, 49 | color: color.hex, 50 | frame: NodeFrame(x: x, y: y, width: width, height: kRelationItemHeight), 51 | sons: elt.value) 52 | nodes.append(node) 53 | x += width + kRelationHorizentalSpacing 54 | } 55 | } 56 | 57 | return nodes 58 | } 59 | 60 | extension PrettyRelation { 61 | 62 | convenience init(dependency: [String: [String]]) { 63 | 64 | let groups = Array(groupPodDependency(dependency).reversed()) 65 | // let size = preferredSize(groups) 66 | self.init(nodes: nodesForGroups(groups)) 67 | } 68 | } 69 | 70 | 71 | extension PrettyRelation { 72 | 73 | var preferredSize: CGSize { 74 | 75 | let (width, height) = nodes.reduce((0, 0)) { (result, node) in 76 | 77 | return (max(result.0, node.frame.x + node.frame.width), 78 | max(result.1, node.frame.y + node.frame.height)) 79 | } 80 | return CGSize(width: width + kRelationViewPadding, height: height + kRelationViewPadding) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /one/Pretty/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeExtensions 11 | 12 | lock 13 | 14 | CFBundleTypeName 15 | Podfile 16 | CFBundleTypeRole 17 | Viewer 18 | NSDocumentClass 19 | NSDocument 20 | 21 | 22 | CFBundleTypeExtensions 23 | 24 | tree 25 | 26 | CFBundleTypeIconFile 27 | 28 | CFBundleTypeName 29 | pretty 30 | CFBundleTypeRole 31 | Editor 32 | NSDocumentClass 33 | NSDocument 34 | 35 | 36 | CFBundleExecutable 37 | $(EXECUTABLE_NAME) 38 | CFBundleIconFile 39 | 40 | CFBundleIdentifier 41 | $(PRODUCT_BUNDLE_IDENTIFIER) 42 | CFBundleInfoDictionaryVersion 43 | 6.0 44 | CFBundleName 45 | $(PRODUCT_NAME) 46 | CFBundlePackageType 47 | APPL 48 | CFBundleShortVersionString 49 | 1.0 50 | CFBundleVersion 51 | 1 52 | LSMinimumSystemVersion 53 | $(MACOSX_DEPLOYMENT_TARGET) 54 | NSHumanReadableCopyright 55 | Copyright © 2018年 Octree. All rights reserved. 56 | NSMainStoryboardFile 57 | Main 58 | NSPrincipalClass 59 | NSApplication 60 | 61 | 62 | -------------------------------------------------------------------------------- /one/Pretty/Model/DependencyNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DependencyNode.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | struct NodeFrame: Codable { 13 | 14 | var x: Int 15 | var y: Int 16 | var width: Int 17 | var height: Int 18 | } 19 | 20 | extension NodeFrame { 21 | 22 | var rect: NSRect { 23 | NSRect(x: x, y: y, width: width, height: height) 24 | } 25 | } 26 | 27 | class DependencyNode: Codable { 28 | 29 | var name: String 30 | var color: String 31 | var frame: NodeFrame 32 | var sons: [String] 33 | 34 | init(name: String, color: String, frame: NodeFrame, sons: [String]) { 35 | 36 | self.name = name 37 | self.color = color 38 | self.frame = frame 39 | self.sons = sons 40 | } 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /one/Pretty/Model/PrettyRelation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepettyRelation.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class PrettyRelation: Codable { 12 | 13 | var nodes: [DependencyNode] 14 | 15 | init(nodes: [DependencyNode]) { 16 | 17 | self.nodes = nodes 18 | } 19 | } 20 | 21 | extension PrettyRelation { 22 | 23 | func jsonData() throws -> Data { 24 | 25 | let encoder = JSONEncoder() 26 | do { 27 | return try encoder.encode(self) 28 | } catch { 29 | throw error 30 | } 31 | } 32 | 33 | func jsonString() throws -> String { 34 | 35 | do { 36 | return try String(data: jsonData(), encoding: .utf8)! 37 | } catch { 38 | throw error 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /one/Pretty/Parser/Parser+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser+Extension.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension Parser { 13 | 14 | var many: Parser<[Result]> { 15 | Parser<[Result]> { 16 | input in 17 | var result = [Result]() 18 | var remainder = input 19 | while let (element, newRemainder) = self.parseX(remainder) { 20 | result.append(element) 21 | remainder = newRemainder 22 | } 23 | return (result, remainder) 24 | } 25 | } 26 | 27 | // 啥叫,脱裤子放屁 28 | 29 | 30 | func convert(_ transform: @escaping (Result) -> T) -> Parser { 31 | 32 | return Parser { 33 | input in 34 | guard let (result, remainder) = self.parseX(input) else { 35 | return nil 36 | } 37 | return (transform(result), remainder) 38 | } 39 | } 40 | 41 | 42 | // 处理下一步 43 | func followed(by other: Parser) -> Parser<(Result, A)> { 44 | return Parser<(Result, A)> { 45 | input in 46 | // 先这一步 self parse,再下一步 other parse 47 | guard let (first, reminder) = self.parseX(input), 48 | let (second, newReminder) = other.parseX(reminder) else { 49 | return nil 50 | } 51 | return ((first, second), newReminder) 52 | } 53 | } 54 | 55 | var optional: Parser { 56 | Parser { 57 | input in 58 | guard let (result, remainder) = self.parseX(input) else { 59 | return (nil, input) 60 | } 61 | return (result, remainder) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /one/Pretty/Parser/Parser+Runes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser+Runes.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Ignoring Left 12 | /// ma -> mb -> mb 13 | /// 14 | /// - Parameters: 15 | /// - lhs: m a 16 | /// - rhs: m b 17 | /// - Returns: m b 18 | 19 | 20 | // 处理右边 21 | func *>(lhs: Parser, rhs: Parser) -> Parser { 22 | return Parser{ 23 | input in 24 | // 先这一步 self parse,再下一步 other parse 25 | guard let (_, reminder) = lhs.parseX(input), 26 | let (second, newReminder) = rhs.parseX(reminder) else { 27 | return nil 28 | } 29 | return (second, newReminder) 30 | } 31 | 32 | } 33 | 34 | /// Ignoring Right 35 | /// ma -> mb -> ma 36 | /// 37 | /// - Parameters: 38 | /// - lhs: m a 39 | /// - rhs: m b 40 | /// - Returns: m a 41 | 42 | 43 | // 处理左边 44 | 45 | // 展开可以省略流程 46 | func <*(lhs: Parser, rhs: Parser) -> Parser { 47 | return Parser<(A)>{ 48 | input in 49 | guard let (first, reminder) = lhs.parseX(input), 50 | let (_, newReminder) = rhs.parseX(reminder) else { 51 | return nil 52 | } 53 | return (first, newReminder) 54 | } 55 | } 56 | // 不能确定类型,是因为不给足信息,可以存在多种解释 57 | 58 | 59 | // 这个方法,非常的神奇 60 | 61 | 62 | // 从链式编程, 到函数式,到操作符, 63 | 64 | 65 | // 代码风格为不直观, 开劝退 66 | 67 | 68 | 69 | // 写代码的人,有才 70 | -------------------------------------------------------------------------------- /one/Pretty/Parser/Parser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Parser { 12 | // result 分为两种, 13 | // String / Character 14 | 15 | // (结果, 中间数据) 16 | let parseX: (Substring) -> (Result, Substring)? 17 | } 18 | 19 | -------------------------------------------------------------------------------- /one/Pretty/Parser/PodLockFileParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PodLockFileParser.swift 3 | // 解析 Podfile.lock 的 PODS: 字段,获取 pod 间的依赖关系 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// 三种基础的文本处理方案 12 | /// 13 | /// 14 | /// Just parse one character 15 | /// 16 | /// - Parameter condition: condition 17 | /// - Returns: Parser 18 | func parse(charMatching condition: @escaping (Character) -> Bool) -> Parser{ 19 | return Parser(parseX: { input in 20 | guard let char = input.first, condition(char) else { 21 | return nil 22 | } 23 | return (char, input.dropFirst()) 24 | }) 25 | } 26 | 27 | 28 | /// parse one specific character 29 | /// 30 | /// - Parameter ch: character 31 | /// - Returns: Parser 32 | func handle(character ch: Character) -> Parser { 33 | 34 | return parse { 35 | $0 == ch 36 | } 37 | } 38 | 39 | 40 | /// Parse Specific String 41 | /// 42 | /// - Parameter string: expected string 43 | /// - Returns: Parser 44 | func handle(string: String) -> Parser { 45 | return Parser { input in 46 | guard input.hasPrefix(string) else { 47 | return nil 48 | } 49 | return (string, input.dropFirst(string.count)) 50 | } 51 | } 52 | 53 | /// 冒号 54 | private let colon = parse { $0 == ":" } 55 | 56 | /// 空格 57 | private let space = handle(character: " ") 58 | 59 | /// 换行 60 | private let newLine = handle(character: "\n") 61 | 62 | /// 缩进 63 | private let indentation = space.followed(by: space) 64 | 65 | /// - 66 | private let hyphon = handle(character: "-") 67 | private let quote = handle(character: "\"") 68 | 69 | private let leftParent = handle(character: "(") 70 | private let rightParent: Parser = handle(character: ")") 71 | 72 | /// Just Parse `PODS:` 😅 73 | private let podsX: Parser = handle(string: "PODS:\n") 74 | 75 | private let word: Parser = parse { 76 | !CharacterSet.whitespacesAndNewlines.contains($0) }.many.convert{ String($0) } 77 | 78 | 79 | // 这个有意思, 这里直观 80 | /// Parse Version Part: `(= 1.2.2)` or `(1.2.3)` or `(whatever)` 81 | private let version: Parser<((Character, [Character]), Character)> = leftParent.followed(by: parse { $0 != ")" }.many).followed(by: rightParent) 82 | 83 | // 调用的简洁,意味着维护了多余的结构 84 | // 链式编程,操作符返回 self 85 | 86 | 87 | // 严格契合,也没匹配多个空格 / 多个 ... 88 | private let item: Parser = (indentation *> hyphon *> space *> quote.optional *> word) 89 | <* (space.followed(by: version)).optional <* quote.optional <* colon.optional <* newLine 90 | 91 | private let subItem: Parser = indentation *> item 92 | // 很有意思的, 链式调用 93 | 94 | // 定义数据处理的逻辑单元, 函数式编程 95 | private let dependencyItem: Parser = Parser { 96 | input in 97 | guard let (result, remainder) = item.parseX(input) else { 98 | return nil 99 | } 100 | return (result, remainder) 101 | }.followed(by: subItem.many.optional).convert{ ($0, $1 ?? []) } 102 | 103 | 104 | 105 | private let dependencyItems: Parser = { 106 | Parser { 107 | input in 108 | guard let (result, remainder) = dependencyItem.many.parseX(input) else { 109 | return nil 110 | } 111 | var map = ResultFmt() 112 | result.forEach { map[$0.0] = $0.1 } 113 | return (map, remainder) 114 | } 115 | }() 116 | 117 | 118 | typealias MidFmt = (String, [String]) 119 | typealias ResultFmt = [String: [String]] 120 | 121 | /// 解析 Podfile.lock 122 | /// 解析成功会返回 [String: [String]] 123 | /// key: Pod Name 124 | /// value: 该 Pod 依赖的其他 Pods 125 | let PodLockFileParser = Parser{ 126 | input in 127 | // 先这一步 self parse,再下一步 other parse 128 | guard let (_, reminder) = podsX.parseX(input), 129 | let (second, newReminder) = dependencyItems.parseX(reminder) else { 130 | return nil 131 | } 132 | return (second, newReminder) 133 | } 134 | 135 | -------------------------------------------------------------------------------- /one/Pretty/Parser/Runes.swift: -------------------------------------------------------------------------------- 1 | 2 | precedencegroup RunesApplicativeSequencePrecedence { 3 | associativity: left 4 | higherThan: LogicalConjunctionPrecedence 5 | lowerThan: NilCoalescingPrecedence 6 | } 7 | 8 | 9 | /** 10 | sequence actions, discarding right (value of the second argument) 11 | Expected function type: `f a -> f b -> f a` 12 | Haskell `infixl 4` 13 | */ 14 | infix operator <* : RunesApplicativeSequencePrecedence 15 | 16 | /** 17 | sequence actions, discarding left (value of the first argument) 18 | Expected function type: `f a -> f b -> f b` 19 | Haskell `infixl 4` 20 | */ 21 | infix operator *> : RunesApplicativeSequencePrecedence 22 | 23 | /** 24 | map a function over a value with context and flatten the result 25 | Expected function type: `(a -> m b) -> m a -> m b` 26 | Haskell `infixr 1` 27 | */ 28 | -------------------------------------------------------------------------------- /one/Pretty/Pretty.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.assets.movies.read-write 8 | 9 | com.apple.security.assets.music.read-write 10 | 11 | com.apple.security.assets.pictures.read-write 12 | 13 | com.apple.security.files.downloads.read-write 14 | 15 | com.apple.security.files.user-selected.read-write 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /one/Pretty/RandomColors/ColorDefinition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorDefinition.swift 3 | // RandomColorSwift 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import Foundation 26 | 27 | typealias Range = (min: Int, max: Int) 28 | 29 | struct ColorDefinition { 30 | let hueRange: Range? 31 | let lowerBounds: [Range] 32 | 33 | lazy var saturationRange: Range = { 34 | let sMin = self.lowerBounds[0].0 35 | let sMax = self.lowerBounds[self.lowerBounds.count - 1].0 36 | return (sMin, sMax) 37 | }() 38 | 39 | lazy var brightnessRange: Range = { 40 | let bMin = self.lowerBounds[self.lowerBounds.count - 1].1 41 | let bMax = self.lowerBounds[0].1 42 | return (bMin, bMax) 43 | }() 44 | 45 | init(hueRange: Range?, lowerBounds: [Range]) { 46 | self.hueRange = hueRange 47 | self.lowerBounds = lowerBounds 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /one/Pretty/RandomColors/Hue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Hue.swift 3 | // RandomColorSwift 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public enum Hue { 28 | 29 | case monochrome, red, orange, yellow, green, blue, purple, pink 30 | case value(Int) 31 | case random 32 | 33 | public func toInt() -> Int { 34 | switch self { 35 | case .monochrome: return 1 36 | case .red: return 2 37 | case .orange: return 3 38 | case .yellow: return 4 39 | case .green: return 5 40 | case .blue: return 6 41 | case .purple: return 7 42 | case .pink: return 8 43 | case .value(_): return -1 44 | case .random: return 0 45 | } 46 | } 47 | } 48 | 49 | public func == (lhs: Hue, rhs: Hue) -> Bool { 50 | return lhs.toInt() == rhs.toInt() 51 | } 52 | 53 | extension Hue: Hashable { 54 | public var hashValue: Int { 55 | get { 56 | return self.toInt() 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /one/Pretty/RandomColors/Luminosity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Luminosity.swift 3 | // RandomColorSwift 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public enum Luminosity: Int { 28 | case bright, light, dark 29 | case random 30 | } 31 | -------------------------------------------------------------------------------- /one/Pretty/RandomColors/RandomColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RandomColor.h 3 | // RandomColor 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | #import 26 | 27 | //! Project version number for RandomColor. 28 | FOUNDATION_EXPORT double RandomColorVersionNumber; 29 | 30 | //! Project version string for RandomColor. 31 | FOUNDATION_EXPORT const unsigned char RandomColorVersionString[]; 32 | 33 | // In this header, you should import all the public headers of your framework using statements like #import 34 | 35 | 36 | -------------------------------------------------------------------------------- /one/Pretty/RandomColors/RandomColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RandomColor.swift 3 | // RandomColorSwift 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | #if os(iOS) 26 | import UIKit 27 | public typealias Color = UIColor 28 | #else 29 | import Cocoa 30 | public typealias Color = NSColor 31 | #endif 32 | 33 | private var colorDictionary: [Hue: ColorDefinition] = [ 34 | .monochrome: ColorDefinition(hueRange: nil, lowerBounds: [(0,0), (100,0)]), 35 | .red: ColorDefinition(hueRange: (-26,18), lowerBounds: [(20,100), (30,92), (40,89), (50,85), (60,78), (70,70), (80,60), (90,55), (100,50)]), 36 | .orange: ColorDefinition(hueRange: (19,46), lowerBounds: [(20,100), (30,93), (40,88), (50,86), (60,85), (70,70), (100,70)]), 37 | .yellow: ColorDefinition(hueRange: (47,62), lowerBounds: [(25,100), (40,94), (50,89), (60,86), (70,84), (80,82), (90,80), (100,75)]), 38 | .green: ColorDefinition(hueRange: (63,178), lowerBounds: [(30,100), (40,90), (50,85), (60,81), (70,74), (80,64), (90,50), (100,40)]), 39 | .blue: ColorDefinition(hueRange: (179,257), lowerBounds: [(20,100), (30,86), (40,80), (50,74), (60,60), (70,52), (80,44), (90,39), (100,35)]), 40 | .purple: ColorDefinition(hueRange: (258, 282), lowerBounds: [(20,100), (30,87), (40,79), (50,70), (60,65), (70,59), (80,52), (90,45), (100,42)]), 41 | .pink: ColorDefinition(hueRange: (283, 334), lowerBounds: [(20,100), (30,90), (40,86), (60,84), (80,80), (90,75), (100,73)]) 42 | ] 43 | 44 | extension Hue { 45 | var range: Range { 46 | switch self { 47 | case .value(let value): return (value, value) 48 | case .random: return (0, 360) 49 | default: 50 | if let colorDefinition = colorDictionary[self] { 51 | return colorDefinition.hueRange ?? (0, 360) 52 | } else { 53 | assert(false, "Unrecgonized Hue enum: \(self).") 54 | return (0, 360) 55 | } 56 | } 57 | } 58 | } 59 | 60 | /** 61 | Generate a single random color with some conditions. 62 | 63 | - parameter hue: Hue of target color. It will be the main property of the generated color. Default is .Random. 64 | - parameter luminosity: Luminosity of target color. It will decide the brightness of generated color. Default is .Random. 65 | 66 | - returns: A random color following input conditions. It will be a `UIColor` object for iOS target, and an `NSColor` object for OSX target. 67 | */ 68 | public func randomColor(hue: Hue = .random, luminosity: Luminosity = .random) -> Color { 69 | 70 | func random(in range: Range) -> Int { 71 | assert(range.max >= range.min, "Max in range should be greater than min") 72 | return Int(arc4random_uniform(UInt32(range.max - range.min))) + range.min 73 | } 74 | 75 | func getColorDefinition(hueValue: Int) -> ColorDefinition { 76 | var hueValue = hueValue 77 | 78 | if hueValue >= 334 && hueValue <= 360 { 79 | hueValue -= 360 80 | } 81 | 82 | let color = colorDictionary.values.filter({ (definition: ColorDefinition) -> Bool in 83 | if let hueRange = definition.hueRange { 84 | return hueValue >= hueRange.min && hueValue <= hueRange.max 85 | } else { 86 | return false 87 | } 88 | }) 89 | 90 | assert(color.count == 1, "There should one and only one color satisfied the filter") 91 | return color.first! 92 | } 93 | 94 | func pickHue(_ hue: Hue) -> Int { 95 | var hueValue = random(in: hue.range) 96 | 97 | // Instead of storing red as two seperate ranges, 98 | // we group them, using negative numbers 99 | if hueValue < 0 { 100 | hueValue = hueValue + 360 101 | } 102 | return hueValue 103 | } 104 | 105 | func pickSaturation(color: ColorDefinition, hue: Hue, luminosity: Luminosity) -> Int { 106 | var color = color 107 | 108 | if luminosity == .random { 109 | return random(in: (0, 100)) 110 | } 111 | 112 | if hue == .monochrome { 113 | return 0 114 | } 115 | 116 | let saturationRange = color.saturationRange 117 | var sMin = saturationRange.min 118 | var sMax = saturationRange.max 119 | 120 | switch luminosity { 121 | case .bright: 122 | sMin = 55 123 | case .dark: 124 | sMin = sMax - 10 125 | case .light: 126 | sMax = 55 127 | default: () 128 | } 129 | 130 | return random(in: (sMin, sMax)) 131 | } 132 | 133 | func pickBrightness(color: ColorDefinition, saturationValue: Int, luminosity: Luminosity) -> Int { 134 | func getMinimumBrightness(saturationValue: Int) -> Int { 135 | let lowerBounds = color.lowerBounds; 136 | for i in 0 ..< lowerBounds.count - 1 { 137 | 138 | let s1 = Float(lowerBounds[i].0) 139 | let v1 = Float(lowerBounds[i].1) 140 | 141 | let s2 = Float(lowerBounds[i+1].0) 142 | let v2 = Float(lowerBounds[i+1].1) 143 | 144 | if Float(saturationValue) >= s1 && Float(saturationValue) <= s2 { 145 | let m = (v2 - v1) / (s2 - s1) 146 | let b = v1 - m * s1 147 | return lroundf(m * Float(saturationValue) + b) 148 | } 149 | } 150 | return 0 151 | } 152 | 153 | var bMin = getMinimumBrightness(saturationValue: saturationValue) 154 | var bMax = 100 155 | 156 | switch luminosity { 157 | case .dark: 158 | bMax = bMin + 20 159 | case .light: 160 | bMin = (bMax + bMin) / 2 161 | case .random: 162 | bMin = 0 163 | bMax = 100 164 | default: () 165 | } 166 | 167 | return random(in: (bMin, bMax)) 168 | } 169 | 170 | 171 | let hueValue = pickHue(hue) 172 | 173 | let color = getColorDefinition(hueValue: hueValue) 174 | 175 | let saturationValue = pickSaturation(color: color, hue: hue, luminosity: luminosity) 176 | let brightnessValue = pickBrightness(color: color, saturationValue: saturationValue, luminosity: luminosity) 177 | 178 | return Color(deviceHue: CGFloat(hueValue) / 360.0, 179 | saturation: CGFloat(saturationValue) / 100.0, 180 | brightness: CGFloat(brightnessValue) / 100.0, 181 | alpha: 1.0) 182 | } 183 | 184 | /** 185 | Generate a set of random colors with some conditions. 186 | 187 | - parameter count: The count of how many colors will be generated. 188 | - parameter hue: Hue of target color. It will be the main property of the generated color. Default is .Random. 189 | - parameter luminosity: Luminosity of target color. It will decide the brightness of generated color. Default is .Random. 190 | 191 | - returns: An array of random colors following input conditions. The elements will be `UIColor` objects for iOS target, and `NSColor` objects for OSX target. 192 | */ 193 | public func randomColors(count: Int, hue: Hue = .random, luminosity: Luminosity = .random) -> [Color] { 194 | var colors: [Color] = [] 195 | while (colors.count < count) { 196 | colors.append(randomColor(hue: hue, luminosity: luminosity)) 197 | } 198 | return colors 199 | } 200 | 201 | -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/one/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /one/Pretty/Resource/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /one/Pretty/Utils/ColorDifference.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorDifference.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import GLKit 10 | 11 | extension GLKVector3 { 12 | 13 | func unpack() -> (Float, Float, Float) { 14 | 15 | return (x, y, z) 16 | } 17 | } 18 | 19 | private func C(_ a: Float, b: Float) -> Float { 20 | return sqrt(pow(a, 2) + pow(b, 2)) 21 | } 22 | 23 | // From http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html 24 | public func CIE2000SquaredColorDifference( 25 | _ kL: Float = 1, 26 | kC: Float = 1, 27 | kH: Float = 1 28 | ) -> (_ lab1:GLKVector3, _ lab2:GLKVector3) -> Float { 29 | 30 | return { (lab1:GLKVector3, lab2:GLKVector3) -> Float in 31 | let (L1, a1, b1) = lab1.unpack() 32 | let (L2, a2, b2) = lab2.unpack() 33 | 34 | let ΔLp = L2 - L1 35 | let Lbp = (L1 + L2) / 2 36 | 37 | let (C1, C2) = (C(a1, b: b1), C(a2, b: b2)) 38 | let Cb = (C1 + C2) / 2 39 | 40 | let G = (1 - sqrt(pow(Cb, 7) / (pow(Cb, 7) + pow(25, 7)))) / 2 41 | let ap: (Float) -> Float = { a in 42 | return a * (1 + G) 43 | } 44 | let (a1p, a2p) = (ap(a1), ap(a2)) 45 | 46 | let (C1p, C2p) = (C(a1p, b: b1), C(a2p, b: b2)) 47 | let ΔCp = C2p - C1p 48 | let Cbp = (C1p + C2p) / 2 49 | 50 | let hp: (Float, Float) -> Float = { ap, b in 51 | if ap == 0 && b == 0 { return 0 } 52 | let θ = GLKMathRadiansToDegrees(atan2(b, ap)) 53 | return fmod(θ < 0 ? (θ + 360) : θ, 360) 54 | } 55 | let (h1p, h2p) = (hp(a1p, b1), hp(a2p, b2)) 56 | let Δhabs = abs(h1p - h2p) 57 | let Δhp: Float = { 58 | if (C1p == 0 || C2p == 0) { 59 | return 0 60 | } else if Δhabs <= 180 { 61 | return h2p - h1p 62 | } else if h2p <= h1p { 63 | return h2p - h1p + 360 64 | } else { 65 | return h2p - h1p - 360 66 | } 67 | }() 68 | 69 | let ΔHp = 2 * sqrt(C1p * C2p) * sin(GLKMathDegreesToRadians(Δhp / 2)) 70 | let Hbp: Float = { 71 | if (C1p == 0 || C2p == 0) { 72 | return h1p + h2p 73 | } else if Δhabs > 180 { 74 | return (h1p + h2p + 360) / 2 75 | } else { 76 | return (h1p + h2p) / 2 77 | } 78 | }() 79 | 80 | var T = 1 81 | - 0.17 * cos(GLKMathDegreesToRadians(Hbp - 30)) 82 | + 0.24 * cos(GLKMathDegreesToRadians(2 * Hbp)) 83 | 84 | T = T 85 | + 0.32 * cos(GLKMathDegreesToRadians(3 * Hbp + 6)) 86 | - 0.20 * cos(GLKMathDegreesToRadians(4 * Hbp - 63)) 87 | 88 | let Sl = 1 + (0.015 * pow(Lbp - 50, 2)) / sqrt(20 + pow(Lbp - 50, 2)) 89 | let Sc = 1 + 0.045 * Cbp 90 | let Sh = 1 + 0.015 * Cbp * T 91 | 92 | let Δθ = 30 * exp(-pow((Hbp - 275) / 25, 2)) 93 | let Rc = 2 * sqrt(pow(Cbp, 7) / (pow(Cbp, 7) + pow(25, 7))) 94 | let Rt = -Rc * sin(GLKMathDegreesToRadians(2 * Δθ)) 95 | 96 | let Lterm = ΔLp / (kL * Sl) 97 | let Cterm = ΔCp / (kC * Sc) 98 | let Hterm = ΔHp / (kH * Sh) 99 | return pow(Lterm, 2) + pow(Cterm, 2) + pow(Hterm, 2) + Rt * Cterm * Hterm 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /one/Pretty/Utils/ColorSpaceConversion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorSpaceConversion.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import GLKit 10 | 11 | // From https://github.com/indragiek/DominantColor 12 | 13 | 14 | func RGBToSRGB(_ rgbVector: GLKVector3) -> GLKVector3 { 15 | #if TARGET_OS_IPHONE 16 | // sRGB is the native device color space on iOS, no conversion is required. 17 | return rgbVector 18 | #else 19 | let color = NSColor(deviceRed: CGFloat(rgbVector.x), green: CGFloat(rgbVector.y), blue: CGFloat(rgbVector.z), alpha: 1.0) 20 | let srgbColor = color.usingColorSpace(.sRGB) 21 | return GLKVector3Make(Float(srgbColor?.redComponent ?? 0), 22 | Float(srgbColor?.greenComponent ?? 0), 23 | Float(srgbColor?.blueComponent ?? 0)) 24 | #endif 25 | } 26 | 27 | func SRGBToRGB(_ srgbVector: GLKVector3) -> GLKVector3 { 28 | #if TARGET_OS_IPHONE 29 | // sRGB is the native device color space on iOS, no conversion is required. 30 | return srgbVector 31 | #else 32 | let components = [ CGFloat(srgbVector.x), CGFloat(srgbVector.y), CGFloat(srgbVector.z), 1.0 ] 33 | let srgbColor = NSColor(colorSpace: .sRGB, components: components, count: 4) 34 | let rgbColor = srgbColor.usingColorSpace(.deviceRGB) 35 | return GLKVector3Make(Float(rgbColor?.redComponent ?? 0), 36 | Float(rgbColor?.greenComponent ?? 0), 37 | Float(rgbColor?.blueComponent ?? 0)) 38 | #endif 39 | } 40 | 41 | //// http://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation 42 | // 43 | func SRGBToLinearSRGB(_ srgbVector: GLKVector3) -> GLKVector3 { 44 | 45 | let f: (Float) -> Float = { c in 46 | if (c <= 0.04045) { 47 | return c / 12.92 48 | } else { 49 | return powf((c + 0.055) / 1.055, 2.4) 50 | } 51 | } 52 | return GLKVector3Make(f(srgbVector.x), f(srgbVector.y), f(srgbVector.z)) 53 | } 54 | 55 | func LinearSRGBToSRGB(_ lSrgbVector: GLKVector3) -> GLKVector3 { 56 | 57 | let f: (Float) -> Float = { c in 58 | if (c <= 0.0031308) { 59 | return c * 12.92 60 | } else { 61 | return (1.055 * powf(c, 1.0 / 2.4)) - 0.055 62 | } 63 | } 64 | 65 | return GLKVector3Make(f(lSrgbVector.x), f(lSrgbVector.y), f(lSrgbVector.z)) 66 | } 67 | 68 | // mark: XYZ (CIE 1931) 69 | //// http://en.wikipedia.org/wiki/CIE_1931_color_space#Construction_of_the_CIE_XYZ_color_space_from_the_Wright.E2.80.93Guild_data 70 | 71 | private let LinearSRGBToXYZMatrix = GLKMatrix3Make(0.4124, 0.2126, 0.0193, 0.3576, 0.7152, 0.1192, 0.1805, 0.0722, 0.9505) 72 | 73 | func LinearSRGBToXYZ(_ linearSrgbVector: GLKVector3) -> GLKVector3 { 74 | 75 | let unscaledXYZVector = GLKMatrix3MultiplyVector3(LinearSRGBToXYZMatrix, linearSrgbVector) 76 | return GLKVector3MultiplyScalar(unscaledXYZVector, 100) 77 | } 78 | 79 | private let XYZToLinearSRGBMatrix = GLKMatrix3Make(3.2406, -0.9689, 0.0557, -1.5372, 1.8758, -0.2040, -0.4986, 0.0415, 1.0570) 80 | 81 | func XYZToLinearSRGB(_ xyzVector: GLKVector3) -> GLKVector3 { 82 | 83 | let scaledXYZVector = GLKVector3DivideScalar(xyzVector, 100) 84 | return GLKMatrix3MultiplyVector3(XYZToLinearSRGBMatrix, scaledXYZVector) 85 | } 86 | 87 | // MARK: LAB 88 | //// http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions 89 | 90 | func XYZToLAB(_ xyzVector: GLKVector3, tristimulus: GLKVector3) -> GLKVector3 { 91 | 92 | let f: (Float) -> Float = { t in 93 | 94 | if (t > powf(6 / 29, 3)) { 95 | return powf(t, 1 / 3) 96 | } else { 97 | return ((1 / 3) * powf(29 / 6, 2) * t) + (4 / 29) 98 | } 99 | } 100 | let fx = f(xyzVector.x / tristimulus.x) 101 | let fy = f(xyzVector.y / tristimulus.y) 102 | let fz = f(xyzVector.z / tristimulus.z) 103 | 104 | let l = (116 * fy) - 16 105 | let a = 500 * (fx - fy) 106 | let b = 200 * (fy - fz) 107 | 108 | return GLKVector3Make(l, a, b) 109 | } 110 | 111 | func LABToXYZ(_ labVector: GLKVector3, tristimulus: GLKVector3) -> GLKVector3 { 112 | let f: (Float) -> Float = { t in 113 | if (t > (6 / 29)) { 114 | return powf(t, 3) 115 | } else { 116 | return 3 * powf(6 / 29, 2) * (t - (4 / 29)) 117 | } 118 | } 119 | let c = (1 / 116) * (labVector.x + 16) 120 | 121 | let y = tristimulus.y * f(c) 122 | let x = tristimulus.x * f(c + ((1 / 500) * labVector.y)) 123 | let z = tristimulus.z * f(c - ((1 / 200) * labVector.z)) 124 | 125 | return GLKVector3Make(x, y, z) 126 | } 127 | 128 | //#pragma mark - Public 129 | // 130 | //// From http://www.easyrgb.com/index.php?X=MATH&H=15#text15 131 | let D65Tristimulus = GLKVector3Make(95.047, 100, 108.883) 132 | 133 | func RGBToLAB(rgbVector: GLKVector3) -> GLKVector3 { 134 | 135 | let srgbVector = RGBToSRGB(rgbVector) 136 | let lSrgbVector = SRGBToLinearSRGB(srgbVector) 137 | let xyzVector = LinearSRGBToXYZ(lSrgbVector) 138 | return XYZToLAB(xyzVector, tristimulus: D65Tristimulus) 139 | } 140 | 141 | func LABToRGB(labVector: GLKVector3) -> GLKVector3 { 142 | 143 | let xyzVector = LABToXYZ(labVector, tristimulus: D65Tristimulus) 144 | let lSrgbVector = XYZToLinearSRGB(xyzVector) 145 | let srgbVector = LinearSRGBToSRGB(lSrgbVector) 146 | return SRGBToRGB(srgbVector) 147 | } 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /one/Pretty/Utils/DependencySort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DependencySort.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// 12 | /// - Parameter dependency: 依赖 [name: Sons] 13 | /// - Returns: [name: dads] 14 | private func parentNode(_ dependency: [String: [String]]) -> [String: [String]] { 15 | 16 | var pMap = [String: [String]]() 17 | for (key, sons) in dependency { 18 | for name in sons { 19 | var parents = pMap[name] ?? [] 20 | parents.append(key) 21 | pMap[name] = parents 22 | } 23 | } 24 | 25 | return pMap 26 | } 27 | 28 | 29 | /// 根据 lib 的 depth 进行分组 30 | /// 31 | /// - Parameter dependency: pod dependency 32 | /// - Returns: grouped dependency 33 | func groupPodDependency(_ dependency: [String: [String]]) -> [[String: [String]]] { 34 | 35 | let reversed = parentNode(dependency) 36 | var names = Set(dependency.keys) 37 | 38 | var lastDepthNames = [String]() 39 | var groups = [[String: [String]]]() 40 | while names.count > 0 { 41 | 42 | var group = [String: [String]]() 43 | let copyedNames = names 44 | for name in copyedNames { 45 | if lastDepthNames.contains(reversed[name] ?? []) { 46 | 47 | names.remove(name) 48 | group[name] = dependency[name] 49 | } 50 | } 51 | lastDepthNames.append(contentsOf: group.keys) 52 | groups.append(group) 53 | } 54 | 55 | return groups 56 | } 57 | -------------------------------------------------------------------------------- /one/Pretty/Utils/HexColors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HexColors.swift 3 | // 4 | // Created by Marius Landwehr on 25.12.16. 5 | // The MIT License (MIT) 6 | // Copyright (c) 2016 Marius Landwehr marius.landwehr@gmail.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | #if os(iOS) || os(watchOS) || os(tvOS) 15 | import UIKit 16 | public typealias HexColor = UIColor 17 | #else 18 | import Cocoa 19 | public typealias HexColor = NSColor 20 | #endif 21 | 22 | 23 | public extension HexColor { 24 | typealias Hex = String 25 | 26 | convenience init?(_ hex: Hex, alpha: CGFloat? = nil) { 27 | 28 | guard let hexType = Type(from: hex), let components = hexType.components() else { 29 | return nil 30 | } 31 | 32 | self.init(calibratedRed: components.red, green: components.green, blue: components.blue, alpha: alpha ?? components.alpha) 33 | } 34 | 35 | /// The string hex value representation of the current color 36 | var hex: Hex { 37 | var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0, rgb: Int 38 | getRed(&r, green: &g, blue: &b, alpha: &a) 39 | 40 | if a == 1 { // no alpha value set, we are returning the short version 41 | rgb = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0 42 | return String(format: "#%06x", rgb) 43 | } else { 44 | rgb = (Int)(r*255)<<24 | (Int)(g*255)<<16 | (Int)(b*255)<<8 | (Int)(a*255)<<0 45 | return String(format: "#%08x", rgb) 46 | } 47 | } 48 | 49 | private enum `Type` { 50 | 51 | case RGBshort(rgb: Hex) 52 | case RGBshortAlpha(rgba: Hex) 53 | case RGB(rgb: Hex) 54 | case RGBA(rgba: Hex) 55 | 56 | init?(from hex: Hex) { 57 | 58 | var hexString = hex 59 | hexString.removeHashIfNecessary() 60 | 61 | guard let t = Type.transform(hex: hexString) else { 62 | return nil 63 | } 64 | 65 | self = t 66 | } 67 | 68 | static func transform(hex string: Hex) -> Type? { 69 | switch string.count { 70 | case 3: 71 | return .RGBshort(rgb: string) 72 | case 4: 73 | return .RGBshortAlpha(rgba: string) 74 | case 6: 75 | return .RGB(rgb: string) 76 | case 8: 77 | return .RGBA(rgba: string) 78 | default: 79 | return nil 80 | } 81 | } 82 | 83 | var value: Hex { 84 | switch self { 85 | case .RGBshort(let rgb): 86 | return rgb 87 | case .RGBshortAlpha(let rgba): 88 | return rgba 89 | case .RGB(let rgb): 90 | return rgb 91 | case .RGBA(let rgba): 92 | return rgba 93 | } 94 | } 95 | 96 | typealias rgbComponents = (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) 97 | func components() -> rgbComponents? { 98 | 99 | var hexValue: UInt32 = 0 100 | guard Scanner(string: value).scanHexInt32(&hexValue) else { 101 | return nil 102 | } 103 | 104 | let r, g, b, a, divisor: CGFloat 105 | 106 | switch self { 107 | case .RGBshort(_): 108 | divisor = 15 109 | r = CGFloat((hexValue & 0xF00) >> 8) / divisor 110 | g = CGFloat((hexValue & 0x0F0) >> 4) / divisor 111 | b = CGFloat( hexValue & 0x00F) / divisor 112 | a = 1 113 | case .RGBshortAlpha(_): 114 | divisor = 15 115 | r = CGFloat((hexValue & 0xF000) >> 12) / divisor 116 | g = CGFloat((hexValue & 0x0F00) >> 8) / divisor 117 | b = CGFloat((hexValue & 0x00F0) >> 4) / divisor 118 | a = CGFloat( hexValue & 0x000F) / divisor 119 | case .RGB(_): 120 | divisor = 255 121 | r = CGFloat((hexValue & 0xFF0000) >> 16) / divisor 122 | g = CGFloat((hexValue & 0x00FF00) >> 8) / divisor 123 | b = CGFloat( hexValue & 0x0000FF) / divisor 124 | a = 1 125 | case .RGBA(_): 126 | divisor = 255 127 | r = CGFloat((hexValue & 0xFF000000) >> 24) / divisor 128 | g = CGFloat((hexValue & 0x00FF0000) >> 16) / divisor 129 | b = CGFloat((hexValue & 0x0000FF00) >> 8) / divisor 130 | a = CGFloat( hexValue & 0x000000FF) / divisor 131 | } 132 | 133 | return (red: r, green: g, blue: b, alpha: a) 134 | } 135 | } 136 | } 137 | 138 | private extension String { 139 | 140 | mutating func removeHashIfNecessary() { 141 | if hasPrefix("#") { 142 | self = replacingOccurrences(of: "#", with: "") 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /one/Pretty/View/RelationItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelationItemView.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class RelationItemView: NSView { 12 | 13 | var text = "" { 14 | didSet { 15 | label.stringValue = text 16 | } 17 | } 18 | private(set) var label: NSTextField = { 19 | let textfield = NSTextField() 20 | textfield.isEditable = false 21 | textfield.textColor = .white 22 | textfield.alignment = .center 23 | textfield.isBordered = false 24 | return textfield 25 | }() 26 | 27 | var backgroundColor: NSColor? { 28 | didSet { 29 | layer?.backgroundColor = backgroundColor?.cgColor 30 | label.backgroundColor = backgroundColor 31 | } 32 | } 33 | 34 | init(frame frameRect: NSRect, text: String) { 35 | super.init(frame: frameRect) 36 | self.text = text 37 | addSubview(label) 38 | label.stringValue = text 39 | wantsLayer = true 40 | layer?.cornerRadius = 10 41 | layer?.masksToBounds = true 42 | } 43 | 44 | required init?(coder decoder: NSCoder) { 45 | fatalError("init(coder:) has not been implemented") 46 | } 47 | 48 | override func draw(_ dirtyRect: NSRect) { 49 | super.draw(dirtyRect) 50 | } 51 | 52 | override func layout() { 53 | super.layout() 54 | label.frame = bounds.insetBy(dx: 5, dy: 5) 55 | } 56 | 57 | var center: NSPoint { 58 | NSPoint(x: frame.origin.x + frame.size.width / 2, 59 | y: frame.origin.y + frame.size.height / 2) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /one/Pretty/View/RelationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelationView.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | class RelationView: NSView { 13 | 14 | // MARK: Accessor 15 | 16 | /// "parent|daughter": Layer 17 | private var lineMap = [String: CAShapeLayer]() 18 | private var itemMap = [String: RelationItemView]() 19 | private var nodeMap = [String: DependencyNode]() 20 | private var currentDraggingItem: RelationItemView? = nil 21 | 22 | private var lastDragPosition = NSPoint(x: 0, y: 0) 23 | 24 | var prettyRelation = PrettyRelation(nodes: []) { 25 | didSet { 26 | setUp() 27 | } 28 | } 29 | 30 | // MARK: Life Cycle 31 | 32 | init() { 33 | super.init(frame:.zero) 34 | } 35 | 36 | required init?(coder decoder: NSCoder) { 37 | super.init(coder: decoder) 38 | } 39 | 40 | 41 | // MARK: Public Method 42 | 43 | override func mouseDown(with event: NSEvent) { 44 | 45 | let position = convert(event.locationInWindow, from: nil) 46 | 47 | if let item = findItemLocate(in: position) { 48 | 49 | currentDraggingItem = item 50 | lastDragPosition = position 51 | } 52 | } 53 | 54 | override func mouseDragged(with event: NSEvent) { 55 | 56 | if let item = currentDraggingItem { 57 | 58 | let position = convert(event.locationInWindow, from: nil) 59 | item.frame = item.frame.offset(position.minus(lastDragPosition)) 60 | 61 | lastDragPosition = position 62 | updateLine(relate: item.text) 63 | } 64 | } 65 | 66 | override func mouseUp(with event: NSEvent) { 67 | 68 | guard let item = currentDraggingItem else { 69 | return 70 | } 71 | 72 | if let node = nodeMap[item.text] { 73 | 74 | node.frame = NodeFrame(x: Int(item.frame.origin.x), 75 | y: Int(item.frame.origin.y), 76 | width: Int(item.frame.size.width), 77 | height: Int(item.frame.size.height)) 78 | } 79 | 80 | currentDraggingItem = nil 81 | updateLine(relate: item.text) 82 | } 83 | 84 | 85 | // MARK: Private Method 86 | 87 | private func setUp() { 88 | 89 | clear() 90 | let superSize = superview?.frame.size ?? CGSize() 91 | 92 | let size = prettyRelation.preferredSize 93 | 94 | frame = NSRect(x: 0, y: 0, width: max(size.width, superSize.width), 95 | height: max(superSize.height, size.height)) 96 | 97 | wantsLayer = true 98 | layer?.backgroundColor = NSColor.white.cgColor 99 | 100 | setupItems() 101 | setupLines() 102 | 103 | lineMap.forEach { 104 | layer?.addSublayer($0.1) 105 | } 106 | 107 | itemMap.forEach { 108 | addSubview($0.1) 109 | } 110 | } 111 | 112 | 113 | /// 创建 RelationItemViews 114 | private func setupItems() { 115 | 116 | prettyRelation.nodes.forEach { node in 117 | 118 | let item = RelationItemView(frame: node.frame.rect, text: node.name) 119 | let color = NSColor(node.color) 120 | item.backgroundColor = color 121 | 122 | if (color?.colorDifference(.white) ?? 0) < 0.1 { 123 | 124 | item.label.textColor = NSColor(white: 0.2, alpha: 1.0) 125 | } 126 | 127 | itemMap[node.name] = item 128 | nodeMap[node.name] = node 129 | } 130 | } 131 | 132 | 133 | /// 创建 Node 之间的连接线 134 | private func setupLines() { 135 | 136 | prettyRelation.nodes.forEach { parent in 137 | 138 | let color = NSColor(parent.color) 139 | parent.sons.forEach { son in 140 | 141 | let key = parent.name + "|" + son 142 | let shapeLayer = CAShapeLayer() 143 | shapeLayer.fillColor = NSColor.clear.cgColor 144 | shapeLayer.strokeColor = color?.cgColor 145 | shapeLayer.lineWidth = 2 146 | shapeLayer.path = linePath(parent: parent.name, son: son) 147 | lineMap[key] = shapeLayer 148 | } 149 | } 150 | } 151 | 152 | private func clear() { 153 | 154 | lineMap.removeAll() 155 | itemMap.removeAll() 156 | nodeMap.removeAll() 157 | subviews.forEach { $0.removeFromSuperview() } 158 | layer?.sublayers?.forEach { $0.removeFromSuperlayer() } 159 | } 160 | 161 | 162 | /// 根据 Position 找到对应位置的 RelationItemView 163 | /// 164 | /// - Parameter position: NSPoint 165 | /// - Returns: RelationItemView or nil 166 | private func findItemLocate(in position: NSPoint) -> RelationItemView? { 167 | 168 | for (_, item) in itemMap { 169 | if item.frame.contains(position) { 170 | return item 171 | } 172 | } 173 | return nil 174 | } 175 | 176 | 177 | /// 根据父节点和子节点的名称创建连接线的 Path 178 | /// 179 | /// - Parameters: 180 | /// - parent: 父节点名称 181 | /// - son: 子节点名称 182 | /// - Returns: CGPath 183 | private func linePath(parent: String, son: String) -> CGPath { 184 | 185 | let parentItem = itemMap[parent]! 186 | let sonItem = itemMap[son]! 187 | 188 | let path = CGMutablePath() 189 | path.move(to: parentItem.center) 190 | path.addLine(to: sonItem.center) 191 | return path 192 | } 193 | 194 | 195 | /// 更新和某个节点相关的线 196 | /// 197 | /// - Parameter name: 节点名称 198 | private func updateLine(relate name: String) { 199 | 200 | for (key, value) in lineMap { 201 | 202 | let components = key.components(separatedBy: "|") 203 | if components.contains(name) { 204 | 205 | value.path = linePath(parent: components.first!, son: components.last!) 206 | 207 | if components.first == currentDraggingItem?.text { 208 | 209 | value.strokeColor = NSColor.red.cgColor 210 | } else if (components.last == currentDraggingItem?.text) { 211 | 212 | value.strokeColor = NSColor.blue.cgColor 213 | } else { 214 | value.strokeColor = itemMap[components.first!]?.backgroundColor?.cgColor 215 | } 216 | } 217 | } 218 | 219 | } 220 | 221 | } 222 | -------------------------------------------------------------------------------- /three/Pretty.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C26572C220786509005650E8 /* CharacterSet+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26572C120786509005650E8 /* CharacterSet+Extension.swift */; }; 11 | C2756DF320765C8100FF160B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756DF220765C8100FF160B /* AppDelegate.swift */; }; 12 | C2756DF520765C8100FF160B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756DF420765C8100FF160B /* ViewController.swift */; }; 13 | C2756DF720765C8200FF160B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C2756DF620765C8200FF160B /* Assets.xcassets */; }; 14 | C2756DFA20765C8200FF160B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C2756DF820765C8200FF160B /* Main.storyboard */; }; 15 | C2756E2A20765D4E00FF160B /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E2720765D4E00FF160B /* Array+Extension.swift */; }; 16 | C2756E2B20765D4E00FF160B /* RelationItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E2820765D4E00FF160B /* RelationItemView.swift */; }; 17 | C2D8BC02207720F30093FC8E /* NSPoint+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2D8BC01207720F30093FC8E /* NSPoint+Extension.swift */; }; 18 | C2D8BC04207720FD0093FC8E /* NSRect+Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2D8BC03207720FD0093FC8E /* NSRect+Dependency.swift */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | C26572C120786509005650E8 /* CharacterSet+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Extension.swift"; sourceTree = ""; }; 23 | C2756DEF20765C8100FF160B /* Pretty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pretty.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | C2756DF220765C8100FF160B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 25 | C2756DF420765C8100FF160B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 26 | C2756DF620765C8200FF160B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | C2756DF920765C8200FF160B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | C2756DFB20765C8200FF160B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | C2756DFC20765C8200FF160B /* Pretty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Pretty.entitlements; sourceTree = ""; }; 30 | C2756E2720765D4E00FF160B /* Array+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = ""; }; 31 | C2756E2820765D4E00FF160B /* RelationItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelationItemView.swift; sourceTree = ""; }; 32 | C2D8BC01207720F30093FC8E /* NSPoint+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSPoint+Extension.swift"; sourceTree = ""; }; 33 | C2D8BC03207720FD0093FC8E /* NSRect+Dependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSRect+Dependency.swift"; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | C2756DEC20765C8100FF160B /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | C2756DE620765C8100FF160B = { 48 | isa = PBXGroup; 49 | children = ( 50 | C2756DF120765C8100FF160B /* Pretty */, 51 | C2756DF020765C8100FF160B /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | C2756DF020765C8100FF160B /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | C2756DEF20765C8100FF160B /* Pretty.app */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | C2756DF120765C8100FF160B /* Pretty */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | C2D8BC08207722160093FC8E /* Resource */, 67 | C2D8BBFE20771FF80093FC8E /* Extension */, 68 | C2D8BC07207721D90093FC8E /* View */, 69 | C2D8BC09207722260093FC8E /* Controller */, 70 | C2756DF220765C8100FF160B /* AppDelegate.swift */, 71 | C2756DFB20765C8200FF160B /* Info.plist */, 72 | C2756DFC20765C8200FF160B /* Pretty.entitlements */, 73 | ); 74 | path = Pretty; 75 | sourceTree = ""; 76 | }; 77 | C2D8BBFE20771FF80093FC8E /* Extension */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | C2756E2720765D4E00FF160B /* Array+Extension.swift */, 81 | C2D8BC01207720F30093FC8E /* NSPoint+Extension.swift */, 82 | C2D8BC03207720FD0093FC8E /* NSRect+Dependency.swift */, 83 | C26572C120786509005650E8 /* CharacterSet+Extension.swift */, 84 | ); 85 | path = Extension; 86 | sourceTree = ""; 87 | }; 88 | C2D8BC07207721D90093FC8E /* View */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | C2756E2820765D4E00FF160B /* RelationItemView.swift */, 92 | ); 93 | path = View; 94 | sourceTree = ""; 95 | }; 96 | C2D8BC08207722160093FC8E /* Resource */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | C2756DF620765C8200FF160B /* Assets.xcassets */, 100 | C2756DF820765C8200FF160B /* Main.storyboard */, 101 | ); 102 | path = Resource; 103 | sourceTree = ""; 104 | }; 105 | C2D8BC09207722260093FC8E /* Controller */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | C2756DF420765C8100FF160B /* ViewController.swift */, 109 | ); 110 | path = Controller; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXNativeTarget section */ 116 | C2756DEE20765C8100FF160B /* Pretty */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = C2756DFF20765C8200FF160B /* Build configuration list for PBXNativeTarget "Pretty" */; 119 | buildPhases = ( 120 | C2756DEB20765C8100FF160B /* Sources */, 121 | C2756DEC20765C8100FF160B /* Frameworks */, 122 | C2756DED20765C8100FF160B /* Resources */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = Pretty; 129 | productName = Pretty; 130 | productReference = C2756DEF20765C8100FF160B /* Pretty.app */; 131 | productType = "com.apple.product-type.application"; 132 | }; 133 | /* End PBXNativeTarget section */ 134 | 135 | /* Begin PBXProject section */ 136 | C2756DE720765C8100FF160B /* Project object */ = { 137 | isa = PBXProject; 138 | attributes = { 139 | LastSwiftUpdateCheck = 0930; 140 | LastUpgradeCheck = 1240; 141 | ORGANIZATIONNAME = Octree; 142 | TargetAttributes = { 143 | C2756DEE20765C8100FF160B = { 144 | CreatedOnToolsVersion = 9.3; 145 | }; 146 | }; 147 | }; 148 | buildConfigurationList = C2756DEA20765C8100FF160B /* Build configuration list for PBXProject "Pretty" */; 149 | compatibilityVersion = "Xcode 9.3"; 150 | developmentRegion = en; 151 | hasScannedForEncodings = 0; 152 | knownRegions = ( 153 | en, 154 | Base, 155 | ); 156 | mainGroup = C2756DE620765C8100FF160B; 157 | productRefGroup = C2756DF020765C8100FF160B /* Products */; 158 | projectDirPath = ""; 159 | projectRoot = ""; 160 | targets = ( 161 | C2756DEE20765C8100FF160B /* Pretty */, 162 | ); 163 | }; 164 | /* End PBXProject section */ 165 | 166 | /* Begin PBXResourcesBuildPhase section */ 167 | C2756DED20765C8100FF160B /* Resources */ = { 168 | isa = PBXResourcesBuildPhase; 169 | buildActionMask = 2147483647; 170 | files = ( 171 | C2756DF720765C8200FF160B /* Assets.xcassets in Resources */, 172 | C2756DFA20765C8200FF160B /* Main.storyboard in Resources */, 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | /* End PBXResourcesBuildPhase section */ 177 | 178 | /* Begin PBXSourcesBuildPhase section */ 179 | C2756DEB20765C8100FF160B /* Sources */ = { 180 | isa = PBXSourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | C26572C220786509005650E8 /* CharacterSet+Extension.swift in Sources */, 184 | C2D8BC02207720F30093FC8E /* NSPoint+Extension.swift in Sources */, 185 | C2756E2B20765D4E00FF160B /* RelationItemView.swift in Sources */, 186 | C2756E2A20765D4E00FF160B /* Array+Extension.swift in Sources */, 187 | C2756DF520765C8100FF160B /* ViewController.swift in Sources */, 188 | C2756DF320765C8100FF160B /* AppDelegate.swift in Sources */, 189 | C2D8BC04207720FD0093FC8E /* NSRect+Dependency.swift in Sources */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXSourcesBuildPhase section */ 194 | 195 | /* Begin PBXVariantGroup section */ 196 | C2756DF820765C8200FF160B /* Main.storyboard */ = { 197 | isa = PBXVariantGroup; 198 | children = ( 199 | C2756DF920765C8200FF160B /* Base */, 200 | ); 201 | name = Main.storyboard; 202 | sourceTree = ""; 203 | }; 204 | /* End PBXVariantGroup section */ 205 | 206 | /* Begin XCBuildConfiguration section */ 207 | C2756DFD20765C8200FF160B /* Debug */ = { 208 | isa = XCBuildConfiguration; 209 | buildSettings = { 210 | ALWAYS_SEARCH_USER_PATHS = NO; 211 | CLANG_ANALYZER_NONNULL = YES; 212 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 213 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 214 | CLANG_CXX_LIBRARY = "libc++"; 215 | CLANG_ENABLE_MODULES = YES; 216 | CLANG_ENABLE_OBJC_ARC = YES; 217 | CLANG_ENABLE_OBJC_WEAK = YES; 218 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 219 | CLANG_WARN_BOOL_CONVERSION = YES; 220 | CLANG_WARN_COMMA = YES; 221 | CLANG_WARN_CONSTANT_CONVERSION = YES; 222 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 223 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 224 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 225 | CLANG_WARN_EMPTY_BODY = YES; 226 | CLANG_WARN_ENUM_CONVERSION = YES; 227 | CLANG_WARN_INFINITE_RECURSION = YES; 228 | CLANG_WARN_INT_CONVERSION = YES; 229 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 230 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 231 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 232 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 233 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 234 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 235 | CLANG_WARN_STRICT_PROTOTYPES = YES; 236 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 237 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 238 | CLANG_WARN_UNREACHABLE_CODE = YES; 239 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 240 | CODE_SIGN_IDENTITY = "Mac Developer"; 241 | COPY_PHASE_STRIP = NO; 242 | DEBUG_INFORMATION_FORMAT = dwarf; 243 | ENABLE_STRICT_OBJC_MSGSEND = YES; 244 | ENABLE_TESTABILITY = YES; 245 | GCC_C_LANGUAGE_STANDARD = gnu11; 246 | GCC_DYNAMIC_NO_PIC = NO; 247 | GCC_NO_COMMON_BLOCKS = YES; 248 | GCC_OPTIMIZATION_LEVEL = 0; 249 | GCC_PREPROCESSOR_DEFINITIONS = ( 250 | "DEBUG=1", 251 | "$(inherited)", 252 | ); 253 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 254 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 255 | GCC_WARN_UNDECLARED_SELECTOR = YES; 256 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 257 | GCC_WARN_UNUSED_FUNCTION = YES; 258 | GCC_WARN_UNUSED_VARIABLE = YES; 259 | MACOSX_DEPLOYMENT_TARGET = 10.13; 260 | MTL_ENABLE_DEBUG_INFO = YES; 261 | ONLY_ACTIVE_ARCH = YES; 262 | SDKROOT = macosx; 263 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 264 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 265 | }; 266 | name = Debug; 267 | }; 268 | C2756DFE20765C8200FF160B /* Release */ = { 269 | isa = XCBuildConfiguration; 270 | buildSettings = { 271 | ALWAYS_SEARCH_USER_PATHS = NO; 272 | CLANG_ANALYZER_NONNULL = YES; 273 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 274 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 275 | CLANG_CXX_LIBRARY = "libc++"; 276 | CLANG_ENABLE_MODULES = YES; 277 | CLANG_ENABLE_OBJC_ARC = YES; 278 | CLANG_ENABLE_OBJC_WEAK = YES; 279 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 280 | CLANG_WARN_BOOL_CONVERSION = YES; 281 | CLANG_WARN_COMMA = YES; 282 | CLANG_WARN_CONSTANT_CONVERSION = YES; 283 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 284 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 285 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 286 | CLANG_WARN_EMPTY_BODY = YES; 287 | CLANG_WARN_ENUM_CONVERSION = YES; 288 | CLANG_WARN_INFINITE_RECURSION = YES; 289 | CLANG_WARN_INT_CONVERSION = YES; 290 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 291 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 292 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 293 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 294 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 295 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 296 | CLANG_WARN_STRICT_PROTOTYPES = YES; 297 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 298 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 299 | CLANG_WARN_UNREACHABLE_CODE = YES; 300 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 301 | CODE_SIGN_IDENTITY = "Mac Developer"; 302 | COPY_PHASE_STRIP = NO; 303 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 304 | ENABLE_NS_ASSERTIONS = NO; 305 | ENABLE_STRICT_OBJC_MSGSEND = YES; 306 | GCC_C_LANGUAGE_STANDARD = gnu11; 307 | GCC_NO_COMMON_BLOCKS = YES; 308 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 309 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 310 | GCC_WARN_UNDECLARED_SELECTOR = YES; 311 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 312 | GCC_WARN_UNUSED_FUNCTION = YES; 313 | GCC_WARN_UNUSED_VARIABLE = YES; 314 | MACOSX_DEPLOYMENT_TARGET = 10.13; 315 | MTL_ENABLE_DEBUG_INFO = NO; 316 | SDKROOT = macosx; 317 | SWIFT_COMPILATION_MODE = wholemodule; 318 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 319 | }; 320 | name = Release; 321 | }; 322 | C2756E0020765C8200FF160B /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 326 | CODE_SIGN_ENTITLEMENTS = Pretty/Pretty.entitlements; 327 | CODE_SIGN_IDENTITY = "-"; 328 | CODE_SIGN_STYLE = Automatic; 329 | COMBINE_HIDPI_IMAGES = YES; 330 | DEVELOPMENT_TEAM = D878P8TW6L; 331 | INFOPLIST_FILE = Pretty/Info.plist; 332 | LD_RUNPATH_SEARCH_PATHS = ( 333 | "$(inherited)", 334 | "@executable_path/../Frameworks", 335 | ); 336 | PRODUCT_BUNDLE_IDENTIFIER = me.octree.Pretty; 337 | PRODUCT_NAME = "$(TARGET_NAME)"; 338 | SWIFT_VERSION = 4.0; 339 | }; 340 | name = Debug; 341 | }; 342 | C2756E0120765C8200FF160B /* Release */ = { 343 | isa = XCBuildConfiguration; 344 | buildSettings = { 345 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 346 | CODE_SIGN_ENTITLEMENTS = Pretty/Pretty.entitlements; 347 | CODE_SIGN_IDENTITY = "-"; 348 | CODE_SIGN_STYLE = Automatic; 349 | COMBINE_HIDPI_IMAGES = YES; 350 | DEVELOPMENT_TEAM = D878P8TW6L; 351 | INFOPLIST_FILE = Pretty/Info.plist; 352 | LD_RUNPATH_SEARCH_PATHS = ( 353 | "$(inherited)", 354 | "@executable_path/../Frameworks", 355 | ); 356 | PRODUCT_BUNDLE_IDENTIFIER = me.octree.Pretty; 357 | PRODUCT_NAME = "$(TARGET_NAME)"; 358 | SWIFT_VERSION = 4.0; 359 | }; 360 | name = Release; 361 | }; 362 | /* End XCBuildConfiguration section */ 363 | 364 | /* Begin XCConfigurationList section */ 365 | C2756DEA20765C8100FF160B /* Build configuration list for PBXProject "Pretty" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | C2756DFD20765C8200FF160B /* Debug */, 369 | C2756DFE20765C8200FF160B /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | C2756DFF20765C8200FF160B /* Build configuration list for PBXNativeTarget "Pretty" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | C2756E0020765C8200FF160B /* Debug */, 378 | C2756E0120765C8200FF160B /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | /* End XCConfigurationList section */ 384 | }; 385 | rootObject = C2756DE720765C8100FF160B /* Project object */; 386 | } 387 | -------------------------------------------------------------------------------- /three/Pretty.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /three/Pretty.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /three/Pretty/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | let OCTOpenFileNotification = "OCTOpenFileNotification" 12 | public var FileName = "" 13 | 14 | @NSApplicationMain 15 | class AppDelegate: NSObject, NSApplicationDelegate { 16 | 17 | func applicationDidFinishLaunching(_ aNotification: Notification) { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | func applicationWillTerminate(_ aNotification: Notification) { 22 | // Insert code here to tear down your application 23 | } 24 | 25 | 26 | 27 | func application(_ sender: NSApplication, openFile filename: String) -> Bool { 28 | 29 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: OCTOpenFileNotification) , object: filename) 30 | FileName = filename 31 | return true 32 | } 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /three/Pretty/Controller/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | view.layer?.backgroundColor = NSColor.red.cgColor 19 | NotificationCenter.default.addObserver(self, selector: #selector(self.handleOpenFile(notification:)), name: NSNotification.Name(rawValue: OCTOpenFileNotification), object: nil) 20 | FileName = "/Users/jzd/Movies/podfile_lock_resolution/three/Pretty/Resource/one.json" 21 | if FileName.count > 0 { 22 | parse(file: FileName) 23 | } 24 | } 25 | 26 | 27 | 28 | @objc func handleOpenFile(notification: Notification) { 29 | 30 | guard let filename = notification.object as? String else { 31 | return 32 | } 33 | parse(file: filename) 34 | } 35 | 36 | 37 | 38 | 39 | 40 | 41 | func parse(file name: String) { 42 | 43 | if name.hasSuffix(".json") { 44 | handle(file: name) 45 | } 46 | } 47 | 48 | 49 | 50 | 51 | 52 | func handle(file name: String) { 53 | do { 54 | let content = try String(contentsOfFile: name, encoding: .utf8) 55 | let myStrings = content.components(separatedBy: .newlines) 56 | 57 | var result = [String]() 58 | 59 | 60 | 61 | for ln in myStrings{ 62 | 63 | 64 | if ln.contains("\""){ 65 | 66 | let cakes = ln.components(separatedBy: ":") 67 | /* 68 | cakes.forEach { 69 | print($0) 70 | } 71 | 72 | */ 73 | // print("\n\n\n\n\n") 74 | var onePiece: String? 75 | // print(cakes.count) 76 | if cakes.count > 1, let key = cakes[0].lean.k{ 77 | 78 | onePiece = "let " 79 | // print(cakes[0]) 80 | onePiece?.append(key) 81 | 82 | onePiece?.append(": ") 83 | 84 | let val = cakes[1].lean 85 | 86 | if Int(val) == nil{ 87 | onePiece?.append("String") 88 | } 89 | else{ 90 | onePiece?.append("Int") 91 | } 92 | } 93 | 94 | if let info = onePiece{ 95 | 96 | result.append(info) 97 | } 98 | 99 | } 100 | 101 | 102 | 103 | } 104 | 105 | 106 | // print("\n\n\n\n\n") 107 | result.forEach { 108 | print($0) 109 | } 110 | print("\n\n\n\n\n") 111 | } catch { 112 | 113 | alert(title: "Error", msg: error.localizedDescription) 114 | } 115 | } 116 | 117 | 118 | 119 | 120 | 121 | func alert(title: String, msg: String) { 122 | 123 | 124 | let alert = NSAlert() 125 | 126 | 127 | alert.addButton(withTitle: "Ok") 128 | alert.messageText = title 129 | alert.informativeText = msg 130 | alert.alertStyle = .warning 131 | alert.runModal() 132 | } 133 | 134 | } 135 | 136 | 137 | 138 | 139 | extension String{ 140 | 141 | var k: String?{ 142 | let ret = groups(for: "^\"(.+)\"$") 143 | // print(ret) 144 | 145 | if ret.count > 0, ret[0].count > 1{ 146 | return ret[0][1] 147 | } 148 | else{ 149 | return nil 150 | } 151 | } 152 | 153 | 154 | func groups(for regexPattern: String) -> [[String]] { 155 | do { 156 | let text = self 157 | let regex = try NSRegularExpression(pattern: regexPattern) 158 | let matches = regex.matches(in: text, 159 | range: NSRange(text.startIndex..., in: text)) 160 | return matches.map { match in 161 | return (0.. [[Element]] { 14 | 15 | let rows = (count + size - 1) / size 16 | var groups = Array<[Element]>(repeating: [Element](), count: rows) 17 | 18 | for (index, elt) in enumerated() { 19 | 20 | let newIndex = index / size 21 | groups[newIndex].append(elt) 22 | } 23 | 24 | return groups 25 | } 26 | } 27 | 28 | 29 | extension Array where Element: Equatable { 30 | 31 | func contains(_ other: [Element]) -> Bool { 32 | 33 | for elt in other { 34 | if !contains(elt) { 35 | 36 | return false 37 | } 38 | } 39 | 40 | return true 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /three/Pretty/Extension/CharacterSet+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharacterSet+Extension.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension CharacterSet { 12 | 13 | func contains(_ c: Character) -> Bool { 14 | 15 | let scalars = String(c).unicodeScalars 16 | guard scalars.count == 1 else { 17 | return false 18 | } 19 | return contains(scalars.first!) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /three/Pretty/Extension/NSPoint+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSPoint+Extension.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension NSPoint { 13 | 14 | func minus(_ other: NSPoint) -> NSPoint { 15 | 16 | return NSPoint(x: x - other.x, y: y - other.y) 17 | } 18 | 19 | func offset(_ p: NSPoint) -> NSPoint { 20 | 21 | return NSPoint(x: x + p.x, y: y + p.y) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /three/Pretty/Extension/NSRect+Dependency.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSRect+Dependency.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension NSRect { 13 | 14 | func offset(_ p: NSPoint) -> NSRect { 15 | 16 | return NSRect(origin: origin.offset(p), size: size) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /three/Pretty/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeExtensions 11 | 12 | lock 13 | 14 | CFBundleTypeName 15 | Podfile 16 | CFBundleTypeRole 17 | Viewer 18 | NSDocumentClass 19 | NSDocument 20 | 21 | 22 | CFBundleTypeExtensions 23 | 24 | tree 25 | 26 | CFBundleTypeIconFile 27 | 28 | CFBundleTypeName 29 | pretty 30 | CFBundleTypeRole 31 | Editor 32 | NSDocumentClass 33 | NSDocument 34 | 35 | 36 | CFBundleExecutable 37 | $(EXECUTABLE_NAME) 38 | CFBundleIconFile 39 | 40 | CFBundleIdentifier 41 | $(PRODUCT_BUNDLE_IDENTIFIER) 42 | CFBundleInfoDictionaryVersion 43 | 6.0 44 | CFBundleName 45 | $(PRODUCT_NAME) 46 | CFBundlePackageType 47 | APPL 48 | CFBundleShortVersionString 49 | 1.0 50 | CFBundleVersion 51 | 1 52 | LSMinimumSystemVersion 53 | $(MACOSX_DEPLOYMENT_TARGET) 54 | NSHumanReadableCopyright 55 | Copyright © 2018年 Octree. All rights reserved. 56 | NSMainStoryboardFile 57 | Main 58 | NSPrincipalClass 59 | NSApplication 60 | 61 | 62 | -------------------------------------------------------------------------------- /three/Pretty/Pretty.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.assets.movies.read-write 8 | 9 | com.apple.security.assets.music.read-write 10 | 11 | com.apple.security.assets.pictures.read-write 12 | 13 | com.apple.security.files.downloads.read-write 14 | 15 | com.apple.security.files.user-selected.read-write 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/three/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /three/Pretty/Resource/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /three/Pretty/Resource/one.json: -------------------------------------------------------------------------------- 1 | { 2 | "cover" : "http:\/\/mnc-dictation.oss-cn-shanghai.aliyuncs.com\/users%2F17161%2Fugc%2F17161_1626767183.png?OSSAccessKeyId=LTAIQ8Lif1HHVkXd&Expires=1626908242&Signature=7BQ0xpS39qyNX%2FlsfAUFZv7WSnY%3D", 3 | "title" : "语文真的", 4 | "columnID" : 238 5 | } -------------------------------------------------------------------------------- /three/Pretty/View/RelationItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelationItemView.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class RelationItemView: NSView { 12 | 13 | var text = "" { 14 | didSet { 15 | label.stringValue = text 16 | } 17 | } 18 | private(set) var label: NSTextField = { 19 | let textfield = NSTextField() 20 | textfield.isEditable = false 21 | textfield.textColor = .white 22 | textfield.alignment = .center 23 | textfield.isBordered = false 24 | return textfield 25 | }() 26 | 27 | var backgroundColor: NSColor? { 28 | didSet { 29 | layer?.backgroundColor = backgroundColor?.cgColor 30 | label.backgroundColor = backgroundColor 31 | } 32 | } 33 | 34 | init(frame frameRect: NSRect, text: String) { 35 | super.init(frame: frameRect) 36 | self.text = text 37 | addSubview(label) 38 | label.stringValue = text 39 | wantsLayer = true 40 | layer?.cornerRadius = 10 41 | layer?.masksToBounds = true 42 | } 43 | 44 | required init?(coder decoder: NSCoder) { 45 | fatalError("init(coder:) has not been implemented") 46 | } 47 | 48 | override func draw(_ dirtyRect: NSRect) { 49 | super.draw(dirtyRect) 50 | } 51 | 52 | override func layout() { 53 | super.layout() 54 | label.frame = bounds.insetBy(dx: 5, dy: 5) 55 | } 56 | 57 | var center: NSPoint { 58 | NSPoint(x: frame.origin.x + frame.size.width / 2, 59 | y: frame.origin.y + frame.size.height / 2) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /two/Pretty.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C239289320791B980071E305 /* ColorDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C239289220791B980071E305 /* ColorDifference.swift */; }; 11 | C239289520791D410071E305 /* Color+Difference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C239289420791D410071E305 /* Color+Difference.swift */; }; 12 | C239289720791F4C0071E305 /* ColorSpaceConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C239289620791F4C0071E305 /* ColorSpaceConversion.swift */; }; 13 | C253827D207681E3008BDB09 /* DependencyNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C253827C207681E3008BDB09 /* DependencyNode.swift */; }; 14 | C2538282207685A5008BDB09 /* HexColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2538281207685A5008BDB09 /* HexColors.swift */; }; 15 | C26572C220786509005650E8 /* CharacterSet+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26572C120786509005650E8 /* CharacterSet+Extension.swift */; }; 16 | C2756DF320765C8100FF160B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756DF220765C8100FF160B /* AppDelegate.swift */; }; 17 | C2756DF520765C8100FF160B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756DF420765C8100FF160B /* ViewController.swift */; }; 18 | C2756DF720765C8200FF160B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C2756DF620765C8200FF160B /* Assets.xcassets */; }; 19 | C2756DFA20765C8200FF160B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C2756DF820765C8200FF160B /* Main.storyboard */; }; 20 | C2756E1620765CBB00FF160B /* Luminosity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E0320765CBA00FF160B /* Luminosity.swift */; }; 21 | C2756E1720765CBB00FF160B /* RandomColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E0420765CBA00FF160B /* RandomColor.swift */; }; 22 | C2756E1820765CBB00FF160B /* ColorDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E0520765CBA00FF160B /* ColorDefinition.swift */; }; 23 | C2756E1920765CBB00FF160B /* Hue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E0620765CBA00FF160B /* Hue.swift */; }; 24 | C2756E2120765CBB00FF160B /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E1120765CBB00FF160B /* Parser.swift */; }; 25 | C2756E2920765D4E00FF160B /* RelationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E2620765D4E00FF160B /* RelationView.swift */; }; 26 | C2756E2A20765D4E00FF160B /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E2720765D4E00FF160B /* Array+Extension.swift */; }; 27 | C2756E2B20765D4E00FF160B /* RelationItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2756E2820765D4E00FF160B /* RelationItemView.swift */; }; 28 | C2D8BBFD2076A4960093FC8E /* DependencySort.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2D8BBFC2076A4960093FC8E /* DependencySort.swift */; }; 29 | C2D8BC002077201E0093FC8E /* PrettyRelation+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2D8BBFF2077201E0093FC8E /* PrettyRelation+Extension.swift */; }; 30 | C2D8BC02207720F30093FC8E /* NSPoint+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2D8BC01207720F30093FC8E /* NSPoint+Extension.swift */; }; 31 | C2D8BC04207720FD0093FC8E /* NSRect+Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2D8BC03207720FD0093FC8E /* NSRect+Dependency.swift */; }; 32 | C2D8BC06207721230093FC8E /* PrettyRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2D8BC05207721230093FC8E /* PrettyRelation.swift */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | C239289220791B980071E305 /* ColorDifference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorDifference.swift; sourceTree = ""; }; 37 | C239289420791D410071E305 /* Color+Difference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Difference.swift"; sourceTree = ""; }; 38 | C239289620791F4C0071E305 /* ColorSpaceConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorSpaceConversion.swift; sourceTree = ""; }; 39 | C253827C207681E3008BDB09 /* DependencyNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyNode.swift; sourceTree = ""; }; 40 | C2538281207685A5008BDB09 /* HexColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HexColors.swift; sourceTree = ""; }; 41 | C26572C120786509005650E8 /* CharacterSet+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Extension.swift"; sourceTree = ""; }; 42 | C2756DEF20765C8100FF160B /* Pretty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pretty.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | C2756DF220765C8100FF160B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 44 | C2756DF420765C8100FF160B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 45 | C2756DF620765C8200FF160B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 46 | C2756DF920765C8200FF160B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | C2756DFB20765C8200FF160B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | C2756DFC20765C8200FF160B /* Pretty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Pretty.entitlements; sourceTree = ""; }; 49 | C2756E0320765CBA00FF160B /* Luminosity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Luminosity.swift; sourceTree = ""; }; 50 | C2756E0420765CBA00FF160B /* RandomColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomColor.swift; sourceTree = ""; }; 51 | C2756E0520765CBA00FF160B /* ColorDefinition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorDefinition.swift; sourceTree = ""; }; 52 | C2756E0620765CBA00FF160B /* Hue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Hue.swift; sourceTree = ""; }; 53 | C2756E0720765CBA00FF160B /* RandomColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RandomColor.h; sourceTree = ""; }; 54 | C2756E1120765CBB00FF160B /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = ""; }; 55 | C2756E2620765D4E00FF160B /* RelationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelationView.swift; sourceTree = ""; }; 56 | C2756E2720765D4E00FF160B /* Array+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = ""; }; 57 | C2756E2820765D4E00FF160B /* RelationItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelationItemView.swift; sourceTree = ""; }; 58 | C2D8BBFC2076A4960093FC8E /* DependencySort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencySort.swift; sourceTree = ""; }; 59 | C2D8BBFF2077201E0093FC8E /* PrettyRelation+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrettyRelation+Extension.swift"; sourceTree = ""; }; 60 | C2D8BC01207720F30093FC8E /* NSPoint+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSPoint+Extension.swift"; sourceTree = ""; }; 61 | C2D8BC03207720FD0093FC8E /* NSRect+Dependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSRect+Dependency.swift"; sourceTree = ""; }; 62 | C2D8BC05207721230093FC8E /* PrettyRelation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrettyRelation.swift; sourceTree = ""; }; 63 | /* End PBXFileReference section */ 64 | 65 | /* Begin PBXFrameworksBuildPhase section */ 66 | C2756DEC20765C8100FF160B /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | /* End PBXFrameworksBuildPhase section */ 74 | 75 | /* Begin PBXGroup section */ 76 | C253827B207681CA008BDB09 /* Model */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | C253827C207681E3008BDB09 /* DependencyNode.swift */, 80 | C2D8BC05207721230093FC8E /* PrettyRelation.swift */, 81 | ); 82 | path = Model; 83 | sourceTree = ""; 84 | }; 85 | C253827E2076824B008BDB09 /* Utils */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | C2538281207685A5008BDB09 /* HexColors.swift */, 89 | C2D8BBFC2076A4960093FC8E /* DependencySort.swift */, 90 | C239289220791B980071E305 /* ColorDifference.swift */, 91 | C239289620791F4C0071E305 /* ColorSpaceConversion.swift */, 92 | ); 93 | path = Utils; 94 | sourceTree = ""; 95 | }; 96 | C2756DE620765C8100FF160B = { 97 | isa = PBXGroup; 98 | children = ( 99 | C2756DF120765C8100FF160B /* Pretty */, 100 | C2756DF020765C8100FF160B /* Products */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | C2756DF020765C8100FF160B /* Products */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | C2756DEF20765C8100FF160B /* Pretty.app */, 108 | ); 109 | name = Products; 110 | sourceTree = ""; 111 | }; 112 | C2756DF120765C8100FF160B /* Pretty */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | C2D8BC08207722160093FC8E /* Resource */, 116 | C2D8BBFE20771FF80093FC8E /* Extension */, 117 | C2756E0220765CBA00FF160B /* RandomColors */, 118 | C2756E0D20765CBB00FF160B /* Parser */, 119 | C253827E2076824B008BDB09 /* Utils */, 120 | C253827B207681CA008BDB09 /* Model */, 121 | C2D8BC07207721D90093FC8E /* View */, 122 | C2D8BC09207722260093FC8E /* Controller */, 123 | C2756DF220765C8100FF160B /* AppDelegate.swift */, 124 | C2756DFB20765C8200FF160B /* Info.plist */, 125 | C2756DFC20765C8200FF160B /* Pretty.entitlements */, 126 | ); 127 | path = Pretty; 128 | sourceTree = ""; 129 | }; 130 | C2756E0220765CBA00FF160B /* RandomColors */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | C2756E0320765CBA00FF160B /* Luminosity.swift */, 134 | C2756E0420765CBA00FF160B /* RandomColor.swift */, 135 | C2756E0520765CBA00FF160B /* ColorDefinition.swift */, 136 | C2756E0620765CBA00FF160B /* Hue.swift */, 137 | C2756E0720765CBA00FF160B /* RandomColor.h */, 138 | ); 139 | path = RandomColors; 140 | sourceTree = ""; 141 | }; 142 | C2756E0D20765CBB00FF160B /* Parser */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | C2756E1120765CBB00FF160B /* Parser.swift */, 146 | ); 147 | path = Parser; 148 | sourceTree = ""; 149 | }; 150 | C2D8BBFE20771FF80093FC8E /* Extension */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | C2756E2720765D4E00FF160B /* Array+Extension.swift */, 154 | C2D8BBFF2077201E0093FC8E /* PrettyRelation+Extension.swift */, 155 | C2D8BC01207720F30093FC8E /* NSPoint+Extension.swift */, 156 | C2D8BC03207720FD0093FC8E /* NSRect+Dependency.swift */, 157 | C26572C120786509005650E8 /* CharacterSet+Extension.swift */, 158 | C239289420791D410071E305 /* Color+Difference.swift */, 159 | ); 160 | path = Extension; 161 | sourceTree = ""; 162 | }; 163 | C2D8BC07207721D90093FC8E /* View */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | C2756E2820765D4E00FF160B /* RelationItemView.swift */, 167 | C2756E2620765D4E00FF160B /* RelationView.swift */, 168 | ); 169 | path = View; 170 | sourceTree = ""; 171 | }; 172 | C2D8BC08207722160093FC8E /* Resource */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | C2756DF620765C8200FF160B /* Assets.xcassets */, 176 | C2756DF820765C8200FF160B /* Main.storyboard */, 177 | ); 178 | path = Resource; 179 | sourceTree = ""; 180 | }; 181 | C2D8BC09207722260093FC8E /* Controller */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | C2756DF420765C8100FF160B /* ViewController.swift */, 185 | ); 186 | path = Controller; 187 | sourceTree = ""; 188 | }; 189 | /* End PBXGroup section */ 190 | 191 | /* Begin PBXNativeTarget section */ 192 | C2756DEE20765C8100FF160B /* Pretty */ = { 193 | isa = PBXNativeTarget; 194 | buildConfigurationList = C2756DFF20765C8200FF160B /* Build configuration list for PBXNativeTarget "Pretty" */; 195 | buildPhases = ( 196 | C2756DEB20765C8100FF160B /* Sources */, 197 | C2756DEC20765C8100FF160B /* Frameworks */, 198 | C2756DED20765C8100FF160B /* Resources */, 199 | ); 200 | buildRules = ( 201 | ); 202 | dependencies = ( 203 | ); 204 | name = Pretty; 205 | productName = Pretty; 206 | productReference = C2756DEF20765C8100FF160B /* Pretty.app */; 207 | productType = "com.apple.product-type.application"; 208 | }; 209 | /* End PBXNativeTarget section */ 210 | 211 | /* Begin PBXProject section */ 212 | C2756DE720765C8100FF160B /* Project object */ = { 213 | isa = PBXProject; 214 | attributes = { 215 | LastSwiftUpdateCheck = 0930; 216 | LastUpgradeCheck = 1240; 217 | ORGANIZATIONNAME = Octree; 218 | TargetAttributes = { 219 | C2756DEE20765C8100FF160B = { 220 | CreatedOnToolsVersion = 9.3; 221 | }; 222 | }; 223 | }; 224 | buildConfigurationList = C2756DEA20765C8100FF160B /* Build configuration list for PBXProject "Pretty" */; 225 | compatibilityVersion = "Xcode 9.3"; 226 | developmentRegion = en; 227 | hasScannedForEncodings = 0; 228 | knownRegions = ( 229 | en, 230 | Base, 231 | ); 232 | mainGroup = C2756DE620765C8100FF160B; 233 | productRefGroup = C2756DF020765C8100FF160B /* Products */; 234 | projectDirPath = ""; 235 | projectRoot = ""; 236 | targets = ( 237 | C2756DEE20765C8100FF160B /* Pretty */, 238 | ); 239 | }; 240 | /* End PBXProject section */ 241 | 242 | /* Begin PBXResourcesBuildPhase section */ 243 | C2756DED20765C8100FF160B /* Resources */ = { 244 | isa = PBXResourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | C2756DF720765C8200FF160B /* Assets.xcassets in Resources */, 248 | C2756DFA20765C8200FF160B /* Main.storyboard in Resources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | /* End PBXResourcesBuildPhase section */ 253 | 254 | /* Begin PBXSourcesBuildPhase section */ 255 | C2756DEB20765C8100FF160B /* Sources */ = { 256 | isa = PBXSourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | C239289520791D410071E305 /* Color+Difference.swift in Sources */, 260 | C26572C220786509005650E8 /* CharacterSet+Extension.swift in Sources */, 261 | C239289320791B980071E305 /* ColorDifference.swift in Sources */, 262 | C2D8BC06207721230093FC8E /* PrettyRelation.swift in Sources */, 263 | C2538282207685A5008BDB09 /* HexColors.swift in Sources */, 264 | C2756E1720765CBB00FF160B /* RandomColor.swift in Sources */, 265 | C2D8BC02207720F30093FC8E /* NSPoint+Extension.swift in Sources */, 266 | C253827D207681E3008BDB09 /* DependencyNode.swift in Sources */, 267 | C2756E1820765CBB00FF160B /* ColorDefinition.swift in Sources */, 268 | C2756E2B20765D4E00FF160B /* RelationItemView.swift in Sources */, 269 | C2756E2A20765D4E00FF160B /* Array+Extension.swift in Sources */, 270 | C2756E2920765D4E00FF160B /* RelationView.swift in Sources */, 271 | C2756E1920765CBB00FF160B /* Hue.swift in Sources */, 272 | C2D8BC002077201E0093FC8E /* PrettyRelation+Extension.swift in Sources */, 273 | C239289720791F4C0071E305 /* ColorSpaceConversion.swift in Sources */, 274 | C2756E1620765CBB00FF160B /* Luminosity.swift in Sources */, 275 | C2756E2120765CBB00FF160B /* Parser.swift in Sources */, 276 | C2756DF520765C8100FF160B /* ViewController.swift in Sources */, 277 | C2756DF320765C8100FF160B /* AppDelegate.swift in Sources */, 278 | C2D8BBFD2076A4960093FC8E /* DependencySort.swift in Sources */, 279 | C2D8BC04207720FD0093FC8E /* NSRect+Dependency.swift in Sources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | /* End PBXSourcesBuildPhase section */ 284 | 285 | /* Begin PBXVariantGroup section */ 286 | C2756DF820765C8200FF160B /* Main.storyboard */ = { 287 | isa = PBXVariantGroup; 288 | children = ( 289 | C2756DF920765C8200FF160B /* Base */, 290 | ); 291 | name = Main.storyboard; 292 | sourceTree = ""; 293 | }; 294 | /* End PBXVariantGroup section */ 295 | 296 | /* Begin XCBuildConfiguration section */ 297 | C2756DFD20765C8200FF160B /* Debug */ = { 298 | isa = XCBuildConfiguration; 299 | buildSettings = { 300 | ALWAYS_SEARCH_USER_PATHS = NO; 301 | CLANG_ANALYZER_NONNULL = YES; 302 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 303 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 304 | CLANG_CXX_LIBRARY = "libc++"; 305 | CLANG_ENABLE_MODULES = YES; 306 | CLANG_ENABLE_OBJC_ARC = YES; 307 | CLANG_ENABLE_OBJC_WEAK = YES; 308 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 309 | CLANG_WARN_BOOL_CONVERSION = YES; 310 | CLANG_WARN_COMMA = YES; 311 | CLANG_WARN_CONSTANT_CONVERSION = YES; 312 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 313 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 314 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 315 | CLANG_WARN_EMPTY_BODY = YES; 316 | CLANG_WARN_ENUM_CONVERSION = YES; 317 | CLANG_WARN_INFINITE_RECURSION = YES; 318 | CLANG_WARN_INT_CONVERSION = YES; 319 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 320 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 321 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 323 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 324 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 325 | CLANG_WARN_STRICT_PROTOTYPES = YES; 326 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 327 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | CODE_SIGN_IDENTITY = "Mac Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = dwarf; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu11; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PREPROCESSOR_DEFINITIONS = ( 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | MACOSX_DEPLOYMENT_TARGET = 10.13; 350 | MTL_ENABLE_DEBUG_INFO = YES; 351 | ONLY_ACTIVE_ARCH = YES; 352 | SDKROOT = macosx; 353 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 354 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 355 | }; 356 | name = Debug; 357 | }; 358 | C2756DFE20765C8200FF160B /* Release */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | ALWAYS_SEARCH_USER_PATHS = NO; 362 | CLANG_ANALYZER_NONNULL = YES; 363 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_ENABLE_OBJC_WEAK = YES; 369 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 370 | CLANG_WARN_BOOL_CONVERSION = YES; 371 | CLANG_WARN_COMMA = YES; 372 | CLANG_WARN_CONSTANT_CONVERSION = YES; 373 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 374 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 375 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 376 | CLANG_WARN_EMPTY_BODY = YES; 377 | CLANG_WARN_ENUM_CONVERSION = YES; 378 | CLANG_WARN_INFINITE_RECURSION = YES; 379 | CLANG_WARN_INT_CONVERSION = YES; 380 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 381 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 382 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 383 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 384 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 385 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 386 | CLANG_WARN_STRICT_PROTOTYPES = YES; 387 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 388 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 389 | CLANG_WARN_UNREACHABLE_CODE = YES; 390 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 391 | CODE_SIGN_IDENTITY = "Mac Developer"; 392 | COPY_PHASE_STRIP = NO; 393 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 394 | ENABLE_NS_ASSERTIONS = NO; 395 | ENABLE_STRICT_OBJC_MSGSEND = YES; 396 | GCC_C_LANGUAGE_STANDARD = gnu11; 397 | GCC_NO_COMMON_BLOCKS = YES; 398 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 399 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 400 | GCC_WARN_UNDECLARED_SELECTOR = YES; 401 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 402 | GCC_WARN_UNUSED_FUNCTION = YES; 403 | GCC_WARN_UNUSED_VARIABLE = YES; 404 | MACOSX_DEPLOYMENT_TARGET = 10.13; 405 | MTL_ENABLE_DEBUG_INFO = NO; 406 | SDKROOT = macosx; 407 | SWIFT_COMPILATION_MODE = wholemodule; 408 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 409 | }; 410 | name = Release; 411 | }; 412 | C2756E0020765C8200FF160B /* Debug */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 416 | CODE_SIGN_ENTITLEMENTS = Pretty/Pretty.entitlements; 417 | CODE_SIGN_IDENTITY = "-"; 418 | CODE_SIGN_STYLE = Automatic; 419 | COMBINE_HIDPI_IMAGES = YES; 420 | DEVELOPMENT_TEAM = D878P8TW6L; 421 | INFOPLIST_FILE = Pretty/Info.plist; 422 | LD_RUNPATH_SEARCH_PATHS = ( 423 | "$(inherited)", 424 | "@executable_path/../Frameworks", 425 | ); 426 | PRODUCT_BUNDLE_IDENTIFIER = me.octree.Pretty; 427 | PRODUCT_NAME = "$(TARGET_NAME)"; 428 | SWIFT_VERSION = 4.0; 429 | }; 430 | name = Debug; 431 | }; 432 | C2756E0120765C8200FF160B /* Release */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 436 | CODE_SIGN_ENTITLEMENTS = Pretty/Pretty.entitlements; 437 | CODE_SIGN_IDENTITY = "-"; 438 | CODE_SIGN_STYLE = Automatic; 439 | COMBINE_HIDPI_IMAGES = YES; 440 | DEVELOPMENT_TEAM = D878P8TW6L; 441 | INFOPLIST_FILE = Pretty/Info.plist; 442 | LD_RUNPATH_SEARCH_PATHS = ( 443 | "$(inherited)", 444 | "@executable_path/../Frameworks", 445 | ); 446 | PRODUCT_BUNDLE_IDENTIFIER = me.octree.Pretty; 447 | PRODUCT_NAME = "$(TARGET_NAME)"; 448 | SWIFT_VERSION = 4.0; 449 | }; 450 | name = Release; 451 | }; 452 | /* End XCBuildConfiguration section */ 453 | 454 | /* Begin XCConfigurationList section */ 455 | C2756DEA20765C8100FF160B /* Build configuration list for PBXProject "Pretty" */ = { 456 | isa = XCConfigurationList; 457 | buildConfigurations = ( 458 | C2756DFD20765C8200FF160B /* Debug */, 459 | C2756DFE20765C8200FF160B /* Release */, 460 | ); 461 | defaultConfigurationIsVisible = 0; 462 | defaultConfigurationName = Release; 463 | }; 464 | C2756DFF20765C8200FF160B /* Build configuration list for PBXNativeTarget "Pretty" */ = { 465 | isa = XCConfigurationList; 466 | buildConfigurations = ( 467 | C2756E0020765C8200FF160B /* Debug */, 468 | C2756E0120765C8200FF160B /* Release */, 469 | ); 470 | defaultConfigurationIsVisible = 0; 471 | defaultConfigurationName = Release; 472 | }; 473 | /* End XCConfigurationList section */ 474 | }; 475 | rootObject = C2756DE720765C8100FF160B /* Project object */; 476 | } 477 | -------------------------------------------------------------------------------- /two/Pretty.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /two/Pretty.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /two/Pretty/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | let OCTOpenFileNotification = "OCTOpenFileNotification" 12 | public var FileName = "" 13 | 14 | @NSApplicationMain 15 | class AppDelegate: NSObject, NSApplicationDelegate { 16 | 17 | func applicationDidFinishLaunching(_ aNotification: Notification) { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | func applicationWillTerminate(_ aNotification: Notification) { 22 | // Insert code here to tear down your application 23 | } 24 | 25 | 26 | 27 | func application(_ sender: NSApplication, openFile filename: String) -> Bool { 28 | 29 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: OCTOpenFileNotification) , object: filename) 30 | FileName = filename 31 | return true 32 | } 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /two/Pretty/Controller/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | @IBOutlet weak var scrollView: NSScrollView! 14 | 15 | private let relationView = RelationView() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | scrollView.hasVerticalScroller = true 21 | scrollView.hasHorizontalScroller = true 22 | scrollView.documentView = relationView 23 | 24 | NotificationCenter.default.addObserver(self, selector: #selector(self.handleOpenFile(notification:)), name: NSNotification.Name(rawValue: OCTOpenFileNotification), object: nil) 25 | // FileName = "/Users/jzd/Downloads/Lumiere/Podfile.lock" 26 | if FileName.count > 0 { 27 | 28 | updateRelationView(filename: FileName) 29 | } 30 | } 31 | 32 | override func viewDidLayout() { 33 | super.viewDidLayout() 34 | 35 | guard !relationView.frame.equalTo(CGRect()) else { 36 | return 37 | } 38 | 39 | let size = relationView.prettyRelation.preferredSize 40 | let parentSize = scrollView.frame.size 41 | relationView.frame = CGRect(x: 0, 42 | y: 0, 43 | width: max(size.width, parentSize.width), 44 | height: max(size.height, parentSize.height)) 45 | } 46 | 47 | 48 | @objc func handleOpenFile(notification: Notification) { 49 | 50 | guard let filename = notification.object as? String else { 51 | return 52 | } 53 | 54 | updateRelationView(filename: filename) 55 | } 56 | 57 | func updateRelationView(filename: String) { 58 | 59 | view.window?.title = filename 60 | if filename.hasSuffix(".lock") { 61 | updateWithLockFile(filename: filename) 62 | } else { 63 | updateWithDataFile(filename: filename) 64 | } 65 | } 66 | 67 | func updateWithLockFile(filename: String) { 68 | do { 69 | let string = try String(contentsOfFile: filename, encoding: .utf8) 70 | if let dependency = Parser().parse(string) { 71 | // print(dependency) 72 | relationView.prettyRelation = PrettyRelation(dependency: dependency) 73 | } else { 74 | alert(title: "Error", msg: "Parse Error: Wrong Format") 75 | } 76 | } catch { 77 | 78 | alert(title: "Error", msg: error.localizedDescription) 79 | } 80 | } 81 | 82 | 83 | func updateWithDataFile(filename: String) { 84 | 85 | do { 86 | 87 | let url = URL(fileURLWithPath: filename) 88 | let data = try Data(contentsOf: url) 89 | let relation = try JSONDecoder().decode(PrettyRelation.self, from: data) 90 | relationView.prettyRelation = relation 91 | } catch { 92 | 93 | alert(title: "Error", msg: error.localizedDescription) 94 | } 95 | } 96 | 97 | 98 | func alert(title: String, msg: String) { 99 | 100 | 101 | let alert = NSAlert() 102 | 103 | 104 | alert.addButton(withTitle: "Ok") 105 | alert.messageText = title 106 | alert.informativeText = msg 107 | alert.alertStyle = .warning 108 | alert.runModal() 109 | } 110 | 111 | } 112 | 113 | -------------------------------------------------------------------------------- /two/Pretty/Extension/Array+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+Extension.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Array { 12 | 13 | func group(_ size: Int) -> [[Element]] { 14 | 15 | let rows = (count + size - 1) / size 16 | var groups = Array<[Element]>(repeating: [Element](), count: rows) 17 | 18 | for (index, elt) in enumerated() { 19 | 20 | let newIndex = index / size 21 | groups[newIndex].append(elt) 22 | } 23 | 24 | return groups 25 | } 26 | } 27 | 28 | 29 | extension Array where Element: Equatable { 30 | 31 | func contains(_ other: [Element]) -> Bool { 32 | 33 | for elt in other { 34 | if !contains(elt) { 35 | 36 | return false 37 | } 38 | } 39 | 40 | return true 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /two/Pretty/Extension/CharacterSet+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharacterSet+Extension.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension CharacterSet { 12 | 13 | func contains(_ c: Character) -> Bool { 14 | 15 | let scalars = String(c).unicodeScalars 16 | guard scalars.count == 1 else { 17 | return false 18 | } 19 | return contains(scalars.first!) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /two/Pretty/Extension/Color+Difference.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Difference.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if os(iOS) || os(watchOS) || os(tvOS) 12 | import UIKit 13 | public typealias OCTColor = UIColor 14 | #else 15 | import Cocoa 16 | public typealias OCTColor = NSColor 17 | #endif 18 | 19 | import GLKit 20 | 21 | extension OCTColor { 22 | 23 | 24 | /// 计算与另一个颜色的差异 25 | /// 26 | /// - Parameter other: NSColor Or UIColor 27 | /// - Returns: CIE2000 颜色差异值 28 | func colorDifference(_ other: OCTColor) -> Float { 29 | 30 | let red = UnsafeMutablePointer.allocate(capacity: 1) 31 | let green = UnsafeMutablePointer.allocate(capacity: 1) 32 | let blue = UnsafeMutablePointer.allocate(capacity: 1) 33 | let alpha = UnsafeMutablePointer.allocate(capacity: 1) 34 | 35 | var color = usingColorSpace(.deviceRGB)! 36 | color.getRed(red, green: green, blue: blue, alpha: alpha) 37 | let lab1 = GLKVector3Make(Float(red[0]), Float(green[0]), Float(blue[0])) 38 | 39 | color = other.usingColorSpace(.deviceRGB)! 40 | color.getRed(red, green: green, blue: blue, alpha: alpha) 41 | let lab2 = GLKVector3Make(Float(red[0]), Float(green[0]), Float(blue[0])) 42 | 43 | return CIE2000SquaredColorDifference(1, kC: 1, kH: 1)(lab1, lab2) 44 | } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /two/Pretty/Extension/NSPoint+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSPoint+Extension.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension NSPoint { 13 | 14 | func minus(_ other: NSPoint) -> NSPoint { 15 | 16 | return NSPoint(x: x - other.x, y: y - other.y) 17 | } 18 | 19 | func offset(_ p: NSPoint) -> NSPoint { 20 | 21 | return NSPoint(x: x + p.x, y: y + p.y) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /two/Pretty/Extension/NSRect+Dependency.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSRect+Dependency.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension NSRect { 13 | 14 | func offset(_ p: NSPoint) -> NSRect { 15 | 16 | return NSRect(origin: origin.offset(p), size: size) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /two/Pretty/Extension/PrettyRelation+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrettyRelation+Extension.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | private let kRelationHorizentalSpacing = 30 12 | private let kRelationVerticalSpacing = 100 13 | private let kRelationItemHeight = 28 14 | private let kRelationItemPerRow = 6 15 | private let kRelationViewPadding = 40 16 | 17 | private func widthForItem(_ text: String) -> Int { 18 | 19 | return 10 + 10 * text.count 20 | } 21 | 22 | 23 | private func preferredSize(_ groups: [[String: [String]]]) -> NSSize { 24 | 25 | let rows = groups.count 26 | let height = (kRelationItemHeight + kRelationVerticalSpacing) * rows + kRelationViewPadding * 2 27 | let width = groups.map { elt in 28 | return elt.keys.reduce(2 * kRelationViewPadding) { 29 | $0 + 10 + $1.count * 9 + kRelationHorizentalSpacing 30 | } 31 | }.reduce(0, max) 32 | 33 | return NSSize(width: width, height: height) 34 | } 35 | 36 | private func nodesForGroups(_ groups: [[String: [String]]]) -> [DependencyNode] { 37 | 38 | var nodes = [DependencyNode]() 39 | 40 | for (row, g) in groups.enumerated() { 41 | 42 | var x = kRelationViewPadding 43 | let y = kRelationViewPadding + row * (kRelationItemHeight + kRelationVerticalSpacing) 44 | for elt in g { 45 | 46 | let width = widthForItem(elt.key) 47 | let color = randomColor() 48 | let node = DependencyNode(name: elt.key, 49 | color: color.hex, 50 | frame: NodeFrame(x: x, y: y, width: width, height: kRelationItemHeight), 51 | sons: elt.value) 52 | nodes.append(node) 53 | x += width + kRelationHorizentalSpacing 54 | } 55 | } 56 | 57 | return nodes 58 | } 59 | 60 | extension PrettyRelation { 61 | 62 | convenience init(dependency: [String: [String]]) { 63 | 64 | let groups = Array(groupPodDependency(dependency).reversed()) 65 | // let size = preferredSize(groups) 66 | self.init(nodes: nodesForGroups(groups)) 67 | } 68 | } 69 | 70 | 71 | extension PrettyRelation { 72 | 73 | var preferredSize: CGSize { 74 | 75 | let (width, height) = nodes.reduce((0, 0)) { (result, node) in 76 | 77 | return (max(result.0, node.frame.x + node.frame.width), 78 | max(result.1, node.frame.y + node.frame.height)) 79 | } 80 | return CGSize(width: width + kRelationViewPadding, height: height + kRelationViewPadding) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /two/Pretty/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeExtensions 11 | 12 | lock 13 | 14 | CFBundleTypeName 15 | Podfile 16 | CFBundleTypeRole 17 | Viewer 18 | NSDocumentClass 19 | NSDocument 20 | 21 | 22 | CFBundleTypeExtensions 23 | 24 | tree 25 | 26 | CFBundleTypeIconFile 27 | 28 | CFBundleTypeName 29 | pretty 30 | CFBundleTypeRole 31 | Editor 32 | NSDocumentClass 33 | NSDocument 34 | 35 | 36 | CFBundleExecutable 37 | $(EXECUTABLE_NAME) 38 | CFBundleIconFile 39 | 40 | CFBundleIdentifier 41 | $(PRODUCT_BUNDLE_IDENTIFIER) 42 | CFBundleInfoDictionaryVersion 43 | 6.0 44 | CFBundleName 45 | $(PRODUCT_NAME) 46 | CFBundlePackageType 47 | APPL 48 | CFBundleShortVersionString 49 | 1.0 50 | CFBundleVersion 51 | 1 52 | LSMinimumSystemVersion 53 | $(MACOSX_DEPLOYMENT_TARGET) 54 | NSHumanReadableCopyright 55 | Copyright © 2018年 Octree. All rights reserved. 56 | NSMainStoryboardFile 57 | Main 58 | NSPrincipalClass 59 | NSApplication 60 | 61 | 62 | -------------------------------------------------------------------------------- /two/Pretty/Model/DependencyNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DependencyNode.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | struct NodeFrame: Codable { 13 | 14 | var x: Int 15 | var y: Int 16 | var width: Int 17 | var height: Int 18 | } 19 | 20 | extension NodeFrame { 21 | 22 | var rect: NSRect { 23 | NSRect(x: x, y: y, width: width, height: height) 24 | } 25 | } 26 | 27 | class DependencyNode: Codable { 28 | 29 | var name: String 30 | var color: String 31 | var frame: NodeFrame 32 | var sons: [String] 33 | 34 | init(name: String, color: String, frame: NodeFrame, sons: [String]) { 35 | 36 | self.name = name 37 | self.color = color 38 | self.frame = frame 39 | self.sons = sons 40 | } 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /two/Pretty/Model/PrettyRelation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepettyRelation.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class PrettyRelation: Codable { 12 | 13 | var nodes: [DependencyNode] 14 | 15 | init(nodes: [DependencyNode]) { 16 | 17 | self.nodes = nodes 18 | } 19 | } 20 | 21 | extension PrettyRelation { 22 | 23 | func jsonData() throws -> Data { 24 | 25 | let encoder = JSONEncoder() 26 | do { 27 | return try encoder.encode(self) 28 | } catch { 29 | throw error 30 | } 31 | } 32 | 33 | func jsonString() throws -> String { 34 | 35 | do { 36 | return try String(data: jsonData(), encoding: .utf8)! 37 | } catch { 38 | throw error 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /two/Pretty/Parser/Parser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Tag{ 12 | let pageStart = "PODS:\n" 13 | let lineEnd: Character = "\n" 14 | let pageEnd: String 15 | let itemStart = String(repeating: " ", count: 2) 16 | let subItemStart = String(repeating: " ", count: 4) 17 | init() { 18 | pageEnd = String(repeating: "\(lineEnd)", count: 2) 19 | } 20 | } 21 | 22 | 23 | struct Parser { 24 | 25 | func parse(_ content: String) -> [String: [String]]?{ 26 | 27 | let tag = Tag() 28 | guard content.hasPrefix(tag.pageStart) else { 29 | return nil 30 | } 31 | let info = content.rm(header: tag.pageStart) 32 | guard let temp = info.components(separatedBy: tag.pageEnd).first else{ 33 | return nil 34 | } 35 | var result = [String: [String]]() 36 | let list = temp.split(separator: tag.lineEnd) 37 | let cnt = list.count 38 | var i = 0 39 | while i < cnt { 40 | var tmp = String(list[i]) 41 | var key: String? 42 | var vals = [String]() 43 | if tmp.isItem{ 44 | key = tmp.rm(header: tag.itemStart).word 45 | i += 1 46 | } 47 | var stay = true 48 | while stay, i < cnt { 49 | tmp = String(list[i]) 50 | if tmp.isSubitem{ 51 | vals.append(tmp.rm(header: tag.subItemStart).word) 52 | i += 1 53 | } 54 | else{ 55 | stay = false 56 | } 57 | } 58 | if let k = key{ 59 | result[k] = vals 60 | } 61 | else{ 62 | return nil 63 | } 64 | } 65 | 66 | if result.isEmpty{ 67 | return nil 68 | } 69 | else{ 70 | return result 71 | } 72 | } 73 | } 74 | 75 | 76 | 77 | extension String{ 78 | 79 | var isItem: Bool{ 80 | let tag = Tag() 81 | return hasPrefix(tag.itemStart) && (hasPrefix(tag.subItemStart) == false) 82 | } 83 | 84 | var isSubitem: Bool{ 85 | let tag = Tag() 86 | return hasPrefix(tag.subItemStart) 87 | } 88 | 89 | 90 | var word: String{ 91 | rmRegexHeader.regexExtract 92 | } 93 | 94 | 95 | func rm(header str: String) -> String{ 96 | return String(dropFirst(str.count)) 97 | } 98 | 99 | 100 | var rmRegexHeader: String{ 101 | if let tmp = match(regex: #"- "?"#){ 102 | return String(dropFirst(tmp.count)) 103 | } 104 | else{ 105 | return self 106 | } 107 | } 108 | 109 | 110 | var regexExtract: String{ 111 | if let tmp = match(regex: #"[^\s]+"#){ 112 | return tmp 113 | } 114 | else{ 115 | return self 116 | } 117 | } 118 | 119 | func match(regex: String) -> String? { 120 | guard let regex = try? NSRegularExpression(pattern: regex) else { 121 | return nil 122 | } 123 | let results = regex.matches(in: self, range: NSRange(location: 0, length: utf8.count)) 124 | let nsString = self as NSString 125 | if let first = results.first{ 126 | return nsString.substring(with: first.range) 127 | } 128 | else{ 129 | return nil 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /two/Pretty/Pretty.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.assets.movies.read-write 8 | 9 | com.apple.security.assets.music.read-write 10 | 11 | com.apple.security.assets.pictures.read-write 12 | 13 | com.apple.security.files.downloads.read-write 14 | 15 | com.apple.security.files.user-selected.read-write 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /two/Pretty/RandomColors/ColorDefinition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorDefinition.swift 3 | // RandomColorSwift 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import Foundation 26 | 27 | typealias Range = (min: Int, max: Int) 28 | 29 | struct ColorDefinition { 30 | let hueRange: Range? 31 | let lowerBounds: [Range] 32 | 33 | lazy var saturationRange: Range = { 34 | let sMin = self.lowerBounds[0].0 35 | let sMax = self.lowerBounds[self.lowerBounds.count - 1].0 36 | return (sMin, sMax) 37 | }() 38 | 39 | lazy var brightnessRange: Range = { 40 | let bMin = self.lowerBounds[self.lowerBounds.count - 1].1 41 | let bMax = self.lowerBounds[0].1 42 | return (bMin, bMax) 43 | }() 44 | 45 | init(hueRange: Range?, lowerBounds: [Range]) { 46 | self.hueRange = hueRange 47 | self.lowerBounds = lowerBounds 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /two/Pretty/RandomColors/Hue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Hue.swift 3 | // RandomColorSwift 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public enum Hue { 28 | 29 | case monochrome, red, orange, yellow, green, blue, purple, pink 30 | case value(Int) 31 | case random 32 | 33 | public func toInt() -> Int { 34 | switch self { 35 | case .monochrome: return 1 36 | case .red: return 2 37 | case .orange: return 3 38 | case .yellow: return 4 39 | case .green: return 5 40 | case .blue: return 6 41 | case .purple: return 7 42 | case .pink: return 8 43 | case .value(_): return -1 44 | case .random: return 0 45 | } 46 | } 47 | } 48 | 49 | public func == (lhs: Hue, rhs: Hue) -> Bool { 50 | return lhs.toInt() == rhs.toInt() 51 | } 52 | 53 | extension Hue: Hashable { 54 | public var hashValue: Int { 55 | get { 56 | return self.toInt() 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /two/Pretty/RandomColors/Luminosity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Luminosity.swift 3 | // RandomColorSwift 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public enum Luminosity: Int { 28 | case bright, light, dark 29 | case random 30 | } 31 | -------------------------------------------------------------------------------- /two/Pretty/RandomColors/RandomColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RandomColor.h 3 | // RandomColor 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | #import 26 | 27 | //! Project version number for RandomColor. 28 | FOUNDATION_EXPORT double RandomColorVersionNumber; 29 | 30 | //! Project version string for RandomColor. 31 | FOUNDATION_EXPORT const unsigned char RandomColorVersionString[]; 32 | 33 | // In this header, you should import all the public headers of your framework using statements like #import 34 | 35 | 36 | -------------------------------------------------------------------------------- /two/Pretty/RandomColors/RandomColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RandomColor.swift 3 | // RandomColorSwift 4 | // 5 | // Copyright (c) 2016 Wei Wang (http://onevcat.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | #if os(iOS) 26 | import UIKit 27 | public typealias Color = UIColor 28 | #else 29 | import Cocoa 30 | public typealias Color = NSColor 31 | #endif 32 | 33 | private var colorDictionary: [Hue: ColorDefinition] = [ 34 | .monochrome: ColorDefinition(hueRange: nil, lowerBounds: [(0,0), (100,0)]), 35 | .red: ColorDefinition(hueRange: (-26,18), lowerBounds: [(20,100), (30,92), (40,89), (50,85), (60,78), (70,70), (80,60), (90,55), (100,50)]), 36 | .orange: ColorDefinition(hueRange: (19,46), lowerBounds: [(20,100), (30,93), (40,88), (50,86), (60,85), (70,70), (100,70)]), 37 | .yellow: ColorDefinition(hueRange: (47,62), lowerBounds: [(25,100), (40,94), (50,89), (60,86), (70,84), (80,82), (90,80), (100,75)]), 38 | .green: ColorDefinition(hueRange: (63,178), lowerBounds: [(30,100), (40,90), (50,85), (60,81), (70,74), (80,64), (90,50), (100,40)]), 39 | .blue: ColorDefinition(hueRange: (179,257), lowerBounds: [(20,100), (30,86), (40,80), (50,74), (60,60), (70,52), (80,44), (90,39), (100,35)]), 40 | .purple: ColorDefinition(hueRange: (258, 282), lowerBounds: [(20,100), (30,87), (40,79), (50,70), (60,65), (70,59), (80,52), (90,45), (100,42)]), 41 | .pink: ColorDefinition(hueRange: (283, 334), lowerBounds: [(20,100), (30,90), (40,86), (60,84), (80,80), (90,75), (100,73)]) 42 | ] 43 | 44 | extension Hue { 45 | var range: Range { 46 | switch self { 47 | case .value(let value): return (value, value) 48 | case .random: return (0, 360) 49 | default: 50 | if let colorDefinition = colorDictionary[self] { 51 | return colorDefinition.hueRange ?? (0, 360) 52 | } else { 53 | assert(false, "Unrecgonized Hue enum: \(self).") 54 | return (0, 360) 55 | } 56 | } 57 | } 58 | } 59 | 60 | /** 61 | Generate a single random color with some conditions. 62 | 63 | - parameter hue: Hue of target color. It will be the main property of the generated color. Default is .Random. 64 | - parameter luminosity: Luminosity of target color. It will decide the brightness of generated color. Default is .Random. 65 | 66 | - returns: A random color following input conditions. It will be a `UIColor` object for iOS target, and an `NSColor` object for OSX target. 67 | */ 68 | public func randomColor(hue: Hue = .random, luminosity: Luminosity = .random) -> Color { 69 | 70 | func random(in range: Range) -> Int { 71 | assert(range.max >= range.min, "Max in range should be greater than min") 72 | return Int(arc4random_uniform(UInt32(range.max - range.min))) + range.min 73 | } 74 | 75 | func getColorDefinition(hueValue: Int) -> ColorDefinition { 76 | var hueValue = hueValue 77 | 78 | if hueValue >= 334 && hueValue <= 360 { 79 | hueValue -= 360 80 | } 81 | 82 | let color = colorDictionary.values.filter({ (definition: ColorDefinition) -> Bool in 83 | if let hueRange = definition.hueRange { 84 | return hueValue >= hueRange.min && hueValue <= hueRange.max 85 | } else { 86 | return false 87 | } 88 | }) 89 | 90 | assert(color.count == 1, "There should one and only one color satisfied the filter") 91 | return color.first! 92 | } 93 | 94 | func pickHue(_ hue: Hue) -> Int { 95 | var hueValue = random(in: hue.range) 96 | 97 | // Instead of storing red as two seperate ranges, 98 | // we group them, using negative numbers 99 | if hueValue < 0 { 100 | hueValue = hueValue + 360 101 | } 102 | return hueValue 103 | } 104 | 105 | func pickSaturation(color: ColorDefinition, hue: Hue, luminosity: Luminosity) -> Int { 106 | var color = color 107 | 108 | if luminosity == .random { 109 | return random(in: (0, 100)) 110 | } 111 | 112 | if hue == .monochrome { 113 | return 0 114 | } 115 | 116 | let saturationRange = color.saturationRange 117 | var sMin = saturationRange.min 118 | var sMax = saturationRange.max 119 | 120 | switch luminosity { 121 | case .bright: 122 | sMin = 55 123 | case .dark: 124 | sMin = sMax - 10 125 | case .light: 126 | sMax = 55 127 | default: () 128 | } 129 | 130 | return random(in: (sMin, sMax)) 131 | } 132 | 133 | func pickBrightness(color: ColorDefinition, saturationValue: Int, luminosity: Luminosity) -> Int { 134 | func getMinimumBrightness(saturationValue: Int) -> Int { 135 | let lowerBounds = color.lowerBounds; 136 | for i in 0 ..< lowerBounds.count - 1 { 137 | 138 | let s1 = Float(lowerBounds[i].0) 139 | let v1 = Float(lowerBounds[i].1) 140 | 141 | let s2 = Float(lowerBounds[i+1].0) 142 | let v2 = Float(lowerBounds[i+1].1) 143 | 144 | if Float(saturationValue) >= s1 && Float(saturationValue) <= s2 { 145 | let m = (v2 - v1) / (s2 - s1) 146 | let b = v1 - m * s1 147 | return lroundf(m * Float(saturationValue) + b) 148 | } 149 | } 150 | return 0 151 | } 152 | 153 | var bMin = getMinimumBrightness(saturationValue: saturationValue) 154 | var bMax = 100 155 | 156 | switch luminosity { 157 | case .dark: 158 | bMax = bMin + 20 159 | case .light: 160 | bMin = (bMax + bMin) / 2 161 | case .random: 162 | bMin = 0 163 | bMax = 100 164 | default: () 165 | } 166 | 167 | return random(in: (bMin, bMax)) 168 | } 169 | 170 | 171 | let hueValue = pickHue(hue) 172 | 173 | let color = getColorDefinition(hueValue: hueValue) 174 | 175 | let saturationValue = pickSaturation(color: color, hue: hue, luminosity: luminosity) 176 | let brightnessValue = pickBrightness(color: color, saturationValue: saturationValue, luminosity: luminosity) 177 | 178 | return Color(deviceHue: CGFloat(hueValue) / 360.0, 179 | saturation: CGFloat(saturationValue) / 100.0, 180 | brightness: CGFloat(brightnessValue) / 100.0, 181 | alpha: 1.0) 182 | } 183 | 184 | /** 185 | Generate a set of random colors with some conditions. 186 | 187 | - parameter count: The count of how many colors will be generated. 188 | - parameter hue: Hue of target color. It will be the main property of the generated color. Default is .Random. 189 | - parameter luminosity: Luminosity of target color. It will decide the brightness of generated color. Default is .Random. 190 | 191 | - returns: An array of random colors following input conditions. The elements will be `UIColor` objects for iOS target, and `NSColor` objects for OSX target. 192 | */ 193 | public func randomColors(count: Int, hue: Hue = .random, luminosity: Luminosity = .random) -> [Color] { 194 | var colors: [Color] = [] 195 | while (colors.count < count) { 196 | colors.append(randomColor(hue: hue, luminosity: luminosity)) 197 | } 198 | return colors 199 | } 200 | 201 | -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coyingcat/podfile_lock_resolution/33e1e2b5674dda74613466127a0cc643b76635ca/two/Pretty/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /two/Pretty/Resource/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /two/Pretty/Utils/ColorDifference.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorDifference.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import GLKit 10 | 11 | extension GLKVector3 { 12 | 13 | func unpack() -> (Float, Float, Float) { 14 | 15 | return (x, y, z) 16 | } 17 | } 18 | 19 | private func C(_ a: Float, b: Float) -> Float { 20 | return sqrt(pow(a, 2) + pow(b, 2)) 21 | } 22 | 23 | // From http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html 24 | public func CIE2000SquaredColorDifference( 25 | _ kL: Float = 1, 26 | kC: Float = 1, 27 | kH: Float = 1 28 | ) -> (_ lab1:GLKVector3, _ lab2:GLKVector3) -> Float { 29 | 30 | return { (lab1:GLKVector3, lab2:GLKVector3) -> Float in 31 | let (L1, a1, b1) = lab1.unpack() 32 | let (L2, a2, b2) = lab2.unpack() 33 | 34 | let ΔLp = L2 - L1 35 | let Lbp = (L1 + L2) / 2 36 | 37 | let (C1, C2) = (C(a1, b: b1), C(a2, b: b2)) 38 | let Cb = (C1 + C2) / 2 39 | 40 | let G = (1 - sqrt(pow(Cb, 7) / (pow(Cb, 7) + pow(25, 7)))) / 2 41 | let ap: (Float) -> Float = { a in 42 | return a * (1 + G) 43 | } 44 | let (a1p, a2p) = (ap(a1), ap(a2)) 45 | 46 | let (C1p, C2p) = (C(a1p, b: b1), C(a2p, b: b2)) 47 | let ΔCp = C2p - C1p 48 | let Cbp = (C1p + C2p) / 2 49 | 50 | let hp: (Float, Float) -> Float = { ap, b in 51 | if ap == 0 && b == 0 { return 0 } 52 | let θ = GLKMathRadiansToDegrees(atan2(b, ap)) 53 | return fmod(θ < 0 ? (θ + 360) : θ, 360) 54 | } 55 | let (h1p, h2p) = (hp(a1p, b1), hp(a2p, b2)) 56 | let Δhabs = abs(h1p - h2p) 57 | let Δhp: Float = { 58 | if (C1p == 0 || C2p == 0) { 59 | return 0 60 | } else if Δhabs <= 180 { 61 | return h2p - h1p 62 | } else if h2p <= h1p { 63 | return h2p - h1p + 360 64 | } else { 65 | return h2p - h1p - 360 66 | } 67 | }() 68 | 69 | let ΔHp = 2 * sqrt(C1p * C2p) * sin(GLKMathDegreesToRadians(Δhp / 2)) 70 | let Hbp: Float = { 71 | if (C1p == 0 || C2p == 0) { 72 | return h1p + h2p 73 | } else if Δhabs > 180 { 74 | return (h1p + h2p + 360) / 2 75 | } else { 76 | return (h1p + h2p) / 2 77 | } 78 | }() 79 | 80 | var T = 1 81 | - 0.17 * cos(GLKMathDegreesToRadians(Hbp - 30)) 82 | + 0.24 * cos(GLKMathDegreesToRadians(2 * Hbp)) 83 | 84 | T = T 85 | + 0.32 * cos(GLKMathDegreesToRadians(3 * Hbp + 6)) 86 | - 0.20 * cos(GLKMathDegreesToRadians(4 * Hbp - 63)) 87 | 88 | let Sl = 1 + (0.015 * pow(Lbp - 50, 2)) / sqrt(20 + pow(Lbp - 50, 2)) 89 | let Sc = 1 + 0.045 * Cbp 90 | let Sh = 1 + 0.015 * Cbp * T 91 | 92 | let Δθ = 30 * exp(-pow((Hbp - 275) / 25, 2)) 93 | let Rc = 2 * sqrt(pow(Cbp, 7) / (pow(Cbp, 7) + pow(25, 7))) 94 | let Rt = -Rc * sin(GLKMathDegreesToRadians(2 * Δθ)) 95 | 96 | let Lterm = ΔLp / (kL * Sl) 97 | let Cterm = ΔCp / (kC * Sc) 98 | let Hterm = ΔHp / (kH * Sh) 99 | return pow(Lterm, 2) + pow(Cterm, 2) + pow(Hterm, 2) + Rt * Cterm * Hterm 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /two/Pretty/Utils/ColorSpaceConversion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorSpaceConversion.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/7. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import GLKit 10 | 11 | // From https://github.com/indragiek/DominantColor 12 | 13 | 14 | func RGBToSRGB(_ rgbVector: GLKVector3) -> GLKVector3 { 15 | #if TARGET_OS_IPHONE 16 | // sRGB is the native device color space on iOS, no conversion is required. 17 | return rgbVector 18 | #else 19 | let color = NSColor(deviceRed: CGFloat(rgbVector.x), green: CGFloat(rgbVector.y), blue: CGFloat(rgbVector.z), alpha: 1.0) 20 | let srgbColor = color.usingColorSpace(.sRGB) 21 | return GLKVector3Make(Float(srgbColor?.redComponent ?? 0), 22 | Float(srgbColor?.greenComponent ?? 0), 23 | Float(srgbColor?.blueComponent ?? 0)) 24 | #endif 25 | } 26 | 27 | func SRGBToRGB(_ srgbVector: GLKVector3) -> GLKVector3 { 28 | #if TARGET_OS_IPHONE 29 | // sRGB is the native device color space on iOS, no conversion is required. 30 | return srgbVector 31 | #else 32 | let components = [ CGFloat(srgbVector.x), CGFloat(srgbVector.y), CGFloat(srgbVector.z), 1.0 ] 33 | let srgbColor = NSColor(colorSpace: .sRGB, components: components, count: 4) 34 | let rgbColor = srgbColor.usingColorSpace(.deviceRGB) 35 | return GLKVector3Make(Float(rgbColor?.redComponent ?? 0), 36 | Float(rgbColor?.greenComponent ?? 0), 37 | Float(rgbColor?.blueComponent ?? 0)) 38 | #endif 39 | } 40 | 41 | //// http://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation 42 | // 43 | func SRGBToLinearSRGB(_ srgbVector: GLKVector3) -> GLKVector3 { 44 | 45 | let f: (Float) -> Float = { c in 46 | if (c <= 0.04045) { 47 | return c / 12.92 48 | } else { 49 | return powf((c + 0.055) / 1.055, 2.4) 50 | } 51 | } 52 | return GLKVector3Make(f(srgbVector.x), f(srgbVector.y), f(srgbVector.z)) 53 | } 54 | 55 | func LinearSRGBToSRGB(_ lSrgbVector: GLKVector3) -> GLKVector3 { 56 | 57 | let f: (Float) -> Float = { c in 58 | if (c <= 0.0031308) { 59 | return c * 12.92 60 | } else { 61 | return (1.055 * powf(c, 1.0 / 2.4)) - 0.055 62 | } 63 | } 64 | 65 | return GLKVector3Make(f(lSrgbVector.x), f(lSrgbVector.y), f(lSrgbVector.z)) 66 | } 67 | 68 | // mark: XYZ (CIE 1931) 69 | //// http://en.wikipedia.org/wiki/CIE_1931_color_space#Construction_of_the_CIE_XYZ_color_space_from_the_Wright.E2.80.93Guild_data 70 | 71 | private let LinearSRGBToXYZMatrix = GLKMatrix3Make(0.4124, 0.2126, 0.0193, 0.3576, 0.7152, 0.1192, 0.1805, 0.0722, 0.9505) 72 | 73 | func LinearSRGBToXYZ(_ linearSrgbVector: GLKVector3) -> GLKVector3 { 74 | 75 | let unscaledXYZVector = GLKMatrix3MultiplyVector3(LinearSRGBToXYZMatrix, linearSrgbVector) 76 | return GLKVector3MultiplyScalar(unscaledXYZVector, 100) 77 | } 78 | 79 | private let XYZToLinearSRGBMatrix = GLKMatrix3Make(3.2406, -0.9689, 0.0557, -1.5372, 1.8758, -0.2040, -0.4986, 0.0415, 1.0570) 80 | 81 | func XYZToLinearSRGB(_ xyzVector: GLKVector3) -> GLKVector3 { 82 | 83 | let scaledXYZVector = GLKVector3DivideScalar(xyzVector, 100) 84 | return GLKMatrix3MultiplyVector3(XYZToLinearSRGBMatrix, scaledXYZVector) 85 | } 86 | 87 | // MARK: LAB 88 | //// http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions 89 | 90 | func XYZToLAB(_ xyzVector: GLKVector3, tristimulus: GLKVector3) -> GLKVector3 { 91 | 92 | let f: (Float) -> Float = { t in 93 | 94 | if (t > powf(6 / 29, 3)) { 95 | return powf(t, 1 / 3) 96 | } else { 97 | return ((1 / 3) * powf(29 / 6, 2) * t) + (4 / 29) 98 | } 99 | } 100 | let fx = f(xyzVector.x / tristimulus.x) 101 | let fy = f(xyzVector.y / tristimulus.y) 102 | let fz = f(xyzVector.z / tristimulus.z) 103 | 104 | let l = (116 * fy) - 16 105 | let a = 500 * (fx - fy) 106 | let b = 200 * (fy - fz) 107 | 108 | return GLKVector3Make(l, a, b) 109 | } 110 | 111 | func LABToXYZ(_ labVector: GLKVector3, tristimulus: GLKVector3) -> GLKVector3 { 112 | let f: (Float) -> Float = { t in 113 | if (t > (6 / 29)) { 114 | return powf(t, 3) 115 | } else { 116 | return 3 * powf(6 / 29, 2) * (t - (4 / 29)) 117 | } 118 | } 119 | let c = (1 / 116) * (labVector.x + 16) 120 | 121 | let y = tristimulus.y * f(c) 122 | let x = tristimulus.x * f(c + ((1 / 500) * labVector.y)) 123 | let z = tristimulus.z * f(c - ((1 / 200) * labVector.z)) 124 | 125 | return GLKVector3Make(x, y, z) 126 | } 127 | 128 | //#pragma mark - Public 129 | // 130 | //// From http://www.easyrgb.com/index.php?X=MATH&H=15#text15 131 | let D65Tristimulus = GLKVector3Make(95.047, 100, 108.883) 132 | 133 | func RGBToLAB(rgbVector: GLKVector3) -> GLKVector3 { 134 | 135 | let srgbVector = RGBToSRGB(rgbVector) 136 | let lSrgbVector = SRGBToLinearSRGB(srgbVector) 137 | let xyzVector = LinearSRGBToXYZ(lSrgbVector) 138 | return XYZToLAB(xyzVector, tristimulus: D65Tristimulus) 139 | } 140 | 141 | func LABToRGB(labVector: GLKVector3) -> GLKVector3 { 142 | 143 | let xyzVector = LABToXYZ(labVector, tristimulus: D65Tristimulus) 144 | let lSrgbVector = XYZToLinearSRGB(xyzVector) 145 | let srgbVector = LinearSRGBToSRGB(lSrgbVector) 146 | return SRGBToRGB(srgbVector) 147 | } 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /two/Pretty/Utils/DependencySort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DependencySort.swift 3 | // Pretty 4 | // 5 | // Created by Octree on 2018/4/6. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// 12 | /// - Parameter dependency: 依赖 [name: Sons] 13 | /// - Returns: [name: dads] 14 | private func parentNode(_ dependency: [String: [String]]) -> [String: [String]] { 15 | 16 | var pMap = [String: [String]]() 17 | for (key, sons) in dependency { 18 | for name in sons { 19 | var parents = pMap[name] ?? [] 20 | parents.append(key) 21 | pMap[name] = parents 22 | } 23 | } 24 | 25 | return pMap 26 | } 27 | 28 | 29 | /// 根据 lib 的 depth 进行分组 30 | /// 31 | /// - Parameter dependency: pod dependency 32 | /// - Returns: grouped dependency 33 | func groupPodDependency(_ dependency: [String: [String]]) -> [[String: [String]]] { 34 | 35 | let reversed = parentNode(dependency) 36 | var names = Set(dependency.keys) 37 | 38 | var lastDepthNames = [String]() 39 | var groups = [[String: [String]]]() 40 | while names.count > 0 { 41 | 42 | var group = [String: [String]]() 43 | let copyedNames = names 44 | for name in copyedNames { 45 | if lastDepthNames.contains(reversed[name] ?? []) { 46 | 47 | names.remove(name) 48 | group[name] = dependency[name] 49 | } 50 | } 51 | lastDepthNames.append(contentsOf: group.keys) 52 | groups.append(group) 53 | } 54 | 55 | return groups 56 | } 57 | -------------------------------------------------------------------------------- /two/Pretty/Utils/HexColors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HexColors.swift 3 | // 4 | // Created by Marius Landwehr on 25.12.16. 5 | // The MIT License (MIT) 6 | // Copyright (c) 2016 Marius Landwehr marius.landwehr@gmail.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | #if os(iOS) || os(watchOS) || os(tvOS) 15 | import UIKit 16 | public typealias HexColor = UIColor 17 | #else 18 | import Cocoa 19 | public typealias HexColor = NSColor 20 | #endif 21 | 22 | 23 | public extension HexColor { 24 | typealias Hex = String 25 | 26 | convenience init?(_ hex: Hex, alpha: CGFloat? = nil) { 27 | 28 | guard let hexType = Type(from: hex), let components = hexType.components() else { 29 | return nil 30 | } 31 | 32 | self.init(calibratedRed: components.red, green: components.green, blue: components.blue, alpha: alpha ?? components.alpha) 33 | } 34 | 35 | /// The string hex value representation of the current color 36 | var hex: Hex { 37 | var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0, rgb: Int 38 | getRed(&r, green: &g, blue: &b, alpha: &a) 39 | 40 | if a == 1 { // no alpha value set, we are returning the short version 41 | rgb = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0 42 | return String(format: "#%06x", rgb) 43 | } else { 44 | rgb = (Int)(r*255)<<24 | (Int)(g*255)<<16 | (Int)(b*255)<<8 | (Int)(a*255)<<0 45 | return String(format: "#%08x", rgb) 46 | } 47 | } 48 | 49 | private enum `Type` { 50 | 51 | case RGBshort(rgb: Hex) 52 | case RGBshortAlpha(rgba: Hex) 53 | case RGB(rgb: Hex) 54 | case RGBA(rgba: Hex) 55 | 56 | init?(from hex: Hex) { 57 | 58 | var hexString = hex 59 | hexString.removeHashIfNecessary() 60 | 61 | guard let t = Type.transform(hex: hexString) else { 62 | return nil 63 | } 64 | 65 | self = t 66 | } 67 | 68 | static func transform(hex string: Hex) -> Type? { 69 | switch string.count { 70 | case 3: 71 | return .RGBshort(rgb: string) 72 | case 4: 73 | return .RGBshortAlpha(rgba: string) 74 | case 6: 75 | return .RGB(rgb: string) 76 | case 8: 77 | return .RGBA(rgba: string) 78 | default: 79 | return nil 80 | } 81 | } 82 | 83 | var value: Hex { 84 | switch self { 85 | case .RGBshort(let rgb): 86 | return rgb 87 | case .RGBshortAlpha(let rgba): 88 | return rgba 89 | case .RGB(let rgb): 90 | return rgb 91 | case .RGBA(let rgba): 92 | return rgba 93 | } 94 | } 95 | 96 | typealias rgbComponents = (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) 97 | func components() -> rgbComponents? { 98 | 99 | var hexValue: UInt32 = 0 100 | guard Scanner(string: value).scanHexInt32(&hexValue) else { 101 | return nil 102 | } 103 | 104 | let r, g, b, a, divisor: CGFloat 105 | 106 | switch self { 107 | case .RGBshort(_): 108 | divisor = 15 109 | r = CGFloat((hexValue & 0xF00) >> 8) / divisor 110 | g = CGFloat((hexValue & 0x0F0) >> 4) / divisor 111 | b = CGFloat( hexValue & 0x00F) / divisor 112 | a = 1 113 | case .RGBshortAlpha(_): 114 | divisor = 15 115 | r = CGFloat((hexValue & 0xF000) >> 12) / divisor 116 | g = CGFloat((hexValue & 0x0F00) >> 8) / divisor 117 | b = CGFloat((hexValue & 0x00F0) >> 4) / divisor 118 | a = CGFloat( hexValue & 0x000F) / divisor 119 | case .RGB(_): 120 | divisor = 255 121 | r = CGFloat((hexValue & 0xFF0000) >> 16) / divisor 122 | g = CGFloat((hexValue & 0x00FF00) >> 8) / divisor 123 | b = CGFloat( hexValue & 0x0000FF) / divisor 124 | a = 1 125 | case .RGBA(_): 126 | divisor = 255 127 | r = CGFloat((hexValue & 0xFF000000) >> 24) / divisor 128 | g = CGFloat((hexValue & 0x00FF0000) >> 16) / divisor 129 | b = CGFloat((hexValue & 0x0000FF00) >> 8) / divisor 130 | a = CGFloat( hexValue & 0x000000FF) / divisor 131 | } 132 | 133 | return (red: r, green: g, blue: b, alpha: a) 134 | } 135 | } 136 | } 137 | 138 | private extension String { 139 | 140 | mutating func removeHashIfNecessary() { 141 | if hasPrefix("#") { 142 | self = replacingOccurrences(of: "#", with: "") 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /two/Pretty/View/RelationItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelationItemView.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class RelationItemView: NSView { 12 | 13 | var text = "" { 14 | didSet { 15 | label.stringValue = text 16 | } 17 | } 18 | private(set) var label: NSTextField = { 19 | let textfield = NSTextField() 20 | textfield.isEditable = false 21 | textfield.textColor = .white 22 | textfield.alignment = .center 23 | textfield.isBordered = false 24 | return textfield 25 | }() 26 | 27 | var backgroundColor: NSColor? { 28 | didSet { 29 | layer?.backgroundColor = backgroundColor?.cgColor 30 | label.backgroundColor = backgroundColor 31 | } 32 | } 33 | 34 | init(frame frameRect: NSRect, text: String) { 35 | super.init(frame: frameRect) 36 | self.text = text 37 | addSubview(label) 38 | label.stringValue = text 39 | wantsLayer = true 40 | layer?.cornerRadius = 10 41 | layer?.masksToBounds = true 42 | } 43 | 44 | required init?(coder decoder: NSCoder) { 45 | fatalError("init(coder:) has not been implemented") 46 | } 47 | 48 | override func draw(_ dirtyRect: NSRect) { 49 | super.draw(dirtyRect) 50 | } 51 | 52 | override func layout() { 53 | super.layout() 54 | label.frame = bounds.insetBy(dx: 5, dy: 5) 55 | } 56 | 57 | var center: NSPoint { 58 | NSPoint(x: frame.origin.x + frame.size.width / 2, 59 | y: frame.origin.y + frame.size.height / 2) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /two/Pretty/View/RelationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelationView.swift 3 | // MacApp 4 | // 5 | // Created by Octree on 2018/4/5. 6 | // Copyright © 2018年 Octree. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | class RelationView: NSView { 13 | 14 | // MARK: Accessor 15 | 16 | /// "parent|daughter": Layer 17 | private var lineMap = [String: CAShapeLayer]() 18 | private var itemMap = [String: RelationItemView]() 19 | private var nodeMap = [String: DependencyNode]() 20 | private var currentDraggingItem: RelationItemView? = nil 21 | 22 | private var lastDragPosition = NSPoint(x: 0, y: 0) 23 | 24 | var prettyRelation = PrettyRelation(nodes: []) { 25 | didSet { 26 | setUp() 27 | } 28 | } 29 | 30 | // MARK: Life Cycle 31 | 32 | init() { 33 | super.init(frame:.zero) 34 | } 35 | 36 | required init?(coder decoder: NSCoder) { 37 | super.init(coder: decoder) 38 | } 39 | 40 | 41 | // MARK: Public Method 42 | 43 | override func mouseDown(with event: NSEvent) { 44 | 45 | let position = convert(event.locationInWindow, from: nil) 46 | 47 | if let item = findItemLocate(in: position) { 48 | 49 | currentDraggingItem = item 50 | lastDragPosition = position 51 | } 52 | } 53 | 54 | override func mouseDragged(with event: NSEvent) { 55 | 56 | if let item = currentDraggingItem { 57 | 58 | let position = convert(event.locationInWindow, from: nil) 59 | item.frame = item.frame.offset(position.minus(lastDragPosition)) 60 | 61 | lastDragPosition = position 62 | updateLine(relate: item.text) 63 | } 64 | } 65 | 66 | override func mouseUp(with event: NSEvent) { 67 | 68 | guard let item = currentDraggingItem else { 69 | return 70 | } 71 | 72 | if let node = nodeMap[item.text] { 73 | 74 | node.frame = NodeFrame(x: Int(item.frame.origin.x), 75 | y: Int(item.frame.origin.y), 76 | width: Int(item.frame.size.width), 77 | height: Int(item.frame.size.height)) 78 | } 79 | 80 | currentDraggingItem = nil 81 | updateLine(relate: item.text) 82 | } 83 | 84 | 85 | // MARK: Private Method 86 | 87 | private func setUp() { 88 | 89 | clear() 90 | let superSize = superview?.frame.size ?? CGSize() 91 | 92 | let size = prettyRelation.preferredSize 93 | 94 | frame = NSRect(x: 0, y: 0, width: max(size.width, superSize.width), 95 | height: max(superSize.height, size.height)) 96 | 97 | wantsLayer = true 98 | layer?.backgroundColor = NSColor.white.cgColor 99 | 100 | setupItems() 101 | setupLines() 102 | 103 | lineMap.forEach { 104 | layer?.addSublayer($0.1) 105 | } 106 | 107 | itemMap.forEach { 108 | addSubview($0.1) 109 | } 110 | } 111 | 112 | 113 | /// 创建 RelationItemViews 114 | private func setupItems() { 115 | 116 | prettyRelation.nodes.forEach { node in 117 | 118 | let item = RelationItemView(frame: node.frame.rect, text: node.name) 119 | let color = NSColor(node.color) 120 | item.backgroundColor = color 121 | 122 | if (color?.colorDifference(.white) ?? 0) < 0.1 { 123 | 124 | item.label.textColor = NSColor(white: 0.2, alpha: 1.0) 125 | } 126 | 127 | itemMap[node.name] = item 128 | nodeMap[node.name] = node 129 | } 130 | } 131 | 132 | 133 | /// 创建 Node 之间的连接线 134 | private func setupLines() { 135 | 136 | prettyRelation.nodes.forEach { parent in 137 | 138 | let color = NSColor(parent.color) 139 | parent.sons.forEach { son in 140 | 141 | let key = parent.name + "|" + son 142 | let shapeLayer = CAShapeLayer() 143 | shapeLayer.fillColor = NSColor.clear.cgColor 144 | shapeLayer.strokeColor = color?.half.cgColor 145 | shapeLayer.lineWidth = 2 146 | shapeLayer.lineDashPattern = [4, 4] 147 | shapeLayer.path = linePath(parent: parent.name, son: son) 148 | lineMap[key] = shapeLayer 149 | } 150 | } 151 | } 152 | 153 | private func clear() { 154 | 155 | lineMap.removeAll() 156 | itemMap.removeAll() 157 | nodeMap.removeAll() 158 | subviews.forEach { $0.removeFromSuperview() } 159 | layer?.sublayers?.forEach { $0.removeFromSuperlayer() } 160 | } 161 | 162 | 163 | /// 根据 Position 找到对应位置的 RelationItemView 164 | /// 165 | /// - Parameter position: NSPoint 166 | /// - Returns: RelationItemView or nil 167 | private func findItemLocate(in position: NSPoint) -> RelationItemView? { 168 | 169 | for (_, item) in itemMap { 170 | if item.frame.contains(position) { 171 | return item 172 | } 173 | } 174 | return nil 175 | } 176 | 177 | 178 | /// 根据父节点和子节点的名称创建连接线的 Path 179 | /// 180 | /// - Parameters: 181 | /// - parent: 父节点名称 182 | /// - son: 子节点名称 183 | /// - Returns: CGPath 184 | private func linePath(parent: String, son: String) -> CGPath { 185 | 186 | let parentItem = itemMap[parent]! 187 | let sonItem = itemMap[son]! 188 | 189 | let path = CGMutablePath() 190 | path.move(to: parentItem.center) 191 | path.addLine(to: sonItem.center) 192 | return path 193 | } 194 | 195 | 196 | /// 更新和某个节点相关的线 197 | /// 198 | /// - Parameter name: 节点名称 199 | private func updateLine(relate name: String) { 200 | 201 | for (key, value) in lineMap { 202 | 203 | let components = key.components(separatedBy: "|") 204 | if components.contains(name) { 205 | 206 | value.path = linePath(parent: components.first!, son: components.last!) 207 | 208 | if components.first == currentDraggingItem?.text { 209 | value.lineDashPattern = nil 210 | value.strokeColor = NSColor.red.cgColor 211 | } else if (components.last == currentDraggingItem?.text) { 212 | value.lineDashPattern = nil 213 | value.strokeColor = NSColor.blue.cgColor 214 | } else { 215 | value.lineDashPattern = [4, 4] 216 | value.strokeColor = itemMap[components.first!]?.backgroundColor?.half.cgColor 217 | } 218 | } 219 | } 220 | 221 | } 222 | 223 | } 224 | 225 | 226 | 227 | extension NSColor{ 228 | var half: NSColor{ 229 | withAlphaComponent(0.4) 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /结构.md: -------------------------------------------------------------------------------- 1 | 2 | Parser 种类挺多的: 3 | 4 | 有 `String`, 5 | 6 | `Character`, 7 | 8 | 9 | `(String, [String])` 10 | 11 | 12 |
13 | 14 | 15 | 数据结构是,原材料 16 | 17 | 操作,就是做饭 18 | 19 | 20 |
21 | 22 | 23 | 24 | 没有魔法, 25 | 26 | 函数式编程, 27 | 28 | 东搞西搞, 29 | 30 | 也是为了匹配,已经定义好了的,函数格式 31 | --------------------------------------------------------------------------------