├── .github ├── actions │ └── setup │ │ └── action.yml └── workflows │ └── main.yml ├── .gitignore ├── CGPathIntersection.podspec ├── CGPathIntersection.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── CGPathIntersection-iOS.xcscheme │ ├── CGPathIntersection-macOS.xcscheme │ └── CGPathIntersection-tvOS.xcscheme ├── CGPathIntersection ├── Array+CoalescePoints.swift ├── CGPath+Intersections.swift ├── CGPathImage+RawPixels.swift ├── CGPathImage.swift ├── Info.plist └── PlatformImage.swift ├── CGPathIntersectionTests ├── CGPathIntersectionTests.swift └── Info.plist ├── Gemfile ├── LICENSE ├── Mintfile ├── Package.swift ├── README.md ├── Rakefile └── images ├── roundabout.jpg ├── streets 2.gif └── streets.gif /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup the iOS CI Environment 3 | inputs: 4 | xcode: 5 | description: The version of Xcode to select 6 | install-mint: 7 | description: Whether to install mint 8 | default: true 9 | runs: 10 | using: composite 11 | steps: 12 | - name: Select Xcode ${{ inputs.xcode }} 13 | run: sudo xcode-select --switch /Applications/Xcode_${{ inputs.xcode }}.app 14 | if: ${{ inputs.xcode }} 15 | shell: bash 16 | 17 | - name: Install Mint via Homebrew 18 | run: brew install mint 19 | if: ${{ inputs.install-mint == 'true' }} 20 | shell: bash 21 | 22 | - name: Install Ruby Gems 23 | run: bundle install 24 | shell: bash 25 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build-package: 11 | name: "Build Package" 12 | runs-on: macos-latest 13 | strategy: 14 | matrix: 15 | xcode: 16 | - '12.4' # Swift 5.3 17 | - '12.5.1' # Swift 5.4 18 | - '13.2' # Swift 5.5 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: ./.github/actions/setup 22 | with: 23 | xcode: ${{ matrix.xcode }} 24 | - name: Build Package 25 | run: bundle exec rake build:iOS 26 | 27 | test-package-iOS: 28 | name: "Test Package (iOS)" 29 | runs-on: macos-latest 30 | steps: 31 | - uses: actions/checkout@v2 32 | - uses: ./.github/actions/setup 33 | - name: Test Package 34 | run: bundle exec rake test:iOS 35 | 36 | test-package-macOS: 37 | name: "Test Package (macOS)" 38 | runs-on: macos-latest 39 | steps: 40 | - uses: actions/checkout@v2 41 | - uses: ./.github/actions/setup 42 | - name: Test Package 43 | run: bundle exec rake test:macOS 44 | 45 | test-package-tvOS: 46 | name: "Test Package (tvOS)" 47 | runs-on: macos-latest 48 | steps: 49 | - uses: actions/checkout@v2 50 | - uses: ./.github/actions/setup 51 | - name: Test Package 52 | run: bundle exec rake test:tvOS 53 | 54 | cocoapod: 55 | name: "Lint CocoaPods podspec" 56 | runs-on: macos-latest 57 | steps: 58 | - uses: actions/checkout@v2 59 | - uses: ./.github/actions/setup 60 | with: 61 | install-mint: false 62 | - name: Lint CocoaPod Podspec 63 | run: bundle exec rake lint:podspec 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .build/* 3 | .swiftpm/* 4 | Gemfile.lock 5 | **/xcuserdata/* -------------------------------------------------------------------------------- /CGPathIntersection.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.swift_version = '5.1' 3 | s.ios.deployment_target = '10.0' 4 | s.osx.deployment_target = '10.10' 5 | s.tvos.deployment_target = '10.0' 6 | s.name = "CGPathIntersection" 7 | s.version = "4.0" 8 | s.summary = "An iOS library that identifies points where two CGPaths intersect" 9 | s.homepage = "https://github.com/calda/CGPathIntersection" 10 | s.license = { :type => 'MIT', :file => 'LICENSE' } 11 | s.author = { "Cal Stephens" => "cal@calstephens.tech" } 12 | s.source = { :git => 'https://github.com/calda/CGPathIntersection.git', :tag => '4.0' } 13 | s.source_files = 'CGPathIntersection/*.swift' 14 | s.exclude_files = 'README.md', 'LICENSE.md', 'CGPathIntersection.podspec', 'images/*' 15 | s.requires_arc = true 16 | end 17 | -------------------------------------------------------------------------------- /CGPathIntersection.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2E34FA44279C86FE00A735F9 /* CGPathImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF6981DD8D5F000EA383E /* CGPathImage.swift */; }; 11 | 2E34FA45279C86FE00A735F9 /* CGPath+Intersections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF69A1DD8E03200EA383E /* CGPath+Intersections.swift */; }; 12 | 2E34FA46279C86FE00A735F9 /* CGPathImage+RawPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF69E1DD8F6BD00EA383E /* CGPathImage+RawPixels.swift */; }; 13 | 2E34FA47279C86FE00A735F9 /* Array+CoalescePoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF6A01DD8FFE400EA383E /* Array+CoalescePoints.swift */; }; 14 | 2E34FA50279C874C00A735F9 /* CGPathIntersection.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E34FA3D279C86F400A735F9 /* CGPathIntersection.framework */; }; 15 | 2E34FA56279C875100A735F9 /* CGPathIntersectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF68C1DD8D48800EA383E /* CGPathIntersectionTests.swift */; }; 16 | 2E34FA58279CC25000A735F9 /* PlatformImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E34FA57279CC25000A735F9 /* PlatformImage.swift */; }; 17 | 2E34FA59279CC39C00A735F9 /* PlatformImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E34FA57279CC25000A735F9 /* PlatformImage.swift */; }; 18 | 2EBDE01B27A5BCD3007A1031 /* CGPathIntersection.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2EBDE01327A5BCD3007A1031 /* CGPathIntersection.framework */; }; 19 | 2EBDE02827A5BCE0007A1031 /* CGPathIntersectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF68C1DD8D48800EA383E /* CGPathIntersectionTests.swift */; }; 20 | 2EBDE02927A5BCE3007A1031 /* Array+CoalescePoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF6A01DD8FFE400EA383E /* Array+CoalescePoints.swift */; }; 21 | 2EBDE02A27A5BCE3007A1031 /* PlatformImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E34FA57279CC25000A735F9 /* PlatformImage.swift */; }; 22 | 2EBDE02B27A5BCE3007A1031 /* CGPath+Intersections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF69A1DD8E03200EA383E /* CGPath+Intersections.swift */; }; 23 | 2EBDE02C27A5BCE3007A1031 /* CGPathImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF6981DD8D5F000EA383E /* CGPathImage.swift */; }; 24 | 2EBDE02D27A5BCE3007A1031 /* CGPathImage+RawPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF69E1DD8F6BD00EA383E /* CGPathImage+RawPixels.swift */; }; 25 | 2EFEF6881DD8D48800EA383E /* CGPathIntersection.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2EFEF67E1DD8D48800EA383E /* CGPathIntersection.framework */; }; 26 | 2EFEF68D1DD8D48800EA383E /* CGPathIntersectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF68C1DD8D48800EA383E /* CGPathIntersectionTests.swift */; }; 27 | 2EFEF6991DD8D5F000EA383E /* CGPathImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF6981DD8D5F000EA383E /* CGPathImage.swift */; }; 28 | 2EFEF69B1DD8E03200EA383E /* CGPath+Intersections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF69A1DD8E03200EA383E /* CGPath+Intersections.swift */; }; 29 | 2EFEF69F1DD8F6BD00EA383E /* CGPathImage+RawPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF69E1DD8F6BD00EA383E /* CGPathImage+RawPixels.swift */; }; 30 | 2EFEF6A11DD8FFE400EA383E /* Array+CoalescePoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFEF6A01DD8FFE400EA383E /* Array+CoalescePoints.swift */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | 2E34FA51279C874C00A735F9 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 2EFEF6751DD8D48800EA383E /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 2E34FA3C279C86F400A735F9; 39 | remoteInfo = "CGPathIntersection-macOS"; 40 | }; 41 | 2EBDE01C27A5BCD3007A1031 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = 2EFEF6751DD8D48800EA383E /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = 2EBDE01227A5BCD3007A1031; 46 | remoteInfo = "CGPathIntersection-tvOS"; 47 | }; 48 | 2EFEF6891DD8D48800EA383E /* PBXContainerItemProxy */ = { 49 | isa = PBXContainerItemProxy; 50 | containerPortal = 2EFEF6751DD8D48800EA383E /* Project object */; 51 | proxyType = 1; 52 | remoteGlobalIDString = 2EFEF67D1DD8D48800EA383E; 53 | remoteInfo = CGPathIntersection; 54 | }; 55 | /* End PBXContainerItemProxy section */ 56 | 57 | /* Begin PBXFileReference section */ 58 | 2E34FA3D279C86F400A735F9 /* CGPathIntersection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CGPathIntersection.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 2E34FA4C279C874C00A735F9 /* CGPathIntersectionTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CGPathIntersectionTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | 2E34FA57279CC25000A735F9 /* PlatformImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformImage.swift; sourceTree = ""; }; 61 | 2EBDE01327A5BCD3007A1031 /* CGPathIntersection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CGPathIntersection.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 2EBDE01A27A5BCD3007A1031 /* CGPathIntersection-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CGPathIntersection-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 2EFEF67E1DD8D48800EA383E /* CGPathIntersection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CGPathIntersection.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 2EFEF6821DD8D48800EA383E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 2EFEF6871DD8D48800EA383E /* CGPathIntersectionTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CGPathIntersectionTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 2EFEF68C1DD8D48800EA383E /* CGPathIntersectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGPathIntersectionTests.swift; sourceTree = ""; }; 67 | 2EFEF68E1DD8D48800EA383E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 2EFEF6981DD8D5F000EA383E /* CGPathImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGPathImage.swift; sourceTree = ""; }; 69 | 2EFEF69A1DD8E03200EA383E /* CGPath+Intersections.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGPath+Intersections.swift"; sourceTree = ""; }; 70 | 2EFEF69E1DD8F6BD00EA383E /* CGPathImage+RawPixels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGPathImage+RawPixels.swift"; sourceTree = ""; }; 71 | 2EFEF6A01DD8FFE400EA383E /* Array+CoalescePoints.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+CoalescePoints.swift"; sourceTree = ""; }; 72 | /* End PBXFileReference section */ 73 | 74 | /* Begin PBXFrameworksBuildPhase section */ 75 | 2E34FA3A279C86F400A735F9 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | 2E34FA49279C874C00A735F9 /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | 2E34FA50279C874C00A735F9 /* CGPathIntersection.framework in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | 2EBDE01027A5BCD3007A1031 /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | 2EBDE01727A5BCD3007A1031 /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | 2EBDE01B27A5BCD3007A1031 /* CGPathIntersection.framework in Frameworks */, 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | 2EFEF67A1DD8D48800EA383E /* Frameworks */ = { 106 | isa = PBXFrameworksBuildPhase; 107 | buildActionMask = 2147483647; 108 | files = ( 109 | ); 110 | runOnlyForDeploymentPostprocessing = 0; 111 | }; 112 | 2EFEF6841DD8D48800EA383E /* Frameworks */ = { 113 | isa = PBXFrameworksBuildPhase; 114 | buildActionMask = 2147483647; 115 | files = ( 116 | 2EFEF6881DD8D48800EA383E /* CGPathIntersection.framework in Frameworks */, 117 | ); 118 | runOnlyForDeploymentPostprocessing = 0; 119 | }; 120 | /* End PBXFrameworksBuildPhase section */ 121 | 122 | /* Begin PBXGroup section */ 123 | 2EFEF6741DD8D48800EA383E = { 124 | isa = PBXGroup; 125 | children = ( 126 | 2EFEF6801DD8D48800EA383E /* CGPathIntersection */, 127 | 2EFEF68B1DD8D48800EA383E /* CGPathIntersectionTests */, 128 | 2EFEF67F1DD8D48800EA383E /* Products */, 129 | ); 130 | sourceTree = ""; 131 | }; 132 | 2EFEF67F1DD8D48800EA383E /* Products */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 2EFEF67E1DD8D48800EA383E /* CGPathIntersection.framework */, 136 | 2EFEF6871DD8D48800EA383E /* CGPathIntersectionTests-iOS.xctest */, 137 | 2E34FA3D279C86F400A735F9 /* CGPathIntersection.framework */, 138 | 2E34FA4C279C874C00A735F9 /* CGPathIntersectionTests-macOS.xctest */, 139 | 2EBDE01327A5BCD3007A1031 /* CGPathIntersection.framework */, 140 | 2EBDE01A27A5BCD3007A1031 /* CGPathIntersection-tvOSTests.xctest */, 141 | ); 142 | name = Products; 143 | sourceTree = ""; 144 | }; 145 | 2EFEF6801DD8D48800EA383E /* CGPathIntersection */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 2EFEF6821DD8D48800EA383E /* Info.plist */, 149 | 2EFEF6981DD8D5F000EA383E /* CGPathImage.swift */, 150 | 2EFEF69A1DD8E03200EA383E /* CGPath+Intersections.swift */, 151 | 2EFEF69E1DD8F6BD00EA383E /* CGPathImage+RawPixels.swift */, 152 | 2EFEF6A01DD8FFE400EA383E /* Array+CoalescePoints.swift */, 153 | 2E34FA57279CC25000A735F9 /* PlatformImage.swift */, 154 | ); 155 | path = CGPathIntersection; 156 | sourceTree = ""; 157 | }; 158 | 2EFEF68B1DD8D48800EA383E /* CGPathIntersectionTests */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 2EFEF68C1DD8D48800EA383E /* CGPathIntersectionTests.swift */, 162 | 2EFEF68E1DD8D48800EA383E /* Info.plist */, 163 | ); 164 | path = CGPathIntersectionTests; 165 | sourceTree = ""; 166 | }; 167 | /* End PBXGroup section */ 168 | 169 | /* Begin PBXHeadersBuildPhase section */ 170 | 2E34FA38279C86F400A735F9 /* Headers */ = { 171 | isa = PBXHeadersBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | }; 177 | 2EBDE00E27A5BCD3007A1031 /* Headers */ = { 178 | isa = PBXHeadersBuildPhase; 179 | buildActionMask = 2147483647; 180 | files = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | 2EFEF67B1DD8D48800EA383E /* Headers */ = { 185 | isa = PBXHeadersBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXHeadersBuildPhase section */ 192 | 193 | /* Begin PBXNativeTarget section */ 194 | 2E34FA3C279C86F400A735F9 /* CGPathIntersection-macOS */ = { 195 | isa = PBXNativeTarget; 196 | buildConfigurationList = 2E34FA41279C86F400A735F9 /* Build configuration list for PBXNativeTarget "CGPathIntersection-macOS" */; 197 | buildPhases = ( 198 | 2E34FA38279C86F400A735F9 /* Headers */, 199 | 2E34FA39279C86F400A735F9 /* Sources */, 200 | 2E34FA3A279C86F400A735F9 /* Frameworks */, 201 | 2E34FA3B279C86F400A735F9 /* Resources */, 202 | ); 203 | buildRules = ( 204 | ); 205 | dependencies = ( 206 | ); 207 | name = "CGPathIntersection-macOS"; 208 | productName = "CGPathIntersection-macOS"; 209 | productReference = 2E34FA3D279C86F400A735F9 /* CGPathIntersection.framework */; 210 | productType = "com.apple.product-type.framework"; 211 | }; 212 | 2E34FA4B279C874C00A735F9 /* CGPathIntersectionTests-macOS */ = { 213 | isa = PBXNativeTarget; 214 | buildConfigurationList = 2E34FA53279C874C00A735F9 /* Build configuration list for PBXNativeTarget "CGPathIntersectionTests-macOS" */; 215 | buildPhases = ( 216 | 2E34FA48279C874C00A735F9 /* Sources */, 217 | 2E34FA49279C874C00A735F9 /* Frameworks */, 218 | 2E34FA4A279C874C00A735F9 /* Resources */, 219 | ); 220 | buildRules = ( 221 | ); 222 | dependencies = ( 223 | 2E34FA52279C874C00A735F9 /* PBXTargetDependency */, 224 | ); 225 | name = "CGPathIntersectionTests-macOS"; 226 | productName = "CGPathIntersectionTests-macOS"; 227 | productReference = 2E34FA4C279C874C00A735F9 /* CGPathIntersectionTests-macOS.xctest */; 228 | productType = "com.apple.product-type.bundle.unit-test"; 229 | }; 230 | 2EBDE01227A5BCD3007A1031 /* CGPathIntersection-tvOS */ = { 231 | isa = PBXNativeTarget; 232 | buildConfigurationList = 2EBDE02627A5BCD3007A1031 /* Build configuration list for PBXNativeTarget "CGPathIntersection-tvOS" */; 233 | buildPhases = ( 234 | 2EBDE00E27A5BCD3007A1031 /* Headers */, 235 | 2EBDE00F27A5BCD3007A1031 /* Sources */, 236 | 2EBDE01027A5BCD3007A1031 /* Frameworks */, 237 | 2EBDE01127A5BCD3007A1031 /* Resources */, 238 | ); 239 | buildRules = ( 240 | ); 241 | dependencies = ( 242 | ); 243 | name = "CGPathIntersection-tvOS"; 244 | productName = "CGPathIntersection-tvOS"; 245 | productReference = 2EBDE01327A5BCD3007A1031 /* CGPathIntersection.framework */; 246 | productType = "com.apple.product-type.framework"; 247 | }; 248 | 2EBDE01927A5BCD3007A1031 /* CGPathIntersection-tvOSTests */ = { 249 | isa = PBXNativeTarget; 250 | buildConfigurationList = 2EBDE02727A5BCD3007A1031 /* Build configuration list for PBXNativeTarget "CGPathIntersection-tvOSTests" */; 251 | buildPhases = ( 252 | 2EBDE01627A5BCD3007A1031 /* Sources */, 253 | 2EBDE01727A5BCD3007A1031 /* Frameworks */, 254 | 2EBDE01827A5BCD3007A1031 /* Resources */, 255 | ); 256 | buildRules = ( 257 | ); 258 | dependencies = ( 259 | 2EBDE01D27A5BCD3007A1031 /* PBXTargetDependency */, 260 | ); 261 | name = "CGPathIntersection-tvOSTests"; 262 | productName = "CGPathIntersection-tvOSTests"; 263 | productReference = 2EBDE01A27A5BCD3007A1031 /* CGPathIntersection-tvOSTests.xctest */; 264 | productType = "com.apple.product-type.bundle.unit-test"; 265 | }; 266 | 2EFEF67D1DD8D48800EA383E /* CGPathIntersection */ = { 267 | isa = PBXNativeTarget; 268 | buildConfigurationList = 2EFEF6921DD8D48800EA383E /* Build configuration list for PBXNativeTarget "CGPathIntersection" */; 269 | buildPhases = ( 270 | 2EFEF6791DD8D48800EA383E /* Sources */, 271 | 2EFEF67A1DD8D48800EA383E /* Frameworks */, 272 | 2EFEF67B1DD8D48800EA383E /* Headers */, 273 | 2EFEF67C1DD8D48800EA383E /* Resources */, 274 | ); 275 | buildRules = ( 276 | ); 277 | dependencies = ( 278 | ); 279 | name = CGPathIntersection; 280 | productName = CGPathIntersection; 281 | productReference = 2EFEF67E1DD8D48800EA383E /* CGPathIntersection.framework */; 282 | productType = "com.apple.product-type.framework"; 283 | }; 284 | 2EFEF6861DD8D48800EA383E /* CGPathIntersectionTests-iOS */ = { 285 | isa = PBXNativeTarget; 286 | buildConfigurationList = 2EFEF6951DD8D48800EA383E /* Build configuration list for PBXNativeTarget "CGPathIntersectionTests-iOS" */; 287 | buildPhases = ( 288 | 2EFEF6831DD8D48800EA383E /* Sources */, 289 | 2EFEF6841DD8D48800EA383E /* Frameworks */, 290 | 2EFEF6851DD8D48800EA383E /* Resources */, 291 | ); 292 | buildRules = ( 293 | ); 294 | dependencies = ( 295 | 2EFEF68A1DD8D48800EA383E /* PBXTargetDependency */, 296 | ); 297 | name = "CGPathIntersectionTests-iOS"; 298 | productName = CGPathIntersectionTests; 299 | productReference = 2EFEF6871DD8D48800EA383E /* CGPathIntersectionTests-iOS.xctest */; 300 | productType = "com.apple.product-type.bundle.unit-test"; 301 | }; 302 | /* End PBXNativeTarget section */ 303 | 304 | /* Begin PBXProject section */ 305 | 2EFEF6751DD8D48800EA383E /* Project object */ = { 306 | isa = PBXProject; 307 | attributes = { 308 | LastSwiftUpdateCheck = 1300; 309 | LastUpgradeCheck = 1020; 310 | ORGANIZATIONNAME = "Cal Stephens"; 311 | TargetAttributes = { 312 | 2E34FA3C279C86F400A735F9 = { 313 | CreatedOnToolsVersion = 13.0; 314 | DevelopmentTeam = FZ89A5SZ7C; 315 | ProvisioningStyle = Automatic; 316 | }; 317 | 2E34FA4B279C874C00A735F9 = { 318 | CreatedOnToolsVersion = 13.0; 319 | DevelopmentTeam = FZ89A5SZ7C; 320 | ProvisioningStyle = Automatic; 321 | }; 322 | 2EBDE01227A5BCD3007A1031 = { 323 | CreatedOnToolsVersion = 13.0; 324 | DevelopmentTeam = FZ89A5SZ7C; 325 | ProvisioningStyle = Automatic; 326 | }; 327 | 2EBDE01927A5BCD3007A1031 = { 328 | CreatedOnToolsVersion = 13.0; 329 | DevelopmentTeam = FZ89A5SZ7C; 330 | ProvisioningStyle = Automatic; 331 | }; 332 | 2EFEF67D1DD8D48800EA383E = { 333 | CreatedOnToolsVersion = 8.1; 334 | DevelopmentTeam = FZ89A5SZ7C; 335 | LastSwiftMigration = 1020; 336 | ProvisioningStyle = Automatic; 337 | }; 338 | 2EFEF6861DD8D48800EA383E = { 339 | CreatedOnToolsVersion = 8.1; 340 | DevelopmentTeam = FZ89A5SZ7C; 341 | LastSwiftMigration = 0910; 342 | ProvisioningStyle = Automatic; 343 | }; 344 | }; 345 | }; 346 | buildConfigurationList = 2EFEF6781DD8D48800EA383E /* Build configuration list for PBXProject "CGPathIntersection" */; 347 | compatibilityVersion = "Xcode 3.2"; 348 | developmentRegion = en; 349 | hasScannedForEncodings = 0; 350 | knownRegions = ( 351 | en, 352 | Base, 353 | ); 354 | mainGroup = 2EFEF6741DD8D48800EA383E; 355 | productRefGroup = 2EFEF67F1DD8D48800EA383E /* Products */; 356 | projectDirPath = ""; 357 | projectRoot = ""; 358 | targets = ( 359 | 2EFEF67D1DD8D48800EA383E /* CGPathIntersection */, 360 | 2EFEF6861DD8D48800EA383E /* CGPathIntersectionTests-iOS */, 361 | 2E34FA3C279C86F400A735F9 /* CGPathIntersection-macOS */, 362 | 2E34FA4B279C874C00A735F9 /* CGPathIntersectionTests-macOS */, 363 | 2EBDE01227A5BCD3007A1031 /* CGPathIntersection-tvOS */, 364 | 2EBDE01927A5BCD3007A1031 /* CGPathIntersection-tvOSTests */, 365 | ); 366 | }; 367 | /* End PBXProject section */ 368 | 369 | /* Begin PBXResourcesBuildPhase section */ 370 | 2E34FA3B279C86F400A735F9 /* Resources */ = { 371 | isa = PBXResourcesBuildPhase; 372 | buildActionMask = 2147483647; 373 | files = ( 374 | ); 375 | runOnlyForDeploymentPostprocessing = 0; 376 | }; 377 | 2E34FA4A279C874C00A735F9 /* Resources */ = { 378 | isa = PBXResourcesBuildPhase; 379 | buildActionMask = 2147483647; 380 | files = ( 381 | ); 382 | runOnlyForDeploymentPostprocessing = 0; 383 | }; 384 | 2EBDE01127A5BCD3007A1031 /* Resources */ = { 385 | isa = PBXResourcesBuildPhase; 386 | buildActionMask = 2147483647; 387 | files = ( 388 | ); 389 | runOnlyForDeploymentPostprocessing = 0; 390 | }; 391 | 2EBDE01827A5BCD3007A1031 /* Resources */ = { 392 | isa = PBXResourcesBuildPhase; 393 | buildActionMask = 2147483647; 394 | files = ( 395 | ); 396 | runOnlyForDeploymentPostprocessing = 0; 397 | }; 398 | 2EFEF67C1DD8D48800EA383E /* Resources */ = { 399 | isa = PBXResourcesBuildPhase; 400 | buildActionMask = 2147483647; 401 | files = ( 402 | ); 403 | runOnlyForDeploymentPostprocessing = 0; 404 | }; 405 | 2EFEF6851DD8D48800EA383E /* Resources */ = { 406 | isa = PBXResourcesBuildPhase; 407 | buildActionMask = 2147483647; 408 | files = ( 409 | ); 410 | runOnlyForDeploymentPostprocessing = 0; 411 | }; 412 | /* End PBXResourcesBuildPhase section */ 413 | 414 | /* Begin PBXSourcesBuildPhase section */ 415 | 2E34FA39279C86F400A735F9 /* Sources */ = { 416 | isa = PBXSourcesBuildPhase; 417 | buildActionMask = 2147483647; 418 | files = ( 419 | 2E34FA44279C86FE00A735F9 /* CGPathImage.swift in Sources */, 420 | 2E34FA45279C86FE00A735F9 /* CGPath+Intersections.swift in Sources */, 421 | 2E34FA46279C86FE00A735F9 /* CGPathImage+RawPixels.swift in Sources */, 422 | 2E34FA59279CC39C00A735F9 /* PlatformImage.swift in Sources */, 423 | 2E34FA47279C86FE00A735F9 /* Array+CoalescePoints.swift in Sources */, 424 | ); 425 | runOnlyForDeploymentPostprocessing = 0; 426 | }; 427 | 2E34FA48279C874C00A735F9 /* Sources */ = { 428 | isa = PBXSourcesBuildPhase; 429 | buildActionMask = 2147483647; 430 | files = ( 431 | 2E34FA56279C875100A735F9 /* CGPathIntersectionTests.swift in Sources */, 432 | ); 433 | runOnlyForDeploymentPostprocessing = 0; 434 | }; 435 | 2EBDE00F27A5BCD3007A1031 /* Sources */ = { 436 | isa = PBXSourcesBuildPhase; 437 | buildActionMask = 2147483647; 438 | files = ( 439 | 2EBDE02A27A5BCE3007A1031 /* PlatformImage.swift in Sources */, 440 | 2EBDE02927A5BCE3007A1031 /* Array+CoalescePoints.swift in Sources */, 441 | 2EBDE02D27A5BCE3007A1031 /* CGPathImage+RawPixels.swift in Sources */, 442 | 2EBDE02C27A5BCE3007A1031 /* CGPathImage.swift in Sources */, 443 | 2EBDE02B27A5BCE3007A1031 /* CGPath+Intersections.swift in Sources */, 444 | ); 445 | runOnlyForDeploymentPostprocessing = 0; 446 | }; 447 | 2EBDE01627A5BCD3007A1031 /* Sources */ = { 448 | isa = PBXSourcesBuildPhase; 449 | buildActionMask = 2147483647; 450 | files = ( 451 | 2EBDE02827A5BCE0007A1031 /* CGPathIntersectionTests.swift in Sources */, 452 | ); 453 | runOnlyForDeploymentPostprocessing = 0; 454 | }; 455 | 2EFEF6791DD8D48800EA383E /* Sources */ = { 456 | isa = PBXSourcesBuildPhase; 457 | buildActionMask = 2147483647; 458 | files = ( 459 | 2EFEF6991DD8D5F000EA383E /* CGPathImage.swift in Sources */, 460 | 2EFEF69B1DD8E03200EA383E /* CGPath+Intersections.swift in Sources */, 461 | 2EFEF69F1DD8F6BD00EA383E /* CGPathImage+RawPixels.swift in Sources */, 462 | 2E34FA58279CC25000A735F9 /* PlatformImage.swift in Sources */, 463 | 2EFEF6A11DD8FFE400EA383E /* Array+CoalescePoints.swift in Sources */, 464 | ); 465 | runOnlyForDeploymentPostprocessing = 0; 466 | }; 467 | 2EFEF6831DD8D48800EA383E /* Sources */ = { 468 | isa = PBXSourcesBuildPhase; 469 | buildActionMask = 2147483647; 470 | files = ( 471 | 2EFEF68D1DD8D48800EA383E /* CGPathIntersectionTests.swift in Sources */, 472 | ); 473 | runOnlyForDeploymentPostprocessing = 0; 474 | }; 475 | /* End PBXSourcesBuildPhase section */ 476 | 477 | /* Begin PBXTargetDependency section */ 478 | 2E34FA52279C874C00A735F9 /* PBXTargetDependency */ = { 479 | isa = PBXTargetDependency; 480 | target = 2E34FA3C279C86F400A735F9 /* CGPathIntersection-macOS */; 481 | targetProxy = 2E34FA51279C874C00A735F9 /* PBXContainerItemProxy */; 482 | }; 483 | 2EBDE01D27A5BCD3007A1031 /* PBXTargetDependency */ = { 484 | isa = PBXTargetDependency; 485 | target = 2EBDE01227A5BCD3007A1031 /* CGPathIntersection-tvOS */; 486 | targetProxy = 2EBDE01C27A5BCD3007A1031 /* PBXContainerItemProxy */; 487 | }; 488 | 2EFEF68A1DD8D48800EA383E /* PBXTargetDependency */ = { 489 | isa = PBXTargetDependency; 490 | target = 2EFEF67D1DD8D48800EA383E /* CGPathIntersection */; 491 | targetProxy = 2EFEF6891DD8D48800EA383E /* PBXContainerItemProxy */; 492 | }; 493 | /* End PBXTargetDependency section */ 494 | 495 | /* Begin XCBuildConfiguration section */ 496 | 2E34FA42279C86F400A735F9 /* Debug */ = { 497 | isa = XCBuildConfiguration; 498 | buildSettings = { 499 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 500 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 501 | CLANG_ENABLE_OBJC_WEAK = YES; 502 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 503 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 504 | CODE_SIGN_STYLE = Automatic; 505 | COMBINE_HIDPI_IMAGES = YES; 506 | CURRENT_PROJECT_VERSION = 1; 507 | DEFINES_MODULE = YES; 508 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 509 | DYLIB_COMPATIBILITY_VERSION = 1; 510 | DYLIB_CURRENT_VERSION = 1; 511 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 512 | GCC_C_LANGUAGE_STANDARD = gnu11; 513 | GENERATE_INFOPLIST_FILE = YES; 514 | INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Cal Stephens. All rights reserved."; 515 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 516 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 517 | MACOSX_DEPLOYMENT_TARGET = 10.10; 518 | MARKETING_VERSION = 4.0; 519 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 520 | MTL_FAST_MATH = YES; 521 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.CGPathIntersection; 522 | PRODUCT_MODULE_NAME = CGPathIntersection; 523 | PRODUCT_NAME = CGPathIntersection; 524 | SDKROOT = macosx; 525 | SKIP_INSTALL = YES; 526 | SWIFT_EMIT_LOC_STRINGS = YES; 527 | SWIFT_VERSION = 5.0; 528 | }; 529 | name = Debug; 530 | }; 531 | 2E34FA43279C86F400A735F9 /* Release */ = { 532 | isa = XCBuildConfiguration; 533 | buildSettings = { 534 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 535 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 536 | CLANG_ENABLE_OBJC_WEAK = YES; 537 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 538 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 539 | CODE_SIGN_STYLE = Automatic; 540 | COMBINE_HIDPI_IMAGES = YES; 541 | CURRENT_PROJECT_VERSION = 1; 542 | DEFINES_MODULE = YES; 543 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 544 | DYLIB_COMPATIBILITY_VERSION = 1; 545 | DYLIB_CURRENT_VERSION = 1; 546 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 547 | GCC_C_LANGUAGE_STANDARD = gnu11; 548 | GENERATE_INFOPLIST_FILE = YES; 549 | INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Cal Stephens. All rights reserved."; 550 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 551 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 552 | MACOSX_DEPLOYMENT_TARGET = 10.10; 553 | MARKETING_VERSION = 4.0; 554 | MTL_FAST_MATH = YES; 555 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.CGPathIntersection; 556 | PRODUCT_MODULE_NAME = CGPathIntersection; 557 | PRODUCT_NAME = CGPathIntersection; 558 | SDKROOT = macosx; 559 | SKIP_INSTALL = YES; 560 | SWIFT_EMIT_LOC_STRINGS = YES; 561 | SWIFT_VERSION = 5.0; 562 | }; 563 | name = Release; 564 | }; 565 | 2E34FA54279C874C00A735F9 /* Debug */ = { 566 | isa = XCBuildConfiguration; 567 | buildSettings = { 568 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 569 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 570 | CLANG_ENABLE_OBJC_WEAK = YES; 571 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 572 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 573 | CODE_SIGN_STYLE = Automatic; 574 | COMBINE_HIDPI_IMAGES = YES; 575 | CURRENT_PROJECT_VERSION = 1; 576 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 577 | GCC_C_LANGUAGE_STANDARD = gnu11; 578 | GENERATE_INFOPLIST_FILE = YES; 579 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 580 | MACOSX_DEPLOYMENT_TARGET = 11.6; 581 | MARKETING_VERSION = 1.0; 582 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 583 | MTL_FAST_MATH = YES; 584 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.CGPathIntersectionTests-macOS"; 585 | PRODUCT_NAME = "$(TARGET_NAME)"; 586 | SDKROOT = macosx; 587 | SWIFT_EMIT_LOC_STRINGS = NO; 588 | SWIFT_VERSION = 5.0; 589 | }; 590 | name = Debug; 591 | }; 592 | 2E34FA55279C874C00A735F9 /* Release */ = { 593 | isa = XCBuildConfiguration; 594 | buildSettings = { 595 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 596 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 597 | CLANG_ENABLE_OBJC_WEAK = YES; 598 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 599 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 600 | CODE_SIGN_STYLE = Automatic; 601 | COMBINE_HIDPI_IMAGES = YES; 602 | CURRENT_PROJECT_VERSION = 1; 603 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 604 | GCC_C_LANGUAGE_STANDARD = gnu11; 605 | GENERATE_INFOPLIST_FILE = YES; 606 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 607 | MACOSX_DEPLOYMENT_TARGET = 11.6; 608 | MARKETING_VERSION = 1.0; 609 | MTL_FAST_MATH = YES; 610 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.CGPathIntersectionTests-macOS"; 611 | PRODUCT_NAME = "$(TARGET_NAME)"; 612 | SDKROOT = macosx; 613 | SWIFT_EMIT_LOC_STRINGS = NO; 614 | SWIFT_VERSION = 5.0; 615 | }; 616 | name = Release; 617 | }; 618 | 2EBDE02227A5BCD3007A1031 /* Debug */ = { 619 | isa = XCBuildConfiguration; 620 | buildSettings = { 621 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 622 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 623 | CLANG_ENABLE_OBJC_WEAK = YES; 624 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 625 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 626 | CODE_SIGN_STYLE = Automatic; 627 | CURRENT_PROJECT_VERSION = 1; 628 | DEFINES_MODULE = YES; 629 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 630 | DYLIB_COMPATIBILITY_VERSION = 1; 631 | DYLIB_CURRENT_VERSION = 1; 632 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 633 | GCC_C_LANGUAGE_STANDARD = gnu11; 634 | GENERATE_INFOPLIST_FILE = YES; 635 | INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Cal Stephens. All rights reserved."; 636 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 637 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 638 | MARKETING_VERSION = 4.0; 639 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 640 | MTL_FAST_MATH = YES; 641 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.CGPathIntersection; 642 | PRODUCT_NAME = CGPathIntersection; 643 | SDKROOT = appletvos; 644 | SKIP_INSTALL = YES; 645 | SWIFT_EMIT_LOC_STRINGS = YES; 646 | SWIFT_VERSION = 5.0; 647 | TARGETED_DEVICE_FAMILY = 3; 648 | TVOS_DEPLOYMENT_TARGET = 9.0; 649 | }; 650 | name = Debug; 651 | }; 652 | 2EBDE02327A5BCD3007A1031 /* Release */ = { 653 | isa = XCBuildConfiguration; 654 | buildSettings = { 655 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 656 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 657 | CLANG_ENABLE_OBJC_WEAK = YES; 658 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 659 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 660 | CODE_SIGN_STYLE = Automatic; 661 | CURRENT_PROJECT_VERSION = 1; 662 | DEFINES_MODULE = YES; 663 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 664 | DYLIB_COMPATIBILITY_VERSION = 1; 665 | DYLIB_CURRENT_VERSION = 1; 666 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 667 | GCC_C_LANGUAGE_STANDARD = gnu11; 668 | GENERATE_INFOPLIST_FILE = YES; 669 | INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Cal Stephens. All rights reserved."; 670 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 671 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 672 | MARKETING_VERSION = 4.0; 673 | MTL_FAST_MATH = YES; 674 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.CGPathIntersection; 675 | PRODUCT_NAME = CGPathIntersection; 676 | SDKROOT = appletvos; 677 | SKIP_INSTALL = YES; 678 | SWIFT_EMIT_LOC_STRINGS = YES; 679 | SWIFT_VERSION = 5.0; 680 | TARGETED_DEVICE_FAMILY = 3; 681 | TVOS_DEPLOYMENT_TARGET = 9.0; 682 | }; 683 | name = Release; 684 | }; 685 | 2EBDE02427A5BCD3007A1031 /* Debug */ = { 686 | isa = XCBuildConfiguration; 687 | buildSettings = { 688 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 689 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 690 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 691 | CLANG_ENABLE_OBJC_WEAK = YES; 692 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 693 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 694 | CODE_SIGN_STYLE = Automatic; 695 | CURRENT_PROJECT_VERSION = 1; 696 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 697 | GCC_C_LANGUAGE_STANDARD = gnu11; 698 | GENERATE_INFOPLIST_FILE = YES; 699 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 700 | MARKETING_VERSION = 1.0; 701 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 702 | MTL_FAST_MATH = YES; 703 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.CGPathIntersection-tvOSTests"; 704 | PRODUCT_NAME = "$(TARGET_NAME)"; 705 | SDKROOT = appletvos; 706 | SWIFT_EMIT_LOC_STRINGS = NO; 707 | SWIFT_VERSION = 5.0; 708 | TARGETED_DEVICE_FAMILY = 3; 709 | TVOS_DEPLOYMENT_TARGET = 15.0; 710 | }; 711 | name = Debug; 712 | }; 713 | 2EBDE02527A5BCD3007A1031 /* Release */ = { 714 | isa = XCBuildConfiguration; 715 | buildSettings = { 716 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 717 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 718 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 719 | CLANG_ENABLE_OBJC_WEAK = YES; 720 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 721 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 722 | CODE_SIGN_STYLE = Automatic; 723 | CURRENT_PROJECT_VERSION = 1; 724 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 725 | GCC_C_LANGUAGE_STANDARD = gnu11; 726 | GENERATE_INFOPLIST_FILE = YES; 727 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 728 | MARKETING_VERSION = 1.0; 729 | MTL_FAST_MATH = YES; 730 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.CGPathIntersection-tvOSTests"; 731 | PRODUCT_NAME = "$(TARGET_NAME)"; 732 | SDKROOT = appletvos; 733 | SWIFT_EMIT_LOC_STRINGS = NO; 734 | SWIFT_VERSION = 5.0; 735 | TARGETED_DEVICE_FAMILY = 3; 736 | TVOS_DEPLOYMENT_TARGET = 15.0; 737 | }; 738 | name = Release; 739 | }; 740 | 2EFEF6901DD8D48800EA383E /* Debug */ = { 741 | isa = XCBuildConfiguration; 742 | buildSettings = { 743 | ALWAYS_SEARCH_USER_PATHS = NO; 744 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 745 | CLANG_ANALYZER_NONNULL = YES; 746 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 747 | CLANG_CXX_LIBRARY = "libc++"; 748 | CLANG_ENABLE_MODULES = YES; 749 | CLANG_ENABLE_OBJC_ARC = YES; 750 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 751 | CLANG_WARN_BOOL_CONVERSION = YES; 752 | CLANG_WARN_COMMA = YES; 753 | CLANG_WARN_CONSTANT_CONVERSION = YES; 754 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 755 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 756 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 757 | CLANG_WARN_EMPTY_BODY = YES; 758 | CLANG_WARN_ENUM_CONVERSION = YES; 759 | CLANG_WARN_INFINITE_RECURSION = YES; 760 | CLANG_WARN_INT_CONVERSION = YES; 761 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 762 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 763 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 764 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 765 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 766 | CLANG_WARN_STRICT_PROTOTYPES = YES; 767 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 768 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 769 | CLANG_WARN_UNREACHABLE_CODE = YES; 770 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 771 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 772 | COPY_PHASE_STRIP = NO; 773 | CURRENT_PROJECT_VERSION = 1; 774 | DEBUG_INFORMATION_FORMAT = dwarf; 775 | ENABLE_STRICT_OBJC_MSGSEND = YES; 776 | ENABLE_TESTABILITY = YES; 777 | GCC_C_LANGUAGE_STANDARD = gnu99; 778 | GCC_DYNAMIC_NO_PIC = NO; 779 | GCC_NO_COMMON_BLOCKS = YES; 780 | GCC_OPTIMIZATION_LEVEL = 0; 781 | GCC_PREPROCESSOR_DEFINITIONS = ( 782 | "DEBUG=1", 783 | "$(inherited)", 784 | ); 785 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 786 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 787 | GCC_WARN_UNDECLARED_SELECTOR = YES; 788 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 789 | GCC_WARN_UNUSED_FUNCTION = YES; 790 | GCC_WARN_UNUSED_VARIABLE = YES; 791 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 792 | MACOSX_DEPLOYMENT_TARGET = 10.10; 793 | MTL_ENABLE_DEBUG_INFO = YES; 794 | ONLY_ACTIVE_ARCH = YES; 795 | SDKROOT = iphoneos; 796 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 797 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 798 | TARGETED_DEVICE_FAMILY = "1,2"; 799 | VERSIONING_SYSTEM = "apple-generic"; 800 | VERSION_INFO_PREFIX = ""; 801 | }; 802 | name = Debug; 803 | }; 804 | 2EFEF6911DD8D48800EA383E /* Release */ = { 805 | isa = XCBuildConfiguration; 806 | buildSettings = { 807 | ALWAYS_SEARCH_USER_PATHS = NO; 808 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 809 | CLANG_ANALYZER_NONNULL = YES; 810 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 811 | CLANG_CXX_LIBRARY = "libc++"; 812 | CLANG_ENABLE_MODULES = YES; 813 | CLANG_ENABLE_OBJC_ARC = YES; 814 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 815 | CLANG_WARN_BOOL_CONVERSION = YES; 816 | CLANG_WARN_COMMA = YES; 817 | CLANG_WARN_CONSTANT_CONVERSION = YES; 818 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 819 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 820 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 821 | CLANG_WARN_EMPTY_BODY = YES; 822 | CLANG_WARN_ENUM_CONVERSION = YES; 823 | CLANG_WARN_INFINITE_RECURSION = YES; 824 | CLANG_WARN_INT_CONVERSION = YES; 825 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 826 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 827 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 828 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 829 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 830 | CLANG_WARN_STRICT_PROTOTYPES = YES; 831 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 832 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 833 | CLANG_WARN_UNREACHABLE_CODE = YES; 834 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 835 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 836 | COPY_PHASE_STRIP = NO; 837 | CURRENT_PROJECT_VERSION = 1; 838 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 839 | ENABLE_NS_ASSERTIONS = NO; 840 | ENABLE_STRICT_OBJC_MSGSEND = YES; 841 | GCC_C_LANGUAGE_STANDARD = gnu99; 842 | GCC_NO_COMMON_BLOCKS = YES; 843 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 844 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 845 | GCC_WARN_UNDECLARED_SELECTOR = YES; 846 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 847 | GCC_WARN_UNUSED_FUNCTION = YES; 848 | GCC_WARN_UNUSED_VARIABLE = YES; 849 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 850 | MACOSX_DEPLOYMENT_TARGET = 10.10; 851 | MTL_ENABLE_DEBUG_INFO = NO; 852 | SDKROOT = iphoneos; 853 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 854 | TARGETED_DEVICE_FAMILY = "1,2"; 855 | VALIDATE_PRODUCT = YES; 856 | VERSIONING_SYSTEM = "apple-generic"; 857 | VERSION_INFO_PREFIX = ""; 858 | }; 859 | name = Release; 860 | }; 861 | 2EFEF6931DD8D48800EA383E /* Debug */ = { 862 | isa = XCBuildConfiguration; 863 | buildSettings = { 864 | CLANG_ENABLE_MODULES = YES; 865 | CODE_SIGN_IDENTITY = ""; 866 | DEFINES_MODULE = YES; 867 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 868 | DYLIB_COMPATIBILITY_VERSION = 1; 869 | DYLIB_CURRENT_VERSION = 1; 870 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 871 | INFOPLIST_FILE = CGPathIntersection/Info.plist; 872 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 873 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 874 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 875 | MACOSX_DEPLOYMENT_TARGET = 10.15; 876 | MARKETING_VERSION = 4.0; 877 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.CGPathIntersection; 878 | PRODUCT_NAME = "$(TARGET_NAME)"; 879 | SKIP_INSTALL = YES; 880 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 881 | SWIFT_VERSION = 5.0; 882 | }; 883 | name = Debug; 884 | }; 885 | 2EFEF6941DD8D48800EA383E /* Release */ = { 886 | isa = XCBuildConfiguration; 887 | buildSettings = { 888 | CLANG_ENABLE_MODULES = YES; 889 | CODE_SIGN_IDENTITY = ""; 890 | DEFINES_MODULE = YES; 891 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 892 | DYLIB_COMPATIBILITY_VERSION = 1; 893 | DYLIB_CURRENT_VERSION = 1; 894 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 895 | INFOPLIST_FILE = CGPathIntersection/Info.plist; 896 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 897 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 898 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 899 | MACOSX_DEPLOYMENT_TARGET = 10.15; 900 | MARKETING_VERSION = 4.0; 901 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.CGPathIntersection; 902 | PRODUCT_NAME = "$(TARGET_NAME)"; 903 | SKIP_INSTALL = YES; 904 | SWIFT_VERSION = 5.0; 905 | }; 906 | name = Release; 907 | }; 908 | 2EFEF6961DD8D48800EA383E /* Debug */ = { 909 | isa = XCBuildConfiguration; 910 | buildSettings = { 911 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 912 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 913 | INFOPLIST_FILE = CGPathIntersectionTests/Info.plist; 914 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 915 | MACOSX_DEPLOYMENT_TARGET = 10.15; 916 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.CGPathIntersectionTests; 917 | PRODUCT_NAME = "$(TARGET_NAME)"; 918 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 919 | SWIFT_VERSION = 5.0; 920 | TVOS_DEPLOYMENT_TARGET = 10.1; 921 | }; 922 | name = Debug; 923 | }; 924 | 2EFEF6971DD8D48800EA383E /* Release */ = { 925 | isa = XCBuildConfiguration; 926 | buildSettings = { 927 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 928 | DEVELOPMENT_TEAM = FZ89A5SZ7C; 929 | INFOPLIST_FILE = CGPathIntersectionTests/Info.plist; 930 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 931 | MACOSX_DEPLOYMENT_TARGET = 10.15; 932 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.CGPathIntersectionTests; 933 | PRODUCT_NAME = "$(TARGET_NAME)"; 934 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 935 | SWIFT_VERSION = 5.0; 936 | TVOS_DEPLOYMENT_TARGET = 10.1; 937 | }; 938 | name = Release; 939 | }; 940 | /* End XCBuildConfiguration section */ 941 | 942 | /* Begin XCConfigurationList section */ 943 | 2E34FA41279C86F400A735F9 /* Build configuration list for PBXNativeTarget "CGPathIntersection-macOS" */ = { 944 | isa = XCConfigurationList; 945 | buildConfigurations = ( 946 | 2E34FA42279C86F400A735F9 /* Debug */, 947 | 2E34FA43279C86F400A735F9 /* Release */, 948 | ); 949 | defaultConfigurationIsVisible = 0; 950 | defaultConfigurationName = Release; 951 | }; 952 | 2E34FA53279C874C00A735F9 /* Build configuration list for PBXNativeTarget "CGPathIntersectionTests-macOS" */ = { 953 | isa = XCConfigurationList; 954 | buildConfigurations = ( 955 | 2E34FA54279C874C00A735F9 /* Debug */, 956 | 2E34FA55279C874C00A735F9 /* Release */, 957 | ); 958 | defaultConfigurationIsVisible = 0; 959 | defaultConfigurationName = Release; 960 | }; 961 | 2EBDE02627A5BCD3007A1031 /* Build configuration list for PBXNativeTarget "CGPathIntersection-tvOS" */ = { 962 | isa = XCConfigurationList; 963 | buildConfigurations = ( 964 | 2EBDE02227A5BCD3007A1031 /* Debug */, 965 | 2EBDE02327A5BCD3007A1031 /* Release */, 966 | ); 967 | defaultConfigurationIsVisible = 0; 968 | defaultConfigurationName = Release; 969 | }; 970 | 2EBDE02727A5BCD3007A1031 /* Build configuration list for PBXNativeTarget "CGPathIntersection-tvOSTests" */ = { 971 | isa = XCConfigurationList; 972 | buildConfigurations = ( 973 | 2EBDE02427A5BCD3007A1031 /* Debug */, 974 | 2EBDE02527A5BCD3007A1031 /* Release */, 975 | ); 976 | defaultConfigurationIsVisible = 0; 977 | defaultConfigurationName = Release; 978 | }; 979 | 2EFEF6781DD8D48800EA383E /* Build configuration list for PBXProject "CGPathIntersection" */ = { 980 | isa = XCConfigurationList; 981 | buildConfigurations = ( 982 | 2EFEF6901DD8D48800EA383E /* Debug */, 983 | 2EFEF6911DD8D48800EA383E /* Release */, 984 | ); 985 | defaultConfigurationIsVisible = 0; 986 | defaultConfigurationName = Release; 987 | }; 988 | 2EFEF6921DD8D48800EA383E /* Build configuration list for PBXNativeTarget "CGPathIntersection" */ = { 989 | isa = XCConfigurationList; 990 | buildConfigurations = ( 991 | 2EFEF6931DD8D48800EA383E /* Debug */, 992 | 2EFEF6941DD8D48800EA383E /* Release */, 993 | ); 994 | defaultConfigurationIsVisible = 0; 995 | defaultConfigurationName = Release; 996 | }; 997 | 2EFEF6951DD8D48800EA383E /* Build configuration list for PBXNativeTarget "CGPathIntersectionTests-iOS" */ = { 998 | isa = XCConfigurationList; 999 | buildConfigurations = ( 1000 | 2EFEF6961DD8D48800EA383E /* Debug */, 1001 | 2EFEF6971DD8D48800EA383E /* Release */, 1002 | ); 1003 | defaultConfigurationIsVisible = 0; 1004 | defaultConfigurationName = Release; 1005 | }; 1006 | /* End XCConfigurationList section */ 1007 | }; 1008 | rootObject = 2EFEF6751DD8D48800EA383E /* Project object */; 1009 | } 1010 | -------------------------------------------------------------------------------- /CGPathIntersection.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CGPathIntersection.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CGPathIntersection.xcodeproj/xcshareddata/xcschemes/CGPathIntersection-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 64 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /CGPathIntersection.xcodeproj/xcshareddata/xcschemes/CGPathIntersection-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /CGPathIntersection.xcodeproj/xcshareddata/xcschemes/CGPathIntersection-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /CGPathIntersection/Array+CoalescePoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+CoalescePoints.swift 3 | // CGPathIntersection 4 | // 5 | // Created by Cal Stephens on 11/13/16. 6 | // Copyright © 2016 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | import Foundation 11 | 12 | extension Array where Element == CGPoint { 13 | 14 | func coalescePoints() -> [CGPoint] { 15 | var groups = [[CGPoint]]() 16 | 17 | //build groups of nearby pixels 18 | for point in self { 19 | //start a new group if there are none 20 | if groups.count == 0 { 21 | groups.append([point]) 22 | continue 23 | } 24 | 25 | var addedToGroup = false 26 | 27 | //search for a nearby group to join 28 | for i in 0 ..< groups.count { 29 | let distances = groups[i].map{ $0.distance(to: point) } 30 | let miniumDistanceToGroup = distances.sorted().first 31 | 32 | if let minimumDistance = miniumDistanceToGroup, minimumDistance < 6.0 { 33 | groups[i].append(point) 34 | addedToGroup = true 35 | break 36 | } 37 | } 38 | 39 | if !addedToGroup { 40 | groups.append([point]) 41 | } 42 | } 43 | 44 | //map groups to average values 45 | return groups.map { group in 46 | let xSum = group.reduce(0) { $0 + $1.x } 47 | let ySum = group.reduce(0) { $0 + $1.y } 48 | 49 | return CGPoint(x: Int(round(xSum / CGFloat(group.count))), 50 | y: Int(round(ySum / CGFloat(group.count)))) 51 | } 52 | } 53 | 54 | } 55 | 56 | extension CGPoint { 57 | 58 | func distance(to point: CGPoint) -> CGFloat { 59 | return sqrt(pow(self.x - point.x, 2) + pow(self.y - point.y, 2)) 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /CGPathIntersection/CGPath+Intersections.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPath+Intersections.swift 3 | // CGPathIntersection 4 | // 5 | // Created by Cal Stephens on 11/13/16. 6 | // Copyright © 2016 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | extension CGPath { 12 | 13 | public func intersects(_ path: CGPath) -> Bool { 14 | return self.intersectionPoints(with: path).count > 0 15 | } 16 | 17 | public func intersectionPoints(with path: CGPath) -> [CGPoint] { 18 | let pathImage1 = CGPathImage(from: self) 19 | let pathImage2 = CGPathImage(from: path) 20 | 21 | return pathImage1.intersectionPoints(with: pathImage2) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /CGPathIntersection/CGPathImage+RawPixels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPathImage+RawPixels.swift 3 | // CGPathIntersection 4 | // 5 | // Created by Cal Stephens on 11/13/16. 6 | // Copyright © 2016 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreGraphics 11 | 12 | #if canImport(AppKit) 13 | import AppKit 14 | #endif 15 | 16 | struct RawImage { 17 | #if !canImport(UIKit) 18 | /// On macOS we can just call `NSBitmapImageRep.colorAt(x:y)` 19 | let bitmapRep: NSBitmapImageRep 20 | #else 21 | // On iOS we have to access the pixel data manually by computing 22 | // the correct offset in the image data's buffer 23 | let pixels: UnsafePointer 24 | #endif 25 | let options: Options 26 | 27 | struct Options { 28 | let bounds: CGRect 29 | let bytesPerRow: Int 30 | let bitsPerComponent: Int 31 | } 32 | } 33 | 34 | extension CGPathImage { 35 | 36 | var rawImage: RawImage? { 37 | guard let cgImage = image.cgImage else { return nil} 38 | 39 | let boundingBox = CGRect( 40 | origin: CGPoint(x: Int(self.boundingBox.origin.x), y: Int(self.boundingBox.origin.y)), 41 | size: CGSize(width: cgImage.width, height: cgImage.height)) 42 | 43 | let options = RawImage.Options( 44 | bounds: boundingBox, 45 | bytesPerRow: cgImage.bytesPerRow, 46 | bitsPerComponent: cgImage.bitsPerComponent) 47 | 48 | #if !canImport(UIKit) 49 | guard 50 | let tiffRep = image.tiffRepresentation, 51 | let bitmapRep = NSBitmapImageRep(data: tiffRep) 52 | else { return nil } 53 | 54 | return RawImage(bitmapRep: bitmapRep, options: options) 55 | #else 56 | guard 57 | let pixelData = cgImage.dataProvider?.data, 58 | let pixels = CFDataGetBytePtr(pixelData) 59 | else { return nil } 60 | 61 | return RawImage(pixels: pixels, options: options) 62 | #endif 63 | } 64 | 65 | } 66 | 67 | extension RawImage { 68 | #if !canImport(UIKit) 69 | func colorAt(x: Int, y: Int) -> (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { 70 | guard 71 | let nsColor = bitmapRep.colorAt( 72 | x: x - Int(options.bounds.minX), 73 | y: Int(options.bounds.maxY) - y - 1) 74 | else { return (0, 0, 0, 0) } 75 | 76 | var (red, green, blue, alpha) = (CGFloat(0), CGFloat(0), CGFloat(0), CGFloat(0)) 77 | nsColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) 78 | return (red, green, blue, alpha) 79 | } 80 | #else 81 | func colorAt(x: Int, y: Int) -> (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { 82 | // rows in memory are always powers of two, leaving empty bytes to pad as necessary. 83 | let rowWidth = Int(options.bytesPerRow / 4) 84 | let pixelPointer = ((rowWidth * (y - Int(options.bounds.minY))) + (x - Int(options.bounds.minX))) * 4 85 | 86 | // data[pixelInfo] is a pointer to the first in a series of four UInt8s (r, g, b, a) 87 | func byte(_ offset: Int) -> CGFloat { 88 | return CGFloat(pixels[pixelPointer + offset]) / 256.0 89 | } 90 | 91 | // buffer in BGRA format 92 | return (red: byte(2), green: byte(1), blue: byte(0), alpha: byte(3)) 93 | } 94 | #endif 95 | } 96 | 97 | -------------------------------------------------------------------------------- /CGPathIntersection/CGPathImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPathImage.swift 3 | // CGPathIntersection 4 | // 5 | // Created by Cal Stephens on 11/13/16. 6 | // Copyright © 2016 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | public struct CGPathImage { 12 | 13 | 14 | //MARK: - Render image for path 15 | 16 | public let path: CGPath 17 | public let boundingBox: CGRect 18 | public let cgImage: CGImage? 19 | public let image: PlatformImage 20 | 21 | public init(from path: CGPath) { 22 | self.path = path 23 | 24 | // Perfectly-straight lines have a width or height of zero, 25 | // but to create a useful image we have to have at least one row/column of pixels. 26 | let absoluteBoundingBox = path.boundingBoxOfPath 27 | let boundingBox = CGRect( 28 | x: absoluteBoundingBox.origin.x, 29 | y: absoluteBoundingBox.origin.y, 30 | width: max(absoluteBoundingBox.size.width, 1), 31 | height: max(absoluteBoundingBox.size.height, 1)) 32 | 33 | let image = PlatformImage.render( 34 | size: boundingBox.size, 35 | draw: { context in 36 | context.drawPath(path, in: boundingBox) 37 | }) 38 | 39 | self.boundingBox = boundingBox 40 | self.image = image 41 | self.cgImage = image.cgImage 42 | } 43 | 44 | 45 | //MARK: - Calculate Intersections 46 | 47 | public func intersects(_ path: CGPathImage) -> Bool { 48 | return self.intersectionPoints(with: path).count > 0 49 | } 50 | 51 | 52 | public func intersectionPoints(with other: CGPathImage) -> [CGPoint] { 53 | 54 | //fetch raw pixel data 55 | guard 56 | let image1Raw = self.rawImage, 57 | let image2Raw = other.rawImage 58 | else { return [] } 59 | 60 | var intersectionPixels = [CGPoint]() 61 | 62 | let intersectionRect = self.boundingBox.intersection(other.boundingBox) 63 | if intersectionRect.isEmpty { return [] } 64 | 65 | //iterate over intersection of bounding boxes 66 | for x in Int(intersectionRect.minX) ..< Int(intersectionRect.maxX) { 67 | for y in Int(intersectionRect.minY) ..< Int(intersectionRect.maxY) { 68 | 69 | let color1 = image1Raw.colorAt(x: x, y: y) 70 | let color2 = image2Raw.colorAt(x: x, y: y) 71 | 72 | //intersection if significantly darker than 0.5 73 | if color1.alpha > 0.05 && color2.alpha > 0.05 { 74 | intersectionPixels.append(CGPoint(x: x, y: y)) 75 | } 76 | } 77 | } 78 | 79 | if intersectionPixels.count <= 1 { return intersectionPixels } 80 | return intersectionPixels.coalescePoints() 81 | } 82 | 83 | 84 | // MARK: - Debugging Helpers 85 | 86 | /// Renders an image displaying the two `CGPath`s, 87 | /// and highlighting their `intersectionPoints` 88 | func intersectionsImage(with other: CGPathImage) -> PlatformImage { 89 | let totalBoundingBox = self.boundingBox.union(other.boundingBox) 90 | 91 | return PlatformImage.render( 92 | size: totalBoundingBox.size, 93 | draw: { context in 94 | context.setAllowsAntialiasing(false) 95 | context.setShouldAntialias(false) 96 | 97 | func rectInImage(from rect: CGRect) -> CGRect { 98 | return CGRect( 99 | origin: CGPoint( 100 | x: rect.origin.x - totalBoundingBox.minX, 101 | y: rect.origin.y - totalBoundingBox.minY), 102 | size: rect.size) 103 | } 104 | 105 | guard let image1 = self.cgImage, let image2 = other.cgImage else { 106 | return 107 | } 108 | 109 | context.draw(image1, in: rectInImage(from: self.boundingBox)) 110 | context.draw(image2, in: rectInImage(from: other.boundingBox)) 111 | 112 | context.setFillColor(.rgba(1, 0, 0, 1)) 113 | 114 | for intersection in self.intersectionPoints(with: other) { 115 | context.beginPath() 116 | context.addEllipse(in: 117 | rectInImage( 118 | from: CGRect(origin: intersection, size: .zero) 119 | .insetBy(dx: -20, dy: -20))) 120 | 121 | context.closePath() 122 | context.drawPath(using: .fill) 123 | } 124 | }) 125 | } 126 | 127 | /// Renders an image by round-tripping each pixel through the `colorAt(x:y:)` method 128 | func rawPixelImage() -> PlatformImage { 129 | PlatformImage.render(size: boundingBox.size, draw: { context in 130 | guard let rawImage = rawImage else { return } 131 | 132 | let bounds = rawImage.options.bounds 133 | 134 | for x in Int(bounds.minX)...Int(bounds.maxY) { 135 | for y in Int(bounds.minY)...Int(bounds.maxY) { 136 | let pixel = rawImage.colorAt(x: x, y: y) 137 | let color = CGColor.rgba(pixel.red, pixel.green, pixel.blue, pixel.alpha) 138 | context.setFillColor(color) 139 | context.beginPath() 140 | 141 | context.addRect(CGRect( 142 | x: x - Int(bounds.minY) - 5, 143 | y: y - Int(bounds.minY) - 5, 144 | width: 10, 145 | height: 10)) 146 | 147 | context.closePath() 148 | context.drawPath(using: .fill) 149 | } 150 | } 151 | }) 152 | } 153 | } 154 | 155 | extension CGContext { 156 | func drawPath(_ path: CGPath, in boundingBox: CGRect) { 157 | setStrokeColor(.rgba(0, 0, 0, 0.5)) 158 | setLineWidth(2.0) 159 | setAllowsAntialiasing(false) 160 | setShouldAntialias(false) 161 | 162 | var translationToOrigin = CGAffineTransform( 163 | translationX: -boundingBox.minX, 164 | y: -boundingBox.minY) 165 | 166 | let pathAtOrigin = path.copy(using: &translationToOrigin) ?? path 167 | addPath(pathAtOrigin) 168 | drawPath(using: .stroke) 169 | } 170 | } 171 | 172 | extension CGColor { 173 | static func rgba(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat, _ a: CGFloat) -> CGColor { 174 | CGColor( 175 | colorSpace: CGColorSpaceCreateDeviceRGB(), 176 | components: [r, g, b])! 177 | .copy(alpha: a)! 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /CGPathIntersection/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /CGPathIntersection/PlatformImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGImageContext.swift 3 | // CGImageContext 4 | // 5 | // Created by Cal Stephens on 1/22/22. 6 | // Copyright © 2022 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if canImport(UIKit) 12 | import UIKit 13 | public typealias PlatformImage = UIImage 14 | #elseif canImport(AppKit) 15 | import AppKit 16 | public typealias PlatformImage = NSImage 17 | #endif 18 | 19 | extension PlatformImage { 20 | // Renders an image of the given size, using the created `CGContext` 21 | static func render( 22 | size: CGSize, 23 | draw: (CGContext) -> Void) 24 | -> PlatformImage 25 | { 26 | #if canImport(UIKit) 27 | UIGraphicsBeginImageContextWithOptions(size, false, 1.0) 28 | let context = UIGraphicsGetCurrentContext()! 29 | 30 | draw(context) 31 | 32 | let image = UIGraphicsGetImageFromCurrentImageContext()! 33 | UIGraphicsEndImageContext() 34 | return image 35 | #elseif canImport(AppKit) 36 | let imageRepresentation = NSBitmapImageRep( 37 | bitmapDataPlanes: nil, 38 | pixelsWide: Int(size.width), 39 | pixelsHigh: Int(size.height), 40 | bitsPerSample: 8, 41 | samplesPerPixel: 4, 42 | hasAlpha: true, 43 | isPlanar: false, 44 | colorSpaceName: .calibratedRGB, 45 | bytesPerRow: 0, 46 | bitsPerPixel: 0)! 47 | 48 | let context = NSGraphicsContext(bitmapImageRep: imageRepresentation)! 49 | 50 | draw(context.cgContext) 51 | 52 | let image = NSImage(size: size) 53 | image.addRepresentation(imageRepresentation) 54 | return image 55 | #endif 56 | } 57 | } 58 | 59 | #if !canImport(UIKit) 60 | extension PlatformImage { 61 | var cgImage: CGImage? { 62 | cgImage(forProposedRect: nil, context: nil, hints: nil) 63 | } 64 | } 65 | #endif 66 | -------------------------------------------------------------------------------- /CGPathIntersectionTests/CGPathIntersectionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPathIntersectionTests.swift 3 | // CGPathIntersectionTests 4 | // 5 | // Created by Cal Stephens on 11/13/16. 6 | // Copyright © 2016 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import CGPathIntersection 11 | 12 | //lines and circles with intersection positions verified by Wolfram Alpha 13 | class CGPathIntersectionTests: XCTestCase { 14 | 15 | func testWithLinesWhereBoundingBoxesDontIntersect() { 16 | let path1 = CGPath.line(from: CGPoint(x: 50, y: 10), to: CGPoint(x: 60, y: 100)) 17 | let path2 = CGPath.line(from: CGPoint(x: 130, y: 10), to: CGPoint(x: 140, y: 120)) 18 | 19 | XCTAssertFalse(path1.intersects(path2)) 20 | XCTAssertEqual(path1.intersectionPoints(with: path2).count, 0) 21 | } 22 | 23 | func testNoIntersectionFromParallelDiagonalLines() { 24 | let path1 = CGPath.line(from: CGPoint(x: 50, y: 10), to: CGPoint(x: 170, y: 120)) 25 | let path2 = CGPath.line(from: CGPoint(x: 10, y: 10), to: CGPoint(x: 120, y: 120)) 26 | 27 | XCTAssertFalse(path1.intersects(path2)) 28 | XCTAssertEqual(path1.intersectionPoints(with: path2).count, 0) 29 | } 30 | 31 | func testOneIntersectionFromPerpendicularDiagonalLines() { 32 | let path1 = CGPath.line(from: CGPoint(x: 20, y: 20), to: CGPoint(x: 180, y: 180)) 33 | let path2 = CGPath.line(from: CGPoint(x: 180, y: 20), to: CGPoint(x: 20, y: 180)) 34 | 35 | XCTAssertTrue(path1.intersects(path2)) 36 | 37 | let intersectionPoints = path1.intersectionPoints(with: path2) 38 | XCTAssertEqual(intersectionPoints.count, 1) 39 | 40 | let intersectionPoint = intersectionPoints.first ?? .zero 41 | XCTAssertEqual(intersectionPoint.x, 100.0, accuracy: 1.0) 42 | XCTAssertEqual(intersectionPoint.y, 100.0, accuracy: 1.0) 43 | } 44 | 45 | func testOneIntersectionFromPerpendicularStraightLines() { 46 | let path1 = CGPath.line(from: CGPoint(x: 50, y: 0), to: CGPoint(x: 50, y: 100)) 47 | let path2 = CGPath.line(from: CGPoint(x: 0, y: 50), to: CGPoint(x: 100, y: 50)) 48 | 49 | XCTAssertTrue(path1.intersects(path2)) 50 | 51 | let intersectionPoints = path1.intersectionPoints(with: path2) 52 | XCTAssertEqual(intersectionPoints.count, 1) 53 | 54 | let intersectionPoint = intersectionPoints.first ?? .zero 55 | XCTAssertEqual(intersectionPoint.x, 50.0, accuracy: 1.0) 56 | XCTAssertEqual(intersectionPoint.y, 50.0, accuracy: 1.0) 57 | } 58 | 59 | func testOneIntersectionFromArbitraryLines() { 60 | let path1 = CGPath.line(from: CGPoint(x: 123.2, y: 145.5), to: CGPoint(x: 32.87, y: 67.1)) 61 | let path2 = CGPath.line(from: CGPoint(x: 104.7, y: 153.3), to: CGPoint(x: 20.0, y: 10.0)) 62 | 63 | let intersectionPoints = path1.intersectionPoints(with: path2) 64 | XCTAssertEqual(intersectionPoints.count, 1) 65 | 66 | let intersectionPoint = intersectionPoints.first ?? .zero 67 | XCTAssertEqual(intersectionPoint.x, 76.0, accuracy: 1.0) 68 | XCTAssertEqual(intersectionPoint.y, 104.0, accuracy: 1.0) 69 | } 70 | 71 | func testZeroIntersectionFromCircleAndLine() { 72 | let path1 = CGPath.line(from: CGPoint(x: 180, y: 20), to: CGPoint(x: 180, y: 180)) 73 | let path2 = CGPath.circle(at: CGPoint(x: 100, y: 20), withRadius: 20.0) 74 | 75 | XCTAssertFalse(path1.intersects(path2)) 76 | XCTAssertEqual(path1.intersectionPoints(with: path2).count, 0) 77 | } 78 | 79 | func testTwoIntersectionFromCircleAndLine() { 80 | let path1 = CGPath.line(from: CGPoint(x: 20, y: 20), to: CGPoint(x: 180, y: 180)) 81 | let path2 = CGPath.circle(at: CGPoint(x: 100, y: 100), withRadius: 40.0) 82 | 83 | let intersectionPoints = path1.intersectionPoints(with: path2) 84 | XCTAssertEqual(intersectionPoints.count, 2) 85 | 86 | let intersectionPoint1 = intersectionPoints[0] 87 | XCTAssertEqual(intersectionPoint1.x, 72.0, accuracy: 1.0) 88 | XCTAssertEqual(intersectionPoint1.y, 72.0, accuracy: 1.0) 89 | 90 | let intersectionPoint2 = intersectionPoints[1] 91 | XCTAssertEqual(intersectionPoint2.x, 128.0, accuracy: 1.0) 92 | XCTAssertEqual(intersectionPoint2.y, 128.0, accuracy: 1.0) 93 | } 94 | 95 | func testRealLifeExample() { 96 | let path1 = CGPath.line(from: CGPoint(x: 403, y: 1167), to: CGPoint(x: 324, y: 462)) 97 | let path2 = CGPath.line(from: CGPoint(x: 101, y: 835), to: CGPoint(x: 649, y: 659)) 98 | 99 | XCTAssertTrue(path1.intersects(path2)) 100 | 101 | let intersectionPoints = path1.intersectionPoints(with: path2) 102 | XCTAssertEqual(intersectionPoints.count, 1) 103 | 104 | let intersectionPoint = intersectionPoints.first ?? .zero 105 | XCTAssertEqual(intersectionPoint.x, 357.0, accuracy: 1.0) 106 | XCTAssertEqual(intersectionPoint.y, 753.0, accuracy: 1.0) 107 | } 108 | 109 | func testNegativeOrigin1() { 110 | let path1 = CGPath.line(from: .zero, to: CGPoint(x: 1000, y: 1000)) 111 | let path2 = CGPath(rect: CGRect(x: -1000, y: -1000, width: 2000, height: 2000), transform: nil) 112 | 113 | XCTAssertTrue(path1.intersects(path2)) 114 | 115 | let intersectionPoints = path1.intersectionPoints(with: path2) 116 | XCTAssertEqual(intersectionPoints.count, 1) 117 | 118 | let intersectionPoint = intersectionPoints.first ?? .zero 119 | XCTAssertEqual(intersectionPoint.x, 1000, accuracy: 2.0) 120 | XCTAssertEqual(intersectionPoint.y, 1000, accuracy: 2.0) 121 | } 122 | 123 | func testNegativeOrigin2() { 124 | let path1 = CGPath.line(from: .zero, to: CGPoint(x: -1000, y: 1000)) 125 | let path2 = CGPath(rect: CGRect(x: -1000, y: -1000, width: 2000, height: 2000), transform: nil) 126 | 127 | XCTAssertTrue(path1.intersects(path2)) 128 | 129 | let intersectionPoints = path1.intersectionPoints(with: path2) 130 | XCTAssertEqual(intersectionPoints.count, 1) 131 | 132 | let intersectionPoint = intersectionPoints.first ?? .zero 133 | XCTAssertEqual(intersectionPoint.x, -1000, accuracy: 2.0) 134 | XCTAssertEqual(intersectionPoint.y, 1000, accuracy: 2.0) 135 | } 136 | 137 | func testNegativeOrigin3() { 138 | let path1 = CGPath.line(from: .zero, to: CGPoint(x: 1000, y: -1000)) 139 | let path2 = CGPath(rect: CGRect(x: -1000, y: -1000, width: 2000, height: 2000), transform: nil) 140 | 141 | XCTAssertTrue(path1.intersects(path2)) 142 | 143 | let intersectionPoints = path1.intersectionPoints(with: path2) 144 | XCTAssertEqual(intersectionPoints.count, 1) 145 | 146 | let intersectionPoint = intersectionPoints.first ?? .zero 147 | XCTAssertEqual(intersectionPoint.x, 1000, accuracy: 2.0) 148 | XCTAssertEqual(intersectionPoint.y, -1000, accuracy: 2.0) 149 | } 150 | 151 | func testNegativeOrigin4() { 152 | let path1 = CGPath.line(from: .zero, to: CGPoint(x: -1000, y: -1000)) 153 | let path2 = CGPath(rect: CGRect(x: -1000, y: -1000, width: 2000, height: 2000), transform: nil) 154 | 155 | XCTAssertTrue(path1.intersects(path2)) 156 | 157 | let intersectionPoints = path1.intersectionPoints(with: path2) 158 | XCTAssertEqual(intersectionPoints.count, 1) 159 | 160 | let intersectionPoint = intersectionPoints.first ?? .zero 161 | XCTAssertEqual(intersectionPoint.x, -1000, accuracy: 2.0) 162 | XCTAssertEqual(intersectionPoint.y, -1000, accuracy: 2.0) 163 | } 164 | 165 | func testIntersectionGithubIssue2() { 166 | let path0 = CGMutablePath() 167 | path0.move(to: CGPoint(x: 798.6, y: 575.5)) 168 | path0.addLine(to: CGPoint(x: 798.6, y: 573.5)) 169 | path0.addLine(to: CGPoint(x: 794.4, y: 573.5)) 170 | path0.addLine(to: CGPoint(x: 794.4, y: 575.5)) 171 | path0.move(to: CGPoint(x: 798.6, y: 573.5)) 172 | path0.addLine(to: CGPoint(x: 798.6, y: 569.5)) 173 | path0.addLine(to: CGPoint(x: 794.4, y: 569.5)) 174 | path0.addLine(to: CGPoint(x: 794.4, y: 573.5)) 175 | path0.move(to: CGPoint(x: 798.6, y: 569.5)) 176 | path0.addLine(to: CGPoint(x: 798.6, y: 562.5)) 177 | path0.addLine(to: CGPoint(x: 794.4, y: 562.5)) 178 | path0.addLine(to: CGPoint(x: 794.4, y: 569.5)) 179 | path0.move(to: CGPoint(x: 798.6, y: 562.5)) 180 | path0.addLine(to: CGPoint(x: 798.59, y: 537.798)) 181 | path0.addLine(to: CGPoint(x: 794.41, y: 538.202)) 182 | path0.addLine(to: CGPoint(x: 794.4, y: 562.5)) 183 | path0.move(to: CGPoint(x: 798.59, y: 537.798)) 184 | path0.addLine(to: CGPoint(x: 793.035, y: 504.98)) 185 | path0.addLine(to: CGPoint(x: 788.965, y: 506.02)) 186 | path0.addLine(to: CGPoint(x: 794.41, y: 538.202)) 187 | path0.move(to: CGPoint(x: 793.035, y: 504.98)) 188 | path0.addLine(to: CGPoint(x: 780.489, y: 466.827)) 189 | path0.addLine(to: CGPoint(x: 776.511, y: 468.173)) 190 | path0.addLine(to: CGPoint(x: 788.965, y: 506.02)) 191 | path0.move(to: CGPoint(x: 780.489, y: 466.827)) 192 | path0.addLine(to: CGPoint(x: 770.461, y: 438.249)) 193 | path0.addLine(to: CGPoint(x: 766.539, y: 439.751)) 194 | path0.addLine(to: CGPoint(x: 776.511, y: 468.173)) 195 | path0.move(to: CGPoint(x: 770.461, y: 438.249)) 196 | path0.addLine(to: CGPoint(x: 762.422, y: 419.654)) 197 | path0.addLine(to: CGPoint(x: 758.578, y: 421.346)) 198 | path0.addLine(to: CGPoint(x: 766.539, y: 439.751)) 199 | path0.move(to: CGPoint(x: 762.422, y: 419.654)) 200 | path0.addLine(to: CGPoint(x: 759.393, y: 413.091)) 201 | path0.addLine(to: CGPoint(x: 755.607, y: 414.909)) 202 | path0.addLine(to: CGPoint(x: 758.578, y: 421.346)) 203 | path0.move(to: CGPoint(x: 759.393, y: 413.091)) 204 | path0.addLine(to: CGPoint(x: 756.438, y: 407.192)) 205 | path0.addLine(to: CGPoint(x: 752.562, y: 408.808)) 206 | path0.addLine(to: CGPoint(x: 755.607, y: 414.909)) 207 | path0.move(to: CGPoint(x: 756.438, y: 407.192)) 208 | path0.addLine(to: CGPoint(x: 754.474, y: 401.282)) 209 | path0.addLine(to: CGPoint(x: 750.526, y: 402.718)) 210 | path0.addLine(to: CGPoint(x: 752.562, y: 408.808)) 211 | path0.move(to: CGPoint(x: 754.474, y: 401.282)) 212 | path0.addLine(to: CGPoint(x: 752.492, y: 396.336)) 213 | path0.addLine(to: CGPoint(x: 748.508, y: 397.664)) 214 | path0.addLine(to: CGPoint(x: 750.526, y: 402.718)) 215 | path0.move(to: CGPoint(x: 752.492, y: 396.336)) 216 | path0.addLine(to: CGPoint(x: 751.519, y: 392.423)) 217 | path0.addLine(to: CGPoint(x: 747.481, y: 393.577)) 218 | path0.addLine(to: CGPoint(x: 748.508, y: 397.664)) 219 | path0.move(to: CGPoint(x: 751.519, y: 392.423)) 220 | path0.addLine(to: CGPoint(x: 750.46, y: 389.246)) 221 | path0.addLine(to: CGPoint(x: 746.54, y: 390.754)) 222 | path0.addLine(to: CGPoint(x: 747.481, y: 393.577)) 223 | path0.move(to: CGPoint(x: 750.46, y: 389.246)) 224 | path0.addLine(to: CGPoint(x: 749.037, y: 385.991)) 225 | path0.addLine(to: CGPoint(x: 744.963, y: 387.009)) 226 | path0.addLine(to: CGPoint(x: 746.54, y: 390.754)) 227 | path0.move(to: CGPoint(x: 749.037, y: 385.991)) 228 | path0.addLine(to: CGPoint(x: 749.1, y: 384)) 229 | path0.addLine(to: CGPoint(x: 744.9, y: 384)) 230 | path0.addLine(to: CGPoint(x: 744.963, y: 387.009)) 231 | path0.move(to: CGPoint(x: 749.1, y: 384)) 232 | path0.addLine(to: CGPoint(x: 748.95, y: 381.72)) 233 | path0.addLine(to: CGPoint(x: 745.05, y: 383.28)) 234 | path0.addLine(to: CGPoint(x: 744.9, y: 384)) 235 | path0.move(to: CGPoint(x: 748.95, y: 381.72)) 236 | path0.addLine(to: CGPoint(x: 747.747, y: 380.335)) 237 | path0.addLine(to: CGPoint(x: 744.253, y: 382.665)) 238 | path0.addLine(to: CGPoint(x: 745.05, y: 383.28)) 239 | path0.move(to: CGPoint(x: 747.747, y: 380.335)) 240 | path0.addLine(to: CGPoint(x: 747.037, y: 378.991)) 241 | path0.addLine(to: CGPoint(x: 742.963, y: 380.009)) 242 | path0.addLine(to: CGPoint(x: 744.253, y: 382.665)) 243 | path0.move(to: CGPoint(x: 747.037, y: 378.991)) 244 | path0.addLine(to: CGPoint(x: 746.519, y: 374.923)) 245 | path0.addLine(to: CGPoint(x: 742.481, y: 376.077)) 246 | path0.addLine(to: CGPoint(x: 742.963, y: 380.009)) 247 | path0.move(to: CGPoint(x: 746.519, y: 374.923)) 248 | path0.addLine(to: CGPoint(x: 745.007, y: 371.882)) 249 | path0.addLine(to: CGPoint(x: 740.993, y: 373.118)) 250 | path0.addLine(to: CGPoint(x: 742.481, y: 376.077)) 251 | path0.move(to: CGPoint(x: 745.007, y: 371.882)) 252 | path0.addLine(to: CGPoint(x: 744.595, y: 368.86)) 253 | path0.addLine(to: CGPoint(x: 740.405, y: 369.14)) 254 | path0.addLine(to: CGPoint(x: 740.993, y: 373.118)) 255 | path0.move(to: CGPoint(x: 744.595, y: 368.86)) 256 | path0.addLine(to: CGPoint(x: 744.571, y: 364.655)) 257 | path0.addLine(to: CGPoint(x: 740.429, y: 365.345)) 258 | path0.addLine(to: CGPoint(x: 740.405, y: 369.14)) 259 | path0.move(to: CGPoint(x: 744.571, y: 364.655)) 260 | path0.addLine(to: CGPoint(x: 743.526, y: 362.447)) 261 | path0.addLine(to: CGPoint(x: 739.474, y: 363.553)) 262 | path0.addLine(to: CGPoint(x: 740.429, y: 365.345)) 263 | path0.move(to: CGPoint(x: 743.526, y: 362.447)) 264 | path0.addLine(to: CGPoint(x: 743.076, y: 359.181)) 265 | path0.addLine(to: CGPoint(x: 738.924, y: 359.819)) 266 | path0.addLine(to: CGPoint(x: 739.474, y: 363.553)) 267 | path0.move(to: CGPoint(x: 743.076, y: 359.181)) 268 | path0.addLine(to: CGPoint(x: 742.588, y: 356.28)) 269 | path0.addLine(to: CGPoint(x: 738.412, y: 356.72)) 270 | path0.addLine(to: CGPoint(x: 738.924, y: 359.819)) 271 | path0.move(to: CGPoint(x: 742.588, y: 356.28)) 272 | path0.addLine(to: CGPoint(x: 742.098, y: 349.9)) 273 | path0.addLine(to: CGPoint(x: 737.902, y: 350.1)) 274 | path0.addLine(to: CGPoint(x: 738.412, y: 356.72)) 275 | path0.move(to: CGPoint(x: 742.098, y: 349.9)) 276 | path0.addLine(to: CGPoint(x: 742.1, y: 346)) 277 | path0.addLine(to: CGPoint(x: 737.9, y: 346)) 278 | path0.addLine(to: CGPoint(x: 737.902, y: 350.1)) 279 | path0.move(to: CGPoint(x: 742.1, y: 346)) 280 | path0.addLine(to: CGPoint(x: 742.1, y: 343.5)) 281 | path0.addLine(to: CGPoint(x: 737.9, y: 343.5)) 282 | path0.addLine(to: CGPoint(x: 737.9, y: 346)) 283 | path0.move(to: CGPoint(x: 742.1, y: 343.5)) 284 | path0.addLine(to: CGPoint(x: 742.1, y: 342)) 285 | path0.addLine(to: CGPoint(x: 737.9, y: 342)) 286 | path0.addLine(to: CGPoint(x: 737.9, y: 343.5)) 287 | path0.move(to: CGPoint(x: 742.1, y: 342)) 288 | path0.addLine(to: CGPoint(x: 742.1, y: 340)) 289 | path0.addLine(to: CGPoint(x: 737.9, y: 340)) 290 | path0.addLine(to: CGPoint(x: 737.9, y: 342)) 291 | path0.move(to: CGPoint(x: 742.1, y: 340)) 292 | path0.addLine(to: CGPoint(x: 742.087, y: 338.732)) 293 | path0.addLine(to: CGPoint(x: 737.913, y: 338.268)) 294 | path0.addLine(to: CGPoint(x: 737.9, y: 340)) 295 | path0.move(to: CGPoint(x: 742.087, y: 338.732)) 296 | path0.addLine(to: CGPoint(x: 743.561, y: 326.901)) 297 | path0.addLine(to: CGPoint(x: 739.439, y: 326.099)) 298 | path0.addLine(to: CGPoint(x: 737.913, y: 338.268)) 299 | path0.move(to: CGPoint(x: 743.561, y: 326.901)) 300 | path0.addLine(to: CGPoint(x: 745.45, y: 321.28)) 301 | path0.addLine(to: CGPoint(x: 741.55, y: 319.72)) 302 | path0.addLine(to: CGPoint(x: 739.439, y: 326.099)) 303 | path0.move(to: CGPoint(x: 745.45, y: 321.28)) 304 | path0.addLine(to: CGPoint(x: 747.421, y: 317.349)) 305 | path0.addLine(to: CGPoint(x: 743.579, y: 315.651)) 306 | path0.addLine(to: CGPoint(x: 741.55, y: 319.72)) 307 | path0.move(to: CGPoint(x: 747.421, y: 317.349)) 308 | path0.addLine(to: CGPoint(x: 764.412, y: 278.369)) 309 | path0.addLine(to: CGPoint(x: 760.588, y: 276.631)) 310 | path0.addLine(to: CGPoint(x: 743.579, y: 315.651)) 311 | path0.move(to: CGPoint(x: 764.412, y: 278.369)) 312 | path0.addLine(to: CGPoint(x: 769.902, y: 267.89)) 313 | path0.addLine(to: CGPoint(x: 766.098, y: 266.11)) 314 | path0.addLine(to: CGPoint(x: 760.588, y: 276.631)) 315 | path0.move(to: CGPoint(x: 769.902, y: 267.89)) 316 | path0.addLine(to: CGPoint(x: 775.408, y: 254.877)) 317 | path0.addLine(to: CGPoint(x: 771.592, y: 253.123)) 318 | path0.addLine(to: CGPoint(x: 766.098, y: 266.11)) 319 | path0.move(to: CGPoint(x: 775.408, y: 254.877)) 320 | path0.addLine(to: CGPoint(x: 778.378, y: 249.439)) 321 | path0.addLine(to: CGPoint(x: 774.622, y: 247.561)) 322 | path0.addLine(to: CGPoint(x: 771.592, y: 253.123)) 323 | path0.move(to: CGPoint(x: 778.378, y: 249.439)) 324 | path0.addLine(to: CGPoint(x: 781.856, y: 241.983)) 325 | path0.addLine(to: CGPoint(x: 778.144, y: 240.017)) 326 | path0.addLine(to: CGPoint(x: 774.622, y: 247.561)) 327 | path0.move(to: CGPoint(x: 781.856, y: 241.983)) 328 | path0.addLine(to: CGPoint(x: 787.295, y: 232.59)) 329 | path0.addLine(to: CGPoint(x: 783.705, y: 230.41)) 330 | path0.addLine(to: CGPoint(x: 778.144, y: 240.017)) 331 | path0.move(to: CGPoint(x: 787.295, y: 232.59)) 332 | path0.addLine(to: CGPoint(x: 790.323, y: 228.042)) 333 | path0.addLine(to: CGPoint(x: 786.677, y: 225.958)) 334 | path0.addLine(to: CGPoint(x: 783.705, y: 230.41)) 335 | path0.move(to: CGPoint(x: 790.323, y: 228.042)) 336 | path0.addLine(to: CGPoint(x: 793.216, y: 222.211)) 337 | path0.addLine(to: CGPoint(x: 789.784, y: 219.789)) 338 | path0.addLine(to: CGPoint(x: 786.677, y: 225.958)) 339 | path0.move(to: CGPoint(x: 793.216, y: 222.211)) 340 | path0.addLine(to: CGPoint(x: 796.054, y: 219.913)) 341 | path0.addLine(to: CGPoint(x: 792.946, y: 217.087)) 342 | path0.addLine(to: CGPoint(x: 789.784, y: 219.789)) 343 | path0.move(to: CGPoint(x: 796.054, y: 219.913)) 344 | path0.addLine(to: CGPoint(x: 798.227, y: 216.695)) 345 | path0.addLine(to: CGPoint(x: 794.773, y: 214.305)) 346 | path0.addLine(to: CGPoint(x: 792.946, y: 217.087)) 347 | path0.move(to: CGPoint(x: 798.227, y: 216.695)) 348 | path0.addLine(to: CGPoint(x: 800.558, y: 213.408)) 349 | path0.addLine(to: CGPoint(x: 797.442, y: 210.592)) 350 | path0.addLine(to: CGPoint(x: 794.773, y: 214.305)) 351 | path0.move(to: CGPoint(x: 800.558, y: 213.408)) 352 | path0.addLine(to: CGPoint(x: 811.958, y: 201.512)) 353 | path0.addLine(to: CGPoint(x: 809.042, y: 198.488)) 354 | path0.addLine(to: CGPoint(x: 797.442, y: 210.592)) 355 | path0.move(to: CGPoint(x: 811.958, y: 201.512)) 356 | path0.addLine(to: CGPoint(x: 814.101, y: 200.288)) 357 | path0.addLine(to: CGPoint(x: 811.899, y: 196.712)) 358 | path0.addLine(to: CGPoint(x: 809.042, y: 198.488)) 359 | path0.move(to: CGPoint(x: 814.101, y: 200.288)) 360 | path0.addLine(to: CGPoint(x: 817.996, y: 197.849)) 361 | path0.addLine(to: CGPoint(x: 816.004, y: 194.151)) 362 | path0.addLine(to: CGPoint(x: 811.899, y: 196.712)) 363 | path0.move(to: CGPoint(x: 817.996, y: 197.849)) 364 | path0.addLine(to: CGPoint(x: 820.353, y: 196.919)) 365 | path0.addLine(to: CGPoint(x: 818.647, y: 193.081)) 366 | path0.addLine(to: CGPoint(x: 816.004, y: 194.151)) 367 | path0.move(to: CGPoint(x: 820.353, y: 196.919)) 368 | path0.addLine(to: CGPoint(x: 822.404, y: 195.896)) 369 | path0.addLine(to: CGPoint(x: 820.596, y: 192.104)) 370 | path0.addLine(to: CGPoint(x: 818.647, y: 193.081)) 371 | path0.move(to: CGPoint(x: 822.404, y: 195.896)) 372 | path0.addLine(to: CGPoint(x: 824.894, y: 194.754)) 373 | path0.addLine(to: CGPoint(x: 823.143, y: 190.936)) 374 | path0.addLine(to: CGPoint(x: 820.596, y: 192.104)) 375 | 376 | let path1 = CGMutablePath() 377 | path1.move(to: CGPoint(x: 1049, y: 215.4)) 378 | path1.addLine(to: CGPoint(x: 1005, y: 215.4)) 379 | path1.addLine(to: CGPoint(x: 1005, y: 219.6)) 380 | path1.addLine(to: CGPoint(x: 1049, y: 219.6)) 381 | path1.move(to: CGPoint(x: 1005, y: 215.4)) 382 | path1.addLine(to: CGPoint(x: 962.829, y: 215.407)) 383 | path1.addLine(to: CGPoint(x: 963.171, y: 219.593)) 384 | path1.addLine(to: CGPoint(x: 1005, y: 219.6)) 385 | path1.move(to: CGPoint(x: 962.829, y: 215.407)) 386 | path1.addLine(to: CGPoint(x: 900.634, y: 223.932)) 387 | path1.addLine(to: CGPoint(x: 901.366, y: 228.068)) 388 | path1.addLine(to: CGPoint(x: 963.171, y: 219.593)) 389 | path1.move(to: CGPoint(x: 900.634, y: 223.932)) 390 | path1.addLine(to: CGPoint(x: 866.398, y: 232.488)) 391 | path1.addLine(to: CGPoint(x: 867.602, y: 236.512)) 392 | path1.addLine(to: CGPoint(x: 901.366, y: 228.068)) 393 | path1.move(to: CGPoint(x: 866.398, y: 232.488)) 394 | path1.addLine(to: CGPoint(x: 851.413, y: 238.703)) 395 | path1.addLine(to: CGPoint(x: 853.587, y: 242.297)) 396 | path1.addLine(to: CGPoint(x: 867.602, y: 236.512)) 397 | path1.move(to: CGPoint(x: 851.413, y: 238.703)) 398 | path1.addLine(to: CGPoint(x: 843.666, y: 246.478)) 399 | path1.addLine(to: CGPoint(x: 847.334, y: 248.522)) 400 | path1.addLine(to: CGPoint(x: 853.587, y: 242.297)) 401 | path1.move(to: CGPoint(x: 843.666, y: 246.478)) 402 | path1.addLine(to: CGPoint(x: 833.607, y: 270.091)) 403 | path1.addLine(to: CGPoint(x: 837.393, y: 271.909)) 404 | path1.addLine(to: CGPoint(x: 847.334, y: 248.522)) 405 | path1.move(to: CGPoint(x: 833.607, y: 270.091)) 406 | path1.addLine(to: CGPoint(x: 824.782, y: 285.83)) 407 | path1.addLine(to: CGPoint(x: 828.446, y: 287.883)) 408 | path1.addLine(to: CGPoint(x: 837.393, y: 271.909)) 409 | 410 | XCTAssertFalse(path0.intersects(path1)) 411 | } 412 | 413 | } 414 | 415 | 416 | //MARK: - Convenience Helpers 417 | 418 | extension CGPath { 419 | 420 | static func line(from start: CGPoint, to end: CGPoint) -> CGPath { 421 | let path = CGMutablePath() 422 | path.move(to: start) 423 | path.addLine(to: end) 424 | path.closeSubpath() 425 | return path 426 | } 427 | 428 | static func circle(at center: CGPoint, withRadius radius: CGFloat) -> CGPath { 429 | CGPath( 430 | ellipseIn: CGRect( 431 | x: center.x - radius, 432 | y: center.y - radius, 433 | width: radius * 2, 434 | height: radius * 2), 435 | transform: nil) 436 | } 437 | 438 | } 439 | -------------------------------------------------------------------------------- /CGPathIntersectionTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' do 2 | gem 'cocoapods', '~> 1.11.0' 3 | gem "rake", "~> 13.0.0" 4 | end 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Cal Stephens 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 | -------------------------------------------------------------------------------- /Mintfile: -------------------------------------------------------------------------------- 1 | thii/xcbeautify@0.10.2 2 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "CGPathIntersection", 6 | platforms: [.iOS(.v9), .macOS(.v10_10), .tvOS(.v9)], 7 | products: [ 8 | .library( 9 | name: "CGPathIntersection", 10 | targets: ["CGPathIntersection"]), 11 | ], 12 | targets: [ 13 | .target( 14 | name: "CGPathIntersection", 15 | path: "CGPathIntersection"), 16 | .testTarget( 17 | name: "CGPathIntersectionTests", 18 | dependencies: ["CGPathIntersection"], 19 | path: "CGPathIntersectionTests"), 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CGPathIntersection 2 | 3 | [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcalda%2FCGPathIntersection%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/calda/CGPathIntersection) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcalda%2FCGPathIntersection%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/calda/CGPathIntersection) 4 | 5 | **CGPathIntersection** is a library for iOS, macOS, and tvOS that identifies points where two `CGPath`s intersect. 6 | 7 | Surprisingly, this is not provided out-of-the-box by `CoreGraphics`. Intersections can be calculated analytically for simple geometric shapes (especially straight lines), but that method becomes rather challenging when considering a `CGPath` can be arbitrarily complex. `CGPathIntersection` solves this problem by rendering each path into an image and then finding the exact pixels where they intersect. 8 | 9 | ## Installation 10 | #### [Swift Package Manager](https://www.swift.org/package-manager/) 11 | Add the following dependency to your package definition: 12 | 13 | ```swift 14 | .package( 15 | name: "CGPathIntersection", 16 | url: "https://github.com/calda/CGPathIntersection.git", 17 | from: "4.0.0") 18 | ``` 19 | 20 | #### [Carthage](https://github.com/Carthage/Carthage) 21 | Add `github "calda/CGPathIntersection"` to your Cartfile 22 | 23 | #### [CocoaPods](https://github.com/cocoapods/cocoapods) 24 | Add `pod 'CGPathIntersection'` to your Podfile 25 | 26 | ## Usage 27 | 28 | ```swift 29 | import CGPathIntersection 30 | 31 | let path1 = CGPath(...) 32 | let path2 = CGPath(...) 33 | 34 | path1.intersects(path2) // returns a boolean 35 | path1.intersectionPoints(with: path2) // returns an array of points 36 | ``` 37 | 38 | If performing many calculations, you can increase performance by creating a `CGPathImage`. Any calculations performed on a pre-existing `CGPathImage` will run faster than the same calculation performed on a raw `CGPath`. 39 | 40 | ```swift 41 | import CGPathIntersection 42 | 43 | let pathImage = CGPathImage(from: CGPath(...)) 44 | let otherPathImages: [CGPathImage] = [...] 45 | 46 | let intersectingPaths = otherPathImages.filter { pathImage.intersects($0) } 47 | ``` 48 | 49 | ## Example 50 | 51 | CGPathIntersection was created as a component of **[Streets](http://github.com/calda/Streets)**, a prototype SpriteKit game that simulates managing a network of streets. Streets uses CGPathIntersection to connect individual roads together with physical intersections. When a car reaches an intersection, it makes a random turn onto one of the other connected roads. 52 | 53 |

54 | 55 |

56 | 57 | Streets also has some support for more complex paths, like roundabouts: 58 | 59 |

60 | 61 |

62 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | namespace :build do 2 | task :iOS do 3 | xcodebuild('build -scheme CGPathIntersection-iOS -destination generic/platform=iOS') 4 | end 5 | task :macOS do 6 | sh 'swift build' 7 | end 8 | task :tvOS do 9 | xcodebuild('build -scheme CGPathIntersection-tvOS -destination generic/platform=tvOS') 10 | end 11 | end 12 | 13 | namespace :test do 14 | task :iOS do 15 | xcodebuild('test -scheme CGPathIntersection-iOS -destination "platform=iOS Simulator,name=iPhone 12"') 16 | end 17 | task :macOS do 18 | sh 'swift test' 19 | end 20 | task :tvOS do 21 | xcodebuild('test -scheme CGPathIntersection-tvOS -destination "platform=tvOS Simulator,name=Apple TV"') 22 | end 23 | end 24 | 25 | namespace :lint do 26 | desc 'Lints the CocoaPods podspec' 27 | task :podspec do 28 | sh 'pod lib lint CGPathIntersection.podspec' 29 | end 30 | end 31 | 32 | def xcodebuild(command) 33 | sh "set -o pipefail && xcodebuild #{command} | mint run xcbeautify" 34 | end 35 | -------------------------------------------------------------------------------- /images/roundabout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calda/CGPathIntersection/364362e964bdc75f6a70482f92192b7b670e6d55/images/roundabout.jpg -------------------------------------------------------------------------------- /images/streets 2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calda/CGPathIntersection/364362e964bdc75f6a70482f92192b7b670e6d55/images/streets 2.gif -------------------------------------------------------------------------------- /images/streets.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calda/CGPathIntersection/364362e964bdc75f6a70482f92192b7b670e6d55/images/streets.gif --------------------------------------------------------------------------------