├── .gitignore ├── BooleanPath.podspec ├── BooleanPath.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── takuto.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ ├── BooleanPath.xcscheme │ │ └── BooleanPath_Demo.xcscheme └── xcuserdata │ └── takuto.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── BooleanPath ├── BPBezierContour.swift ├── BPBezierCurve.swift ├── BPBezierCurve_Edge.swift ├── BPBezierGraph.swift ├── BPBezierIntersectRange.swift ├── BPBezierIntersection.swift ├── BPContourOverlap.swift ├── BPCurveLocation.swift ├── BPEdgeCrossing.swift ├── BPEdgeOverlap.swift ├── BPEdgeOverlapRun.swift ├── BPGeometry.swift ├── CGPath_Bridge │ ├── CGPath_Extension.swift │ └── LRTBezierPathWrapper.swift ├── Info.plist ├── Int_Extension.swift ├── Legendre.swift ├── NSBezierPath_Boolean.swift └── NSBezierPath_Utilities.swift ├── BooleanPath_Demo ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── BooleanPath_Demo.entitlements ├── DemoView.swift ├── Info.plist └── ViewController.swift ├── Carthage └── Build │ └── Mac │ ├── BooleanPath.framework.dSYM │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ └── DWARF │ │ └── BooleanPath │ └── BooleanPath.framework │ ├── BooleanPath │ ├── Headers │ ├── Modules │ ├── Resources │ └── Versions │ ├── A │ ├── BooleanPath │ ├── Headers │ │ └── BooleanPath-Swift.h │ ├── Modules │ │ ├── BooleanPath.swiftmodule │ │ │ ├── x86_64.swiftdoc │ │ │ └── x86_64.swiftmodule │ │ └── module.modulemap │ └── Resources │ │ └── Info.plist │ └── Current ├── LICENSE ├── README.md └── images └── sample.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac 2 | .DS_Store 3 | 4 | # Xcode 5 | xcuserdata/ 6 | *.xcuserstate 7 | 8 | # Swift Package Manager 9 | Packages.resolved 10 | .swiftpm/ 11 | .build/ 12 | -------------------------------------------------------------------------------- /BooleanPath.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "BooleanPath" 3 | spec.version = "1.0" 4 | spec.summary = "Add boolean operations to NSBezierPath like the pathfinder of Adobe Illustrator." 5 | spec.description = <<-DESC 6 | This is a rewrite of VectorBoolean written by Leslie Titze's. 7 | BooleanPath is written by Swift for macOS. 8 | DESC 9 | spec.homepage = "https://github.com/Kyome22/BooleanPath" 10 | spec.license = { :type => "MIT", :file => "LICENSE" } 11 | spec.author = { "Takuto Nakamura" => "kyomesuke@icloud.com" } 12 | spec.social_media_url = "https://twitter.com/Kyomesuke3" 13 | spec.osx.deployment_target = '10.10' 14 | spec.source = { :git => "https://github.com/Kyome22/BooleanPath.git", :tag => "#{spec.version}" } 15 | spec.frameworks = 'Foundation', 'Cocoa', 'QuartzCore' 16 | spec.source_files = "BooleanPath/**/*.swift" 17 | spec.swift_version = "4.2" 18 | spec.requires_arc = true 19 | end 20 | -------------------------------------------------------------------------------- /BooleanPath.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1C6B72282234D574005B6EEF /* Int_Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72272234D574005B6EEF /* Int_Extension.swift */; }; 11 | 1C6B722A2234D585005B6EEF /* Legendre.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72292234D585005B6EEF /* Legendre.swift */; }; 12 | 1C6B722C2234D5A8005B6EEF /* CGPath_Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B722B2234D5A8005B6EEF /* CGPath_Extension.swift */; }; 13 | 1C6B722E2234D5C3005B6EEF /* LRTBezierPathWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B722D2234D5C3005B6EEF /* LRTBezierPathWrapper.swift */; }; 14 | 1C6B72302234D5D2005B6EEF /* NSBezierPath_Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B722F2234D5D2005B6EEF /* NSBezierPath_Utilities.swift */; }; 15 | 1C6B72322234D5F7005B6EEF /* NSBezierPath_Boolean.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72312234D5F7005B6EEF /* NSBezierPath_Boolean.swift */; }; 16 | 1C6B72342234D608005B6EEF /* BPGeometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72332234D608005B6EEF /* BPGeometry.swift */; }; 17 | 1C6B72362234D615005B6EEF /* BPBezierGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72352234D615005B6EEF /* BPBezierGraph.swift */; }; 18 | 1C6B72382234D624005B6EEF /* BPBezierCurve.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72372234D624005B6EEF /* BPBezierCurve.swift */; }; 19 | 1C6B723A2234D637005B6EEF /* BPBezierCurve_Edge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72392234D637005B6EEF /* BPBezierCurve_Edge.swift */; }; 20 | 1C6B723C2234D64E005B6EEF /* BPBezierContour.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B723B2234D64E005B6EEF /* BPBezierContour.swift */; }; 21 | 1C6B723E2234D65E005B6EEF /* BPBezierIntersection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B723D2234D65E005B6EEF /* BPBezierIntersection.swift */; }; 22 | 1C6B72402234D679005B6EEF /* BPBezierIntersectRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B723F2234D679005B6EEF /* BPBezierIntersectRange.swift */; }; 23 | 1C6B72422234D69C005B6EEF /* BPCurveLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72412234D69C005B6EEF /* BPCurveLocation.swift */; }; 24 | 1C6B72442234D6AB005B6EEF /* BPEdgeCrossing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72432234D6AB005B6EEF /* BPEdgeCrossing.swift */; }; 25 | 1C6B72462234D6BE005B6EEF /* BPEdgeOverlap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72452234D6BE005B6EEF /* BPEdgeOverlap.swift */; }; 26 | 1C6B72482234D6D1005B6EEF /* BPEdgeOverlapRun.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72472234D6D1005B6EEF /* BPEdgeOverlapRun.swift */; }; 27 | 1C6B724A2234D6E0005B6EEF /* BPContourOverlap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72492234D6E0005B6EEF /* BPContourOverlap.swift */; }; 28 | 1C6B72542234E5E6005B6EEF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72532234E5E6005B6EEF /* AppDelegate.swift */; }; 29 | 1C6B72562234E5E6005B6EEF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72552234E5E6005B6EEF /* ViewController.swift */; }; 30 | 1C6B72582234E5E7005B6EEF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1C6B72572234E5E7005B6EEF /* Assets.xcassets */; }; 31 | 1C6B725B2234E5E7005B6EEF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1C6B72592234E5E7005B6EEF /* Main.storyboard */; }; 32 | 1C6B72612234E5F9005B6EEF /* BooleanPath.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C6B721C2234D51A005B6EEF /* BooleanPath.framework */; }; 33 | 1C6B72622234E5F9005B6EEF /* BooleanPath.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 1C6B721C2234D51A005B6EEF /* BooleanPath.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34 | 1C6B72672234E658005B6EEF /* DemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B72662234E658005B6EEF /* DemoView.swift */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXContainerItemProxy section */ 38 | 1C6B72632234E5F9005B6EEF /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 1C6B72132234D519005B6EEF /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = 1C6B721B2234D51A005B6EEF; 43 | remoteInfo = BooleanPath; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXCopyFilesBuildPhase section */ 48 | 1C6B72652234E5F9005B6EEF /* Embed Frameworks */ = { 49 | isa = PBXCopyFilesBuildPhase; 50 | buildActionMask = 2147483647; 51 | dstPath = ""; 52 | dstSubfolderSpec = 10; 53 | files = ( 54 | 1C6B72622234E5F9005B6EEF /* BooleanPath.framework in Embed Frameworks */, 55 | ); 56 | name = "Embed Frameworks"; 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXCopyFilesBuildPhase section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | 1C6B721C2234D51A005B6EEF /* BooleanPath.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BooleanPath.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 1C6B72202234D51A005B6EEF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | 1C6B72272234D574005B6EEF /* Int_Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Int_Extension.swift; sourceTree = ""; }; 65 | 1C6B72292234D585005B6EEF /* Legendre.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Legendre.swift; sourceTree = ""; }; 66 | 1C6B722B2234D5A8005B6EEF /* CGPath_Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGPath_Extension.swift; sourceTree = ""; }; 67 | 1C6B722D2234D5C3005B6EEF /* LRTBezierPathWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LRTBezierPathWrapper.swift; sourceTree = ""; }; 68 | 1C6B722F2234D5D2005B6EEF /* NSBezierPath_Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSBezierPath_Utilities.swift; sourceTree = ""; }; 69 | 1C6B72312234D5F7005B6EEF /* NSBezierPath_Boolean.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSBezierPath_Boolean.swift; sourceTree = ""; }; 70 | 1C6B72332234D608005B6EEF /* BPGeometry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPGeometry.swift; sourceTree = ""; }; 71 | 1C6B72352234D615005B6EEF /* BPBezierGraph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPBezierGraph.swift; sourceTree = ""; }; 72 | 1C6B72372234D624005B6EEF /* BPBezierCurve.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPBezierCurve.swift; sourceTree = ""; }; 73 | 1C6B72392234D637005B6EEF /* BPBezierCurve_Edge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPBezierCurve_Edge.swift; sourceTree = ""; }; 74 | 1C6B723B2234D64E005B6EEF /* BPBezierContour.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPBezierContour.swift; sourceTree = ""; }; 75 | 1C6B723D2234D65E005B6EEF /* BPBezierIntersection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPBezierIntersection.swift; sourceTree = ""; }; 76 | 1C6B723F2234D679005B6EEF /* BPBezierIntersectRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPBezierIntersectRange.swift; sourceTree = ""; }; 77 | 1C6B72412234D69C005B6EEF /* BPCurveLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPCurveLocation.swift; sourceTree = ""; }; 78 | 1C6B72432234D6AB005B6EEF /* BPEdgeCrossing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPEdgeCrossing.swift; sourceTree = ""; }; 79 | 1C6B72452234D6BE005B6EEF /* BPEdgeOverlap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPEdgeOverlap.swift; sourceTree = ""; }; 80 | 1C6B72472234D6D1005B6EEF /* BPEdgeOverlapRun.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPEdgeOverlapRun.swift; sourceTree = ""; }; 81 | 1C6B72492234D6E0005B6EEF /* BPContourOverlap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BPContourOverlap.swift; sourceTree = ""; }; 82 | 1C6B72512234E5E6005B6EEF /* BooleanPath_Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BooleanPath_Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 83 | 1C6B72532234E5E6005B6EEF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 84 | 1C6B72552234E5E6005B6EEF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 85 | 1C6B72572234E5E7005B6EEF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 86 | 1C6B725A2234E5E7005B6EEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 87 | 1C6B725C2234E5E7005B6EEF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 88 | 1C6B725D2234E5E7005B6EEF /* BooleanPath_Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = BooleanPath_Demo.entitlements; sourceTree = ""; }; 89 | 1C6B72662234E658005B6EEF /* DemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoView.swift; sourceTree = ""; }; 90 | /* End PBXFileReference section */ 91 | 92 | /* Begin PBXFrameworksBuildPhase section */ 93 | 1C6B72192234D51A005B6EEF /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | 1C6B724E2234E5E6005B6EEF /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | 1C6B72612234E5F9005B6EEF /* BooleanPath.framework in Frameworks */, 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | /* End PBXFrameworksBuildPhase section */ 109 | 110 | /* Begin PBXGroup section */ 111 | 1C6B72122234D519005B6EEF = { 112 | isa = PBXGroup; 113 | children = ( 114 | 1C6B721E2234D51A005B6EEF /* BooleanPath */, 115 | 1C6B72522234E5E6005B6EEF /* BooleanPath_Demo */, 116 | 1C6B721D2234D51A005B6EEF /* Products */, 117 | ); 118 | sourceTree = ""; 119 | }; 120 | 1C6B721D2234D51A005B6EEF /* Products */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 1C6B721C2234D51A005B6EEF /* BooleanPath.framework */, 124 | 1C6B72512234E5E6005B6EEF /* BooleanPath_Demo.app */, 125 | ); 126 | name = Products; 127 | sourceTree = ""; 128 | }; 129 | 1C6B721E2234D51A005B6EEF /* BooleanPath */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 1C6B72272234D574005B6EEF /* Int_Extension.swift */, 133 | 1C6B72292234D585005B6EEF /* Legendre.swift */, 134 | 1C6B724B2234E20A005B6EEF /* NSBezierPath_Extensions */, 135 | 1C6B724C2234E218005B6EEF /* CGPath_Bridge */, 136 | 1C6B72332234D608005B6EEF /* BPGeometry.swift */, 137 | 1C6B72352234D615005B6EEF /* BPBezierGraph.swift */, 138 | 1C6B72372234D624005B6EEF /* BPBezierCurve.swift */, 139 | 1C6B72392234D637005B6EEF /* BPBezierCurve_Edge.swift */, 140 | 1C6B723B2234D64E005B6EEF /* BPBezierContour.swift */, 141 | 1C6B723D2234D65E005B6EEF /* BPBezierIntersection.swift */, 142 | 1C6B723F2234D679005B6EEF /* BPBezierIntersectRange.swift */, 143 | 1C6B72412234D69C005B6EEF /* BPCurveLocation.swift */, 144 | 1C6B72432234D6AB005B6EEF /* BPEdgeCrossing.swift */, 145 | 1C6B72452234D6BE005B6EEF /* BPEdgeOverlap.swift */, 146 | 1C6B72472234D6D1005B6EEF /* BPEdgeOverlapRun.swift */, 147 | 1C6B72492234D6E0005B6EEF /* BPContourOverlap.swift */, 148 | 1C6B72202234D51A005B6EEF /* Info.plist */, 149 | ); 150 | path = BooleanPath; 151 | sourceTree = ""; 152 | }; 153 | 1C6B724B2234E20A005B6EEF /* NSBezierPath_Extensions */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 1C6B722F2234D5D2005B6EEF /* NSBezierPath_Utilities.swift */, 157 | 1C6B72312234D5F7005B6EEF /* NSBezierPath_Boolean.swift */, 158 | ); 159 | name = NSBezierPath_Extensions; 160 | sourceTree = ""; 161 | }; 162 | 1C6B724C2234E218005B6EEF /* CGPath_Bridge */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 1C6B722B2234D5A8005B6EEF /* CGPath_Extension.swift */, 166 | 1C6B722D2234D5C3005B6EEF /* LRTBezierPathWrapper.swift */, 167 | ); 168 | path = CGPath_Bridge; 169 | sourceTree = ""; 170 | }; 171 | 1C6B72522234E5E6005B6EEF /* BooleanPath_Demo */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 1C6B72532234E5E6005B6EEF /* AppDelegate.swift */, 175 | 1C6B72552234E5E6005B6EEF /* ViewController.swift */, 176 | 1C6B72662234E658005B6EEF /* DemoView.swift */, 177 | 1C6B72572234E5E7005B6EEF /* Assets.xcassets */, 178 | 1C6B72592234E5E7005B6EEF /* Main.storyboard */, 179 | 1C6B725C2234E5E7005B6EEF /* Info.plist */, 180 | 1C6B725D2234E5E7005B6EEF /* BooleanPath_Demo.entitlements */, 181 | ); 182 | path = BooleanPath_Demo; 183 | sourceTree = ""; 184 | }; 185 | /* End PBXGroup section */ 186 | 187 | /* Begin PBXHeadersBuildPhase section */ 188 | 1C6B72172234D51A005B6EEF /* Headers */ = { 189 | isa = PBXHeadersBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | /* End PBXHeadersBuildPhase section */ 196 | 197 | /* Begin PBXNativeTarget section */ 198 | 1C6B721B2234D51A005B6EEF /* BooleanPath */ = { 199 | isa = PBXNativeTarget; 200 | buildConfigurationList = 1C6B72242234D51A005B6EEF /* Build configuration list for PBXNativeTarget "BooleanPath" */; 201 | buildPhases = ( 202 | 1C6B72172234D51A005B6EEF /* Headers */, 203 | 1C6B72182234D51A005B6EEF /* Sources */, 204 | 1C6B72192234D51A005B6EEF /* Frameworks */, 205 | 1C6B721A2234D51A005B6EEF /* Resources */, 206 | ); 207 | buildRules = ( 208 | ); 209 | dependencies = ( 210 | ); 211 | name = BooleanPath; 212 | productName = BooleanPath; 213 | productReference = 1C6B721C2234D51A005B6EEF /* BooleanPath.framework */; 214 | productType = "com.apple.product-type.framework"; 215 | }; 216 | 1C6B72502234E5E6005B6EEF /* BooleanPath_Demo */ = { 217 | isa = PBXNativeTarget; 218 | buildConfigurationList = 1C6B725E2234E5E7005B6EEF /* Build configuration list for PBXNativeTarget "BooleanPath_Demo" */; 219 | buildPhases = ( 220 | 1C6B724D2234E5E6005B6EEF /* Sources */, 221 | 1C6B724E2234E5E6005B6EEF /* Frameworks */, 222 | 1C6B724F2234E5E6005B6EEF /* Resources */, 223 | 1C6B72652234E5F9005B6EEF /* Embed Frameworks */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | 1C6B72642234E5F9005B6EEF /* PBXTargetDependency */, 229 | ); 230 | name = BooleanPath_Demo; 231 | productName = BooleanPath_Demo; 232 | productReference = 1C6B72512234E5E6005B6EEF /* BooleanPath_Demo.app */; 233 | productType = "com.apple.product-type.application"; 234 | }; 235 | /* End PBXNativeTarget section */ 236 | 237 | /* Begin PBXProject section */ 238 | 1C6B72132234D519005B6EEF /* Project object */ = { 239 | isa = PBXProject; 240 | attributes = { 241 | LastSwiftUpdateCheck = 1010; 242 | LastUpgradeCheck = 1010; 243 | ORGANIZATIONNAME = "Takuto Nakamura"; 244 | TargetAttributes = { 245 | 1C6B721B2234D51A005B6EEF = { 246 | CreatedOnToolsVersion = 10.1; 247 | LastSwiftMigration = 1010; 248 | }; 249 | 1C6B72502234E5E6005B6EEF = { 250 | CreatedOnToolsVersion = 10.1; 251 | }; 252 | }; 253 | }; 254 | buildConfigurationList = 1C6B72162234D519005B6EEF /* Build configuration list for PBXProject "BooleanPath" */; 255 | compatibilityVersion = "Xcode 10.0"; 256 | developmentRegion = en; 257 | hasScannedForEncodings = 0; 258 | knownRegions = ( 259 | en, 260 | Base, 261 | ); 262 | mainGroup = 1C6B72122234D519005B6EEF; 263 | productRefGroup = 1C6B721D2234D51A005B6EEF /* Products */; 264 | projectDirPath = ""; 265 | projectRoot = ""; 266 | targets = ( 267 | 1C6B721B2234D51A005B6EEF /* BooleanPath */, 268 | 1C6B72502234E5E6005B6EEF /* BooleanPath_Demo */, 269 | ); 270 | }; 271 | /* End PBXProject section */ 272 | 273 | /* Begin PBXResourcesBuildPhase section */ 274 | 1C6B721A2234D51A005B6EEF /* Resources */ = { 275 | isa = PBXResourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | 1C6B724F2234E5E6005B6EEF /* Resources */ = { 282 | isa = PBXResourcesBuildPhase; 283 | buildActionMask = 2147483647; 284 | files = ( 285 | 1C6B72582234E5E7005B6EEF /* Assets.xcassets in Resources */, 286 | 1C6B725B2234E5E7005B6EEF /* Main.storyboard in Resources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | /* End PBXResourcesBuildPhase section */ 291 | 292 | /* Begin PBXSourcesBuildPhase section */ 293 | 1C6B72182234D51A005B6EEF /* Sources */ = { 294 | isa = PBXSourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | 1C6B72442234D6AB005B6EEF /* BPEdgeCrossing.swift in Sources */, 298 | 1C6B722C2234D5A8005B6EEF /* CGPath_Extension.swift in Sources */, 299 | 1C6B72322234D5F7005B6EEF /* NSBezierPath_Boolean.swift in Sources */, 300 | 1C6B72282234D574005B6EEF /* Int_Extension.swift in Sources */, 301 | 1C6B72362234D615005B6EEF /* BPBezierGraph.swift in Sources */, 302 | 1C6B722E2234D5C3005B6EEF /* LRTBezierPathWrapper.swift in Sources */, 303 | 1C6B72482234D6D1005B6EEF /* BPEdgeOverlapRun.swift in Sources */, 304 | 1C6B723C2234D64E005B6EEF /* BPBezierContour.swift in Sources */, 305 | 1C6B72302234D5D2005B6EEF /* NSBezierPath_Utilities.swift in Sources */, 306 | 1C6B723E2234D65E005B6EEF /* BPBezierIntersection.swift in Sources */, 307 | 1C6B722A2234D585005B6EEF /* Legendre.swift in Sources */, 308 | 1C6B72342234D608005B6EEF /* BPGeometry.swift in Sources */, 309 | 1C6B724A2234D6E0005B6EEF /* BPContourOverlap.swift in Sources */, 310 | 1C6B72382234D624005B6EEF /* BPBezierCurve.swift in Sources */, 311 | 1C6B723A2234D637005B6EEF /* BPBezierCurve_Edge.swift in Sources */, 312 | 1C6B72422234D69C005B6EEF /* BPCurveLocation.swift in Sources */, 313 | 1C6B72462234D6BE005B6EEF /* BPEdgeOverlap.swift in Sources */, 314 | 1C6B72402234D679005B6EEF /* BPBezierIntersectRange.swift in Sources */, 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | }; 318 | 1C6B724D2234E5E6005B6EEF /* Sources */ = { 319 | isa = PBXSourcesBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | 1C6B72562234E5E6005B6EEF /* ViewController.swift in Sources */, 323 | 1C6B72672234E658005B6EEF /* DemoView.swift in Sources */, 324 | 1C6B72542234E5E6005B6EEF /* AppDelegate.swift in Sources */, 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | /* End PBXSourcesBuildPhase section */ 329 | 330 | /* Begin PBXTargetDependency section */ 331 | 1C6B72642234E5F9005B6EEF /* PBXTargetDependency */ = { 332 | isa = PBXTargetDependency; 333 | target = 1C6B721B2234D51A005B6EEF /* BooleanPath */; 334 | targetProxy = 1C6B72632234E5F9005B6EEF /* PBXContainerItemProxy */; 335 | }; 336 | /* End PBXTargetDependency section */ 337 | 338 | /* Begin PBXVariantGroup section */ 339 | 1C6B72592234E5E7005B6EEF /* Main.storyboard */ = { 340 | isa = PBXVariantGroup; 341 | children = ( 342 | 1C6B725A2234E5E7005B6EEF /* Base */, 343 | ); 344 | name = Main.storyboard; 345 | sourceTree = ""; 346 | }; 347 | /* End PBXVariantGroup section */ 348 | 349 | /* Begin XCBuildConfiguration section */ 350 | 1C6B72222234D51A005B6EEF /* Debug */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | ALWAYS_SEARCH_USER_PATHS = NO; 354 | CLANG_ANALYZER_NONNULL = YES; 355 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 356 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 357 | CLANG_CXX_LIBRARY = "libc++"; 358 | CLANG_ENABLE_MODULES = YES; 359 | CLANG_ENABLE_OBJC_ARC = YES; 360 | CLANG_ENABLE_OBJC_WEAK = YES; 361 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 362 | CLANG_WARN_BOOL_CONVERSION = YES; 363 | CLANG_WARN_COMMA = YES; 364 | CLANG_WARN_CONSTANT_CONVERSION = YES; 365 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 366 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 367 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 368 | CLANG_WARN_EMPTY_BODY = YES; 369 | CLANG_WARN_ENUM_CONVERSION = YES; 370 | CLANG_WARN_INFINITE_RECURSION = YES; 371 | CLANG_WARN_INT_CONVERSION = YES; 372 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 373 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 374 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 375 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 376 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 377 | CLANG_WARN_STRICT_PROTOTYPES = YES; 378 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 379 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 380 | CLANG_WARN_UNREACHABLE_CODE = YES; 381 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 382 | CODE_SIGN_IDENTITY = "Mac Developer"; 383 | COPY_PHASE_STRIP = NO; 384 | CURRENT_PROJECT_VERSION = 1; 385 | DEBUG_INFORMATION_FORMAT = dwarf; 386 | ENABLE_STRICT_OBJC_MSGSEND = YES; 387 | ENABLE_TESTABILITY = YES; 388 | GCC_C_LANGUAGE_STANDARD = gnu11; 389 | GCC_DYNAMIC_NO_PIC = NO; 390 | GCC_NO_COMMON_BLOCKS = YES; 391 | GCC_OPTIMIZATION_LEVEL = 0; 392 | GCC_PREPROCESSOR_DEFINITIONS = ( 393 | "DEBUG=1", 394 | "$(inherited)", 395 | ); 396 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 397 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 398 | GCC_WARN_UNDECLARED_SELECTOR = YES; 399 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 400 | GCC_WARN_UNUSED_FUNCTION = YES; 401 | GCC_WARN_UNUSED_VARIABLE = YES; 402 | MACOSX_DEPLOYMENT_TARGET = 10.10; 403 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 404 | MTL_FAST_MATH = YES; 405 | ONLY_ACTIVE_ARCH = YES; 406 | SDKROOT = macosx; 407 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 408 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 409 | VERSIONING_SYSTEM = "apple-generic"; 410 | VERSION_INFO_PREFIX = ""; 411 | }; 412 | name = Debug; 413 | }; 414 | 1C6B72232234D51A005B6EEF /* Release */ = { 415 | isa = XCBuildConfiguration; 416 | buildSettings = { 417 | ALWAYS_SEARCH_USER_PATHS = NO; 418 | CLANG_ANALYZER_NONNULL = YES; 419 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 420 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 421 | CLANG_CXX_LIBRARY = "libc++"; 422 | CLANG_ENABLE_MODULES = YES; 423 | CLANG_ENABLE_OBJC_ARC = YES; 424 | CLANG_ENABLE_OBJC_WEAK = YES; 425 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 426 | CLANG_WARN_BOOL_CONVERSION = YES; 427 | CLANG_WARN_COMMA = YES; 428 | CLANG_WARN_CONSTANT_CONVERSION = YES; 429 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 430 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 431 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 432 | CLANG_WARN_EMPTY_BODY = YES; 433 | CLANG_WARN_ENUM_CONVERSION = YES; 434 | CLANG_WARN_INFINITE_RECURSION = YES; 435 | CLANG_WARN_INT_CONVERSION = YES; 436 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 437 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 438 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 439 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 440 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 441 | CLANG_WARN_STRICT_PROTOTYPES = YES; 442 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 443 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 444 | CLANG_WARN_UNREACHABLE_CODE = YES; 445 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 446 | CODE_SIGN_IDENTITY = "Mac Developer"; 447 | COPY_PHASE_STRIP = NO; 448 | CURRENT_PROJECT_VERSION = 1; 449 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 450 | ENABLE_NS_ASSERTIONS = NO; 451 | ENABLE_STRICT_OBJC_MSGSEND = YES; 452 | GCC_C_LANGUAGE_STANDARD = gnu11; 453 | GCC_NO_COMMON_BLOCKS = YES; 454 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 455 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 456 | GCC_WARN_UNDECLARED_SELECTOR = YES; 457 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 458 | GCC_WARN_UNUSED_FUNCTION = YES; 459 | GCC_WARN_UNUSED_VARIABLE = YES; 460 | MACOSX_DEPLOYMENT_TARGET = 10.10; 461 | MTL_ENABLE_DEBUG_INFO = NO; 462 | MTL_FAST_MATH = YES; 463 | SDKROOT = macosx; 464 | SWIFT_COMPILATION_MODE = wholemodule; 465 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 466 | VERSIONING_SYSTEM = "apple-generic"; 467 | VERSION_INFO_PREFIX = ""; 468 | }; 469 | name = Release; 470 | }; 471 | 1C6B72252234D51A005B6EEF /* Debug */ = { 472 | isa = XCBuildConfiguration; 473 | buildSettings = { 474 | CLANG_ENABLE_MODULES = YES; 475 | CODE_SIGN_IDENTITY = ""; 476 | CODE_SIGN_STYLE = Automatic; 477 | COMBINE_HIDPI_IMAGES = YES; 478 | DEFINES_MODULE = YES; 479 | DEVELOPMENT_TEAM = VJ5N2X84K8; 480 | DYLIB_COMPATIBILITY_VERSION = 1; 481 | DYLIB_CURRENT_VERSION = 1; 482 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 483 | FRAMEWORK_VERSION = A; 484 | INFOPLIST_FILE = BooleanPath/Info.plist; 485 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 486 | LD_RUNPATH_SEARCH_PATHS = ( 487 | "$(inherited)", 488 | "@executable_path/../Frameworks", 489 | "@loader_path/Frameworks", 490 | ); 491 | PRODUCT_BUNDLE_IDENTIFIER = com.kyome.BooleanPath; 492 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 493 | SKIP_INSTALL = YES; 494 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 495 | SWIFT_VERSION = 4.2; 496 | }; 497 | name = Debug; 498 | }; 499 | 1C6B72262234D51A005B6EEF /* Release */ = { 500 | isa = XCBuildConfiguration; 501 | buildSettings = { 502 | CLANG_ENABLE_MODULES = YES; 503 | CODE_SIGN_IDENTITY = ""; 504 | CODE_SIGN_STYLE = Automatic; 505 | COMBINE_HIDPI_IMAGES = YES; 506 | DEFINES_MODULE = YES; 507 | DEVELOPMENT_TEAM = VJ5N2X84K8; 508 | DYLIB_COMPATIBILITY_VERSION = 1; 509 | DYLIB_CURRENT_VERSION = 1; 510 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 511 | FRAMEWORK_VERSION = A; 512 | INFOPLIST_FILE = BooleanPath/Info.plist; 513 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 514 | LD_RUNPATH_SEARCH_PATHS = ( 515 | "$(inherited)", 516 | "@executable_path/../Frameworks", 517 | "@loader_path/Frameworks", 518 | ); 519 | PRODUCT_BUNDLE_IDENTIFIER = com.kyome.BooleanPath; 520 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 521 | SKIP_INSTALL = YES; 522 | SWIFT_VERSION = 4.2; 523 | }; 524 | name = Release; 525 | }; 526 | 1C6B725F2234E5E7005B6EEF /* Debug */ = { 527 | isa = XCBuildConfiguration; 528 | buildSettings = { 529 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 530 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 531 | CODE_SIGN_ENTITLEMENTS = BooleanPath_Demo/BooleanPath_Demo.entitlements; 532 | CODE_SIGN_STYLE = Automatic; 533 | COMBINE_HIDPI_IMAGES = YES; 534 | DEVELOPMENT_TEAM = VJ5N2X84K8; 535 | INFOPLIST_FILE = BooleanPath_Demo/Info.plist; 536 | LD_RUNPATH_SEARCH_PATHS = ( 537 | "$(inherited)", 538 | "@executable_path/../Frameworks", 539 | ); 540 | MACOSX_DEPLOYMENT_TARGET = 10.10; 541 | PRODUCT_BUNDLE_IDENTIFIER = "com.kyome.BooleanPath-Demo"; 542 | PRODUCT_NAME = "$(TARGET_NAME)"; 543 | SWIFT_VERSION = 4.2; 544 | }; 545 | name = Debug; 546 | }; 547 | 1C6B72602234E5E7005B6EEF /* Release */ = { 548 | isa = XCBuildConfiguration; 549 | buildSettings = { 550 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 551 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 552 | CODE_SIGN_ENTITLEMENTS = BooleanPath_Demo/BooleanPath_Demo.entitlements; 553 | CODE_SIGN_STYLE = Automatic; 554 | COMBINE_HIDPI_IMAGES = YES; 555 | DEVELOPMENT_TEAM = VJ5N2X84K8; 556 | INFOPLIST_FILE = BooleanPath_Demo/Info.plist; 557 | LD_RUNPATH_SEARCH_PATHS = ( 558 | "$(inherited)", 559 | "@executable_path/../Frameworks", 560 | ); 561 | MACOSX_DEPLOYMENT_TARGET = 10.10; 562 | PRODUCT_BUNDLE_IDENTIFIER = "com.kyome.BooleanPath-Demo"; 563 | PRODUCT_NAME = "$(TARGET_NAME)"; 564 | SWIFT_VERSION = 4.2; 565 | }; 566 | name = Release; 567 | }; 568 | /* End XCBuildConfiguration section */ 569 | 570 | /* Begin XCConfigurationList section */ 571 | 1C6B72162234D519005B6EEF /* Build configuration list for PBXProject "BooleanPath" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | 1C6B72222234D51A005B6EEF /* Debug */, 575 | 1C6B72232234D51A005B6EEF /* Release */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | 1C6B72242234D51A005B6EEF /* Build configuration list for PBXNativeTarget "BooleanPath" */ = { 581 | isa = XCConfigurationList; 582 | buildConfigurations = ( 583 | 1C6B72252234D51A005B6EEF /* Debug */, 584 | 1C6B72262234D51A005B6EEF /* Release */, 585 | ); 586 | defaultConfigurationIsVisible = 0; 587 | defaultConfigurationName = Release; 588 | }; 589 | 1C6B725E2234E5E7005B6EEF /* Build configuration list for PBXNativeTarget "BooleanPath_Demo" */ = { 590 | isa = XCConfigurationList; 591 | buildConfigurations = ( 592 | 1C6B725F2234E5E7005B6EEF /* Debug */, 593 | 1C6B72602234E5E7005B6EEF /* Release */, 594 | ); 595 | defaultConfigurationIsVisible = 0; 596 | defaultConfigurationName = Release; 597 | }; 598 | /* End XCConfigurationList section */ 599 | }; 600 | rootObject = 1C6B72132234D519005B6EEF /* Project object */; 601 | } 602 | -------------------------------------------------------------------------------- /BooleanPath.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BooleanPath.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BooleanPath.xcodeproj/project.xcworkspace/xcuserdata/takuto.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kyome22/BooleanPath/bf711b546cbea2bbe62a14dbdf501685593437c6/BooleanPath.xcodeproj/project.xcworkspace/xcuserdata/takuto.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /BooleanPath.xcodeproj/xcshareddata/xcschemes/BooleanPath.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /BooleanPath.xcodeproj/xcshareddata/xcschemes/BooleanPath_Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /BooleanPath.xcodeproj/xcuserdata/takuto.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | BooleanPath.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | BooleanPath_Demo.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /BooleanPath/BPBezierContour.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPBezierContour.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | enum BPContourInside { 18 | case filled 19 | case hole 20 | } 21 | 22 | class BPBezierContour { 23 | enum BPContourDirection { 24 | case clockwise 25 | case antiClockwise 26 | } 27 | 28 | fileprivate var _edges: [BPBezierCurve] 29 | fileprivate var _overlaps: [BPContourOverlap] 30 | fileprivate var _bounds: CGRect 31 | fileprivate var _boundingRect: CGRect 32 | fileprivate var _inside: BPContourInside 33 | fileprivate var _bezPathCache: NSBezierPath? 34 | 35 | var inside: BPContourInside { 36 | get { 37 | return _inside 38 | } 39 | set { 40 | _inside = newValue 41 | } 42 | } 43 | 44 | internal var overlaps: [BPContourOverlap] { 45 | return _overlaps 46 | } 47 | 48 | var edges: [BPBezierCurve] { 49 | return _edges 50 | } 51 | 52 | init() { 53 | self._edges = [] 54 | self._overlaps = [] 55 | self._bounds = CGRect.null 56 | self._boundingRect = CGRect.null 57 | self._inside = .filled 58 | } 59 | 60 | class func bezierContourWithCurve(_ curve: BPBezierCurve) -> BPBezierContour { 61 | let result = BPBezierContour() 62 | result.addCurve(curve) 63 | return result 64 | } 65 | 66 | func addCurve(_ curve: BPBezierCurve?) { 67 | if let curve = curve { 68 | curve.contour = self; 69 | curve.index = _edges.count 70 | _edges.append(curve) 71 | _bounds = CGRect.null // force the bounds to be recalculated 72 | _boundingRect = CGRect.null 73 | _bezPathCache = nil 74 | } 75 | } 76 | 77 | func addCurveFrom(_ startCrossing: BPEdgeCrossing?, to endCrossing: BPEdgeCrossing?) { 78 | var curve: BPBezierCurve? 79 | 80 | if startCrossing == nil, let endCrossing = endCrossing { 81 | curve = endCrossing.leftCurve 82 | } else if endCrossing == nil, let startCrossing = startCrossing { 83 | curve = startCrossing.rightCurve 84 | } else if let startCrossing = startCrossing, let endCrossing = endCrossing { 85 | curve = startCrossing.curve?.subcurveWithRange(BPRange(minimum: startCrossing.parameter, maximum: endCrossing.parameter)) 86 | } 87 | if let curve = curve { 88 | addCurve(curve) 89 | } 90 | } 91 | 92 | func addReverseCurve(_ curve: BPBezierCurve?) { 93 | if let curve = curve { 94 | addCurve(curve.reversedCurve()) 95 | } 96 | } 97 | 98 | func addReverseCurveFrom(_ startCrossing: BPEdgeCrossing?, to endCrossing: BPEdgeCrossing?) { 99 | var curve: BPBezierCurve? 100 | 101 | if startCrossing == nil, let endCrossing = endCrossing { 102 | curve = endCrossing.leftCurve 103 | } else if endCrossing == nil, let startCrossing = startCrossing { 104 | curve = startCrossing.rightCurve 105 | } else if let startCrossing = startCrossing, let endCrossing = endCrossing { 106 | curve = startCrossing.curve?.subcurveWithRange(BPRange(minimum: startCrossing.parameter, maximum: endCrossing.parameter)) 107 | } 108 | 109 | if let curve = curve { 110 | addReverseCurve(curve) 111 | } 112 | } 113 | 114 | 115 | // 132 116 | //- (NSRect) bounds 117 | var bounds: CGRect { 118 | // Cache the bounds to save time 119 | if !_bounds.equalTo(CGRect.null) { 120 | return _bounds 121 | } 122 | 123 | // If no edges, no bounds 124 | if _edges.count == 0 { 125 | return CGRect.zero 126 | } 127 | 128 | var totalBounds = CGRect.zero 129 | for edge in _edges { 130 | let edgeBounds: CGRect = edge.bounds 131 | if totalBounds.equalTo(CGRect.zero) { 132 | totalBounds = edgeBounds 133 | } else { 134 | // This was: 135 | // totalBounds = BPUnionRect(totalBounds, bounds) 136 | totalBounds = totalBounds.union(edgeBounds) 137 | } 138 | } 139 | 140 | _bounds = totalBounds 141 | 142 | return _bounds 143 | } 144 | 145 | 146 | // 156 147 | //- (NSRect) boundingRect 148 | var boundingRect: CGRect { 149 | // Cache the boundingRect to save time 150 | if !_boundingRect.equalTo(CGRect.null) { 151 | return _boundingRect 152 | } 153 | 154 | // If no edges, no bounds 155 | if _edges.count == 0 { 156 | return CGRect.zero 157 | } 158 | 159 | var totalBounds = CGRect.zero 160 | for edge in _edges { 161 | let edgeBounds: CGRect = edge.boundingRect 162 | if totalBounds.equalTo(CGRect.zero) { 163 | totalBounds = edgeBounds 164 | } else { 165 | // This was: 166 | // totalBounds = BPUnionRect(totalBounds, bounds) 167 | totalBounds = totalBounds.union(edgeBounds) 168 | } 169 | } 170 | 171 | _boundingRect = totalBounds 172 | 173 | return _boundingRect 174 | } 175 | 176 | 177 | // 180 178 | //- (NSPoint) firstPoint 179 | var firstPoint: CGPoint { 180 | if _edges.count == 0 { 181 | return CGPoint.zero 182 | } 183 | 184 | return _edges[0].endPoint1 185 | } 186 | 187 | // 189 188 | //- (BOOL) containsPoint:(NSPoint)testPoint 189 | func containsPoint(_ testPoint: CGPoint) -> Bool { 190 | 191 | if !boundingRect.contains(testPoint) || !bounds.contains(testPoint) { 192 | return false 193 | } 194 | 195 | // Create a test line from our point to somewhere outside our graph. 196 | // We'll see how many times the test line intersects edges of the graph. 197 | // Based on the even/odd rule, if it's an odd number, we're inside 198 | // the graph, if even, outside. 199 | 200 | let externalXPt = testPoint.x > bounds.minX ? bounds.minX - 10 : bounds.maxX + 10 201 | let lineEndPoint = CGPoint(x: externalXPt, y: testPoint.y) 202 | /* just move us outside the bounds of the graph */ 203 | let testCurve = BPBezierCurve.bezierCurveWithLineStartPoint(testPoint, endPoint: lineEndPoint) 204 | 205 | let intersectCount = numberOfIntersectionsWithRay(testCurve) 206 | 207 | return intersectCount.isOdd 208 | //return intersectCount & 1 == 1 // fast version of: intersectCount % 2 != 0 209 | } 210 | 211 | // 204 212 | //- (NSNSnteger) numberOfIntersectionsWithRay:(BPBezierCurve *)testEdge 213 | func numberOfIntersectionsWithRay(_ testEdge: BPBezierCurve) -> Int { 214 | var count = 0 215 | intersectionsWithRay(testEdge, withBlock: { 216 | (intersection: BPBezierIntersection)-> Void in 217 | count += 1 218 | }) 219 | 220 | return count 221 | } 222 | 223 | // 213 224 | //- (void) intersectionsWithRay:(BPBezierCurve *)testEdge withBlock:(void (^)(BPBezierIntersection *intersection))block 225 | func intersectionsWithRay(_ testEdge: BPBezierCurve, withBlock block:@escaping (_ intersection: BPBezierIntersection) -> Void) { 226 | 227 | var firstIntersection: BPBezierIntersection? 228 | var previousIntersection: BPBezierIntersection? 229 | 230 | // Count how many times we intersect with this particular contour 231 | for edge in _edges { 232 | // Check for intersections between our test ray and the rest of the bezier graph 233 | var intersectRange: BPBezierIntersectRange? 234 | 235 | testEdge.intersectionsWithBezierCurve(edge, overlapRange: &intersectRange) { 236 | (intersection: BPBezierIntersection) -> (setStop: Bool, stopValue:Bool) in 237 | // Make sure this is a proper crossing 238 | if !testEdge.crossesEdge(edge, atIntersection:intersection) || edge.isPoint { 239 | // don't count tangents 240 | return (false, false) 241 | } 242 | 243 | // Make sure we don't count the same intersection twice. 244 | // This happens when the ray crosses at the start or end of an edge. 245 | if intersection.isAtStartOfCurve2, let previousIntersection = previousIntersection { 246 | let previousEdge = edge.previous 247 | if ( previousIntersection.isAtEndPointOfCurve2 && previousEdge === previousIntersection.curve2 ) { 248 | return (false, false) 249 | } 250 | } else if intersection.isAtEndPointOfCurve2, let firstIntersection = firstIntersection { 251 | let nextEdge = edge.next 252 | if firstIntersection.isAtStartOfCurve2 && nextEdge === firstIntersection.curve2 { 253 | return (false, false) 254 | } 255 | } 256 | 257 | block(intersection) 258 | if firstIntersection == nil { 259 | firstIntersection = intersection 260 | } 261 | previousIntersection = intersection 262 | return (false, false) 263 | } 264 | 265 | if let intersectRange = intersectRange { 266 | if testEdge.crossesEdge(edge, atIntersectRange:intersectRange) { 267 | block(intersectRange.middleIntersection) 268 | } 269 | } 270 | } 271 | } 272 | 273 | 274 | // 251 275 | //- (BPBezierCurve *) startEdge 276 | fileprivate var startEdge: BPBezierCurve? { 277 | // When marking we need to start at a point that is 278 | // clearly either inside or outside the other graph, 279 | // otherwise we could mark the crossings exactly opposite 280 | // of what they're supposed to be. 281 | if edges.count == 0 { 282 | return nil 283 | } 284 | 285 | var startEdge = edges[0] 286 | let stopValue = startEdge 287 | 288 | while startEdge.isStartShared { 289 | startEdge = startEdge.next 290 | if startEdge === stopValue { 291 | break; // for safety. But if we're here, we could be hosed 292 | } 293 | } 294 | return startEdge 295 | } 296 | 297 | 298 | // 269 299 | //- (NSPoint) testPointForContainment 300 | var testPointForContainment: CGPoint { 301 | // Start with the startEdge, and if it's not shared (overlapping) 302 | // then use its first point 303 | if var testEdge = self.startEdge { 304 | 305 | if !testEdge.isStartShared { 306 | return testEdge.endPoint1 307 | } 308 | 309 | // At this point we know that all the end points defining this contour are shared. 310 | // We'll need to somewhat arbitrarily pick a point on an edge that's not overlapping 311 | let stopValue = testEdge 312 | let parameter = 0.5 313 | while doesOverlapContainParameter(parameter, onEdge:testEdge) { 314 | testEdge = testEdge.next; 315 | if ( testEdge == stopValue ) { 316 | break; // for safety. But if we're here, we could be hosed 317 | } 318 | } 319 | return testEdge.pointAtParameter(parameter).point 320 | } else { 321 | return CGPoint.zero 322 | } 323 | } 324 | 325 | 326 | // 289 327 | //- (void) startingEdge:(BPBezierCurve **)outEdge parameter:(CGFloat *)outParameter point:(NSPoint *)outPoint 328 | func startingEdge() -> (edge: BPBezierCurve, parameter: Double, point: CGPoint) { 329 | // Start with the startEdge, and if it's not shared (overlapping) 330 | // then use its first point 331 | var testEdge = startEdge! 332 | 333 | if !testEdge.isStartShared { 334 | return (edge: testEdge, parameter: 0.0, point: testEdge.endPoint1) 335 | } 336 | 337 | // At this point we know that all the end points defining this contour are shared. 338 | // We'll need to somewhat arbitrarily pick a point on an edge that's not overlapping 339 | let stopValue = testEdge 340 | let parameter = 0.5 341 | while doesOverlapContainParameter(parameter, onEdge:testEdge) { 342 | testEdge = testEdge.next 343 | if testEdge === stopValue { 344 | break // for safety. But if we're here, we could be hosed 345 | } 346 | } 347 | 348 | let outPoint = testEdge.pointAtParameter(parameter).point 349 | 350 | return (edge: testEdge, parameter: 0.0, point: outPoint) 351 | } 352 | 353 | 354 | // 315 355 | //- (void) markCrossingsAsEntryOrExitWithContour:(BPBezierContour *)otherContour markInside:(BOOL)markInside 356 | func markCrossingsAsEntryOrExitWithContour(_ otherContour: BPBezierContour, markInside: Bool) { 357 | // Go through and mark all the crossings with the given 358 | // contour as "entry" or "exit". 359 | // This determines what part of ths contour is output. 360 | 361 | // When marking we need to start at a point that is clearly 362 | // either inside or outside the other graph, otherwise we 363 | // could mark the crossings exactly opposite of what they're supposed to be. 364 | let (startEdge, startParameter, startPoint) = startingEdge() 365 | 366 | // Calculate the first entry value. 367 | // We need to determine if the edge we're starting 368 | // on is inside or outside the otherContour. 369 | let contains = otherContour.contourAndSelfIntersectingContoursContainPoint(startPoint) 370 | var isEntry = markInside ? !contains : contains 371 | var otherContours = otherContour.selfIntersectingContours 372 | otherContours.append(otherContour) 373 | 374 | let BPStopParameterNoLimit = 2.0 // needs to be > 1.0 375 | let BPStartParameterNoLimit = 0.0 376 | 377 | // Walk all the edges in this contour and mark the crossings 378 | isEntry = markCrossingsOnEdge(startEdge, startParameter:startParameter, stopParameter:BPStopParameterNoLimit, otherContours:otherContours, startIsEntry:isEntry) 379 | 380 | var edge = startEdge.next 381 | while edge !== startEdge { 382 | isEntry = markCrossingsOnEdge(edge, startParameter:BPStartParameterNoLimit, stopParameter:BPStopParameterNoLimit, otherContours:otherContours, startIsEntry:isEntry) 383 | edge = edge.next 384 | } 385 | 386 | markCrossingsOnEdge(startEdge, startParameter:BPStartParameterNoLimit, stopParameter:startParameter, otherContours:otherContours, startIsEntry:isEntry) 387 | } 388 | 389 | 390 | // 347 391 | //- (BOOL) markCrossingsOnEdge:(BPBezierCurve *)edge startParameter:(CGFloat)startParameter stopParameter:(CGFloat)stopParameter otherContours:(NSArray *)otherContours isEntry:(BOOL)startIsEntry 392 | @discardableResult 393 | func markCrossingsOnEdge(_ edge: BPBezierCurve, startParameter: Double, stopParameter: Double, otherContours: [BPBezierContour] , startIsEntry: Bool) -> Bool { 394 | 395 | var isEntry = startIsEntry 396 | 397 | // Mark all the crossings on this edge 398 | edge.crossingsWithBlock() { 399 | (crossing: BPEdgeCrossing) -> (setStop: Bool, stopValue:Bool) in 400 | 401 | // skip over other contours 402 | 403 | // Note: 404 | // The expression: 405 | // arrayOfObjs.filter({ el in el === obj }).count == 0 406 | // evaluates to a Bool indicating that arrayOfObjs does NOT contain obj 407 | // is equivalent to 408 | // ![arrayOfObjs containsObject:obj] 409 | let other = crossing.counterpart?.edge?.contour 410 | let notContained = otherContours.filter({ el in el === other }).count == 0 411 | if crossing.isSelfCrossing || notContained { 412 | return (false, false) // skip 413 | } 414 | 415 | if crossing.parameter < startParameter || crossing.parameter >= stopParameter { 416 | return (false, false) // skip 417 | } 418 | 419 | crossing.entry = isEntry 420 | isEntry = !isEntry // toggle to indicate exit or entry 421 | return (false, false) 422 | } 423 | 424 | return isEntry 425 | } 426 | 427 | 428 | // 363 429 | //- (BOOL) contourAndSelfIntersectingContoursContainPoint:(NSPoint)point 430 | fileprivate func contourAndSelfIntersectingContoursContainPoint(_ point: CGPoint) -> Bool { 431 | var containerCount = 0 432 | if containsPoint(point) { 433 | containerCount += 1 434 | } 435 | for contour in selfIntersectingContours { 436 | if contour.containsPoint(point) { 437 | containerCount += 1 438 | } 439 | } 440 | 441 | return containerCount.isOdd 442 | //return containerCount & 1 != 0 // fast version of: containerCount % 2 != 0 443 | } 444 | 445 | 446 | // 376 447 | //- (NSBezierPath*) bezierPath // GPC: added 448 | var bezierPath: NSBezierPath { 449 | if _bezPathCache == nil { 450 | let path = NSBezierPath() 451 | var firstPoint = true 452 | 453 | for edge in self.edges { 454 | if firstPoint { 455 | path.move(to: edge.endPoint1) 456 | firstPoint = false 457 | } 458 | 459 | if edge.isStraightLine { 460 | path.line(to: edge.endPoint2) 461 | } else { 462 | path.curve(to: edge.endPoint2, 463 | controlPoint1: edge.controlPoint1, 464 | controlPoint2: edge.controlPoint2) 465 | } 466 | } 467 | 468 | if !path.isEmpty { 469 | path.close() 470 | } 471 | path.windingRule = NSBezierPath.WindingRule.evenOdd 472 | _bezPathCache = path 473 | } 474 | 475 | return _bezPathCache! 476 | } 477 | 478 | 479 | // 403 480 | //- (void) close 481 | func close() { 482 | // adds an element to connect first and last points on the contour 483 | if _edges.count == 0 { 484 | return 485 | } 486 | 487 | let first = _edges[0] 488 | if let last = _edges.last { 489 | if !BPArePointsClose(first.endPoint1, point2: last.endPoint2) { 490 | addCurve(BPBezierCurve(startPoint: last.endPoint2, endPoint: first.endPoint1)) 491 | } 492 | } 493 | } 494 | 495 | 496 | // 417 497 | //- (BPBezierContour*) reversedContour // GPC: added 498 | var reversedContour: BPBezierContour { 499 | let revContour = BPBezierContour() 500 | 501 | for edge in _edges { 502 | revContour.addReverseCurve(edge) 503 | } 504 | 505 | return revContour 506 | } 507 | 508 | 509 | // 428 510 | //- (BPContourDirection) direction 511 | var direction: BPContourDirection { 512 | 513 | var lastPoint = CGPoint.zero, currentPoint = CGPoint.zero 514 | var firstPoint = true 515 | var a = CGFloat(0.0) 516 | 517 | for edge in _edges { 518 | if firstPoint { 519 | lastPoint = edge.endPoint1 520 | firstPoint = false 521 | } else { 522 | currentPoint = edge.endPoint2 523 | a += ((lastPoint.x * currentPoint.y) - (currentPoint.x * lastPoint.y)) 524 | lastPoint = currentPoint 525 | } 526 | } 527 | 528 | return ( a >= 0 ) ? BPContourDirection.clockwise : BPContourDirection.antiClockwise 529 | } 530 | 531 | 532 | // 449 533 | //- (BPBezierContour *) contourMadeClockwiseIfNecessary 534 | var contourMadeClockwiseIfNecessary : BPBezierContour { 535 | let dir = self.direction 536 | 537 | if dir == BPContourDirection.clockwise { 538 | return self 539 | } 540 | 541 | return self.reversedContour 542 | } 543 | 544 | 545 | // 459 546 | //- (BOOL) crossesOwnContour:(BPBezierContour *)contour 547 | func crossesOwnContour(_ contour: BPBezierContour) -> Bool { 548 | for edge in _edges { 549 | var intersects = false 550 | 551 | edge.crossingsWithBlock() { 552 | (crossing: BPEdgeCrossing) -> (setStop: Bool, stopValue:Bool) in 553 | 554 | // Only want the self intersecting crossings 555 | if crossing.isSelfCrossing, let ccpart = crossing.counterpart, let intersectingEdge = ccpart.edge { 556 | if intersectingEdge.contour === contour { 557 | intersects = true 558 | return (true, true) 559 | } 560 | } 561 | return (false, false) 562 | } 563 | 564 | if intersects { 565 | return true 566 | } 567 | } 568 | return false 569 | } 570 | 571 | 572 | // 478 573 | //@property (readonly) NSArray *intersectingContours; 574 | //- (NSArray *) intersectingContours 575 | var intersectingContours: [BPBezierContour] { 576 | // Go and find all the unique contours that intersect this specific contour 577 | var iContours: [BPBezierContour] = [] 578 | for edge in _edges { 579 | 580 | edge.intersectingEdgesWithBlock() { 581 | (intersectingEdge: BPBezierCurve) -> Void in 582 | if let ieContour = intersectingEdge.contour { 583 | if iContours.filter({ el in el === ieContour }).count == 0 { 584 | iContours.append(ieContour) 585 | } 586 | } 587 | } 588 | 589 | } 590 | return iContours 591 | } 592 | 593 | // 491 594 | //- (NSArray *) selfIntersectingContours 595 | var selfIntersectingContours: [BPBezierContour] { 596 | // Go and find all the unique contours that intersect 597 | // this specific contour from our own graph 598 | var siContours: [BPBezierContour] = [] 599 | addSelfIntersectingContoursToArray(&siContours, originalContour: self) 600 | return siContours 601 | } 602 | 603 | // 499 604 | //- (void) addSelfIntersectingContoursToArray:(NSMutableArray *)contours originalContour:(BPBezierContour *)originalContour 605 | fileprivate func addSelfIntersectingContoursToArray(_ contours: inout [BPBezierContour], originalContour: BPBezierContour) { 606 | for edge in _edges { 607 | 608 | edge.selfIntersectingEdgesWithBlock() { 609 | (intersectingEdge: BPBezierCurve) -> Void in 610 | if let ieContour = intersectingEdge.contour { 611 | if ieContour !== originalContour && contours.filter({ el in el === ieContour }).count == 0 { 612 | contours.append(ieContour) 613 | ieContour.addSelfIntersectingContoursToArray(&contours, originalContour: originalContour) 614 | } 615 | } 616 | } 617 | 618 | } 619 | } 620 | 621 | 622 | // 511 623 | //- (void) addOverlap:(BPContourOverlap *)overlap 624 | func addOverlap(_ overlap: BPContourOverlap) { 625 | if _overlaps.count == 0 { 626 | return 627 | } 628 | 629 | _overlaps.append(overlap) 630 | } 631 | 632 | 633 | // 519 634 | //- (void) removeAllOverlaps 635 | func removeAllOverlaps() { 636 | if _overlaps.count == 0 { 637 | return 638 | } 639 | 640 | _overlaps.removeAll() 641 | } 642 | 643 | 644 | // 527 645 | //- (BOOL) isEquivalent:(BPBezierContour *)other 646 | func isEquivalent(_ other: BPBezierContour) -> Bool { 647 | if _overlaps.count == 0 { 648 | return false 649 | } 650 | 651 | for overlap in _overlaps { 652 | if overlap.isBetweenContour(self, andContour: other) && overlap.isComplete { 653 | return true 654 | } 655 | } 656 | return false 657 | } 658 | 659 | // 539 660 | //- (void) forEachEdgeOverlapDo:(void (^)(BPEdgeOverlap *overlap))block 661 | fileprivate func forEachEdgeOverlapDo(_ block:(_ overlap: BPEdgeOverlap) -> Void) { 662 | if _overlaps.count == 0 { 663 | return 664 | } 665 | 666 | for overlap in _overlaps { 667 | overlap.runsWithBlock() { 668 | (run: BPEdgeOverlapRun) -> Bool in 669 | for edgeOverlap in run.overlaps { 670 | block(edgeOverlap) 671 | } 672 | return false 673 | } 674 | } 675 | } 676 | 677 | // 552 678 | //- (BOOL) doesOverlapContainCrossing:(BPEdgeCrossing *)crossing 679 | func doesOverlapContainCrossing(_ crossing: BPEdgeCrossing) -> Bool { 680 | if _overlaps.count == 0 { 681 | return false 682 | } 683 | 684 | for overlap in _overlaps { 685 | if overlap.doesContainCrossing(crossing) { 686 | return true 687 | } 688 | } 689 | return false 690 | } 691 | 692 | // 564 693 | //- (BOOL) doesOverlapContainParameter:(CGFloat)parameter onEdge:(BPBezierCurve *)edge 694 | func doesOverlapContainParameter(_ parameter: Double, onEdge edge: BPBezierCurve) -> Bool { 695 | if _overlaps.count > 0 { 696 | for overlap in _overlaps { 697 | if overlap.doesContainParameter(parameter, onEdge:edge) { 698 | return true 699 | } 700 | } 701 | } 702 | return false 703 | } 704 | 705 | // 584 706 | //- (BPCurveLocation *) closestLocationToPoint:(NSPoint)point 707 | func closestLocationToPoint(_ point: CGPoint) -> BPCurveLocation? { 708 | var closestEdge: BPBezierCurve? = nil 709 | var location = BPBezierCurveLocation(parameter: 0.0, distance: 0.0) 710 | 711 | for edge in _edges { 712 | let edgeLocation = edge.closestLocationToPoint(point) 713 | if closestEdge == nil || edgeLocation.distance < location.distance { 714 | closestEdge = edge 715 | location = edgeLocation 716 | } 717 | } 718 | 719 | if let closestEdge = closestEdge { 720 | let curveLocation = BPCurveLocation(edge: closestEdge, parameter: location.parameter, distance: location.distance) 721 | curveLocation.contour = self 722 | return curveLocation 723 | } else { 724 | return nil 725 | } 726 | } 727 | 728 | // 617 729 | //- (NSBezierPath *) debugPathForIntersectionType:(NSInteger)itersectionType 730 | /// Returns a path consisting of small circles placed at 731 | /// the intersections that match 732 | /// 733 | /// This allows the internal state of a contour to be 734 | /// rapidly visualized so that bugs with boolean ops 735 | /// are easier to spot at a glance. 736 | func debugPathForIntersectionType(_ itersectionType: Int) -> NSBezierPath { 737 | 738 | let path: NSBezierPath = NSBezierPath() 739 | 740 | for edge in _edges { 741 | 742 | edge.crossingsWithBlock() { 743 | (crossing: BPEdgeCrossing) -> (setStop: Bool, stopValue:Bool) in 744 | 745 | if itersectionType == 1 { // looking for entries 746 | if !crossing.isEntry { 747 | return (false, false) 748 | } 749 | } else if itersectionType == 2 { // looking for exits 750 | if crossing.isEntry { 751 | return (false, false) 752 | } 753 | } 754 | if crossing.isEntry { 755 | path.append(NSBezierPath.circleAtPoint(crossing.location)) 756 | } else { 757 | path.append(NSBezierPath.rectAtPoint(crossing.location)) 758 | } 759 | 760 | return (false, false) 761 | } 762 | } 763 | 764 | // Add the start point and direction for marking 765 | if let startEdge = self.startEdge { 766 | let startEdgeTangent = BPNormalizePoint(BPSubtractPoint(startEdge.controlPoint1, point2: startEdge.endPoint1)); 767 | path.append(NSBezierPath.triangleAtPoint(startEdge.endPoint1, direction: startEdgeTangent)) 768 | } 769 | 770 | // Add the contour's entire path to make it easy 771 | // to see which one owns which crossings 772 | // (these can be colour-coded when drawing the paths) 773 | path.append(self.bezierPath) 774 | 775 | // If this countour is flagged as "inside", 776 | // the debug path is shown dashed, otherwise solid 777 | if self.inside == .hole { 778 | let dashes: [CGFloat] = [CGFloat(2), CGFloat(3)] 779 | path.setLineDash(dashes, count: 2, phase: 0) 780 | } 781 | 782 | return path; 783 | } 784 | } 785 | -------------------------------------------------------------------------------- /BooleanPath/BPBezierCurve_Edge.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPBezierCurve_Edge.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | private func BPFindEdge1TangentCurves(_ edge: BPBezierCurve, intersection: BPBezierIntersection) -> (leftCurve: BPBezierCurve, rightCurve: BPBezierCurve) { 18 | var leftCurve: BPBezierCurve, rightCurve: BPBezierCurve 19 | if intersection.isAtStartOfCurve1 { 20 | leftCurve = edge.previousNonpoint 21 | rightCurve = edge 22 | } else if intersection.isAtStopOfCurve1 { 23 | leftCurve = edge 24 | rightCurve = edge.nextNonpoint 25 | } else { 26 | leftCurve = intersection.curve1LeftBezier 27 | rightCurve = intersection.curve1RightBezier 28 | } 29 | return (leftCurve: leftCurve, rightCurve: rightCurve) 30 | } 31 | 32 | private func BPFindEdge2TangentCurves(_ edge: BPBezierCurve, intersection: BPBezierIntersection) -> (leftCurve: BPBezierCurve, rightCurve: BPBezierCurve) { 33 | var leftCurve: BPBezierCurve, rightCurve: BPBezierCurve 34 | 35 | if intersection.isAtStartOfCurve2 { 36 | leftCurve = edge.previousNonpoint 37 | rightCurve = edge 38 | } else if intersection.isAtStopOfCurve2 { 39 | leftCurve = edge 40 | rightCurve = edge.nextNonpoint 41 | } else { 42 | leftCurve = intersection.curve2LeftBezier 43 | rightCurve = intersection.curve2RightBezier 44 | } 45 | 46 | return (leftCurve: leftCurve, rightCurve: rightCurve) 47 | } 48 | 49 | private func BPComputeEdgeTangents(_ leftCurve: BPBezierCurve, rightCurve: BPBezierCurve, offset: Double, edgeTangents: inout BPTangentPair) { 50 | edgeTangents.left = leftCurve.tangentFromRightOffset(offset) 51 | edgeTangents.right = rightCurve.tangentFromLeftOffset(offset) 52 | } 53 | 54 | private func BPComputeEdge1RangeTangentCurves(_ edge: BPBezierCurve, intersectRange: BPBezierIntersectRange) -> (leftCurve: BPBezierCurve, rightCurve: BPBezierCurve) { 55 | var leftCurve: BPBezierCurve, rightCurve: BPBezierCurve 56 | if intersectRange.isAtStartOfCurve1 { 57 | leftCurve = edge.previousNonpoint 58 | } else { 59 | leftCurve = intersectRange.curve1LeftBezier 60 | } 61 | if intersectRange.isAtStopOfCurve1 { 62 | rightCurve = edge.nextNonpoint 63 | } else { 64 | rightCurve = intersectRange.curve1RightBezier 65 | } 66 | return (leftCurve: leftCurve, rightCurve: rightCurve) 67 | } 68 | 69 | private func BPComputeEdge2RangeTangentCurves(_ edge: BPBezierCurve, intersectRange: BPBezierIntersectRange) -> (leftCurve: BPBezierCurve, rightCurve: BPBezierCurve) { 70 | var leftCurve: BPBezierCurve, rightCurve: BPBezierCurve 71 | 72 | if intersectRange.isAtStartOfCurve2 { 73 | leftCurve = edge.previousNonpoint 74 | } else { 75 | leftCurve = intersectRange.curve2LeftBezier 76 | } 77 | if intersectRange.isAtStopOfCurve2 { 78 | rightCurve = edge.nextNonpoint 79 | } else { 80 | rightCurve = intersectRange.curve2RightBezier 81 | } 82 | return (leftCurve: leftCurve, rightCurve: rightCurve) 83 | } 84 | 85 | extension BPBezierCurve { 86 | func addCrossing(_ crossing: BPEdgeCrossing) { 87 | crossing.edge = self 88 | crossings.append(crossing) 89 | sortCrossings() 90 | } 91 | 92 | func removeCrossing(_ crossing: BPEdgeCrossing) { 93 | for (index, element) in crossings.enumerated() { 94 | if element === crossing { 95 | crossings.remove(at: index) 96 | break 97 | } 98 | } 99 | sortCrossings() 100 | } 101 | 102 | func removeAllCrossings() { 103 | crossings.removeAll() 104 | } 105 | 106 | var next: BPBezierCurve { 107 | var nxt: BPBezierCurve = self 108 | 109 | if let contour = contour { 110 | if contour.edges.count > 0 { 111 | let nextIndex = index + 1 112 | if nextIndex >= contour.edges.count { 113 | nxt = contour.edges.first! 114 | } else { 115 | nxt = contour.edges[nextIndex] 116 | } 117 | } 118 | } 119 | return nxt 120 | } 121 | 122 | var previous: BPBezierCurve { 123 | var prev: BPBezierCurve = self 124 | 125 | if let contour = contour { 126 | if contour.edges.count > 0 { 127 | if index == 0 { 128 | prev = contour.edges.last! 129 | } else { 130 | prev = contour.edges[index - 1] 131 | } 132 | } 133 | } 134 | return prev 135 | } 136 | 137 | var nextNonpoint: BPBezierCurve { 138 | var edge = self.next 139 | while edge.isPoint { 140 | edge = edge.next 141 | } 142 | return edge 143 | } 144 | 145 | var previousNonpoint: BPBezierCurve { 146 | var edge = self.previous 147 | while edge.isPoint { 148 | edge = edge.previous 149 | } 150 | return edge 151 | } 152 | 153 | var hasCrossings: Bool { 154 | return !crossings.isEmpty 155 | } 156 | 157 | func crossingsWithBlock(_ block: (_ crossing: BPEdgeCrossing) -> (setStop: Bool, stopValue:Bool)) { 158 | for crossing in crossings { 159 | let (set, val) = block(crossing) 160 | if set && val { 161 | break 162 | } 163 | } 164 | } 165 | 166 | func crossingsCopyWithBlock(_ block: (_ crossing: BPEdgeCrossing) -> (setStop: Bool, stopValue:Bool)) { 167 | let crossingsCopy = crossings 168 | for crossing in crossingsCopy { 169 | let (set, val) = block(crossing) 170 | if set && val { 171 | break 172 | } 173 | } 174 | } 175 | 176 | func nextCrossing(_ crossing: BPEdgeCrossing) -> BPEdgeCrossing? { 177 | if crossing.index < crossings.count - 1 { 178 | return crossings[crossing.index + 1] 179 | } else { 180 | return nil 181 | } 182 | } 183 | 184 | func previousCrossing(_ crossing: BPEdgeCrossing) -> BPEdgeCrossing? { 185 | if crossing.index > 0 { 186 | return crossings[crossing.index - 1] 187 | } else { 188 | return nil 189 | } 190 | } 191 | 192 | func intersectingEdgesWithBlock(_ block: (_ intersectingEdge: BPBezierCurve) -> Void) { 193 | 194 | crossingsWithBlock() { 195 | (crossing: BPEdgeCrossing) -> (setStop: Bool, stopValue:Bool) in 196 | if !crossing.isSelfCrossing { 197 | if let crossingCounterpartEdge = crossing.counterpart?.edge { 198 | block(crossingCounterpartEdge) 199 | } 200 | } 201 | return (false, false) 202 | } 203 | } 204 | 205 | func selfIntersectingEdgesWithBlock(_ block: (_ intersectingEdge: BPBezierCurve) -> Void) { 206 | crossingsWithBlock() { 207 | (crossing: BPEdgeCrossing) -> (setStop: Bool, stopValue:Bool) in 208 | 209 | if crossing.isSelfCrossing { 210 | if let crossingCounterpartEdge = crossing.counterpart?.edge { 211 | block(crossingCounterpartEdge) 212 | } 213 | } 214 | return (false, false) 215 | } 216 | } 217 | 218 | var firstCrossing: BPEdgeCrossing? { 219 | return crossings.first 220 | } 221 | 222 | var lastCrossing: BPEdgeCrossing? { 223 | return crossings.last 224 | } 225 | 226 | var firstNonselfCrossing: BPEdgeCrossing? { 227 | var first = firstCrossing 228 | while first != nil && first!.isSelfCrossing { 229 | first = first?.next 230 | } 231 | return first 232 | } 233 | 234 | var lastNonselfCrossing: BPEdgeCrossing? { 235 | var last = lastCrossing 236 | while last != nil && last!.isSelfCrossing { 237 | last = last?.previous 238 | } 239 | return last 240 | } 241 | 242 | var hasNonselfCrossings: Bool { 243 | for crossing in crossings { 244 | if !crossing.isSelfCrossing { 245 | return true 246 | } 247 | } 248 | return false 249 | } 250 | 251 | func crossesEdge(_ edge2: BPBezierCurve, atIntersection intersection: BPBezierIntersection) -> Bool { 252 | if intersection.isTangent { 253 | return false 254 | } 255 | 256 | if !intersection.isAtEndPointOfCurve { 257 | return true 258 | } 259 | 260 | var edge1Tangents = BPTangentPair(left: CGPoint.zero, right: CGPoint.zero) 261 | var edge2Tangents = BPTangentPair(left: CGPoint.zero, right: CGPoint.zero) 262 | var offset = 0.0 263 | 264 | let (edge1LeftCurve, edge1RightCurve) = BPFindEdge1TangentCurves(self, intersection: intersection) 265 | let edge1Length = min(edge1LeftCurve.length(), edge1RightCurve.length()) 266 | 267 | let (edge2LeftCurve, edge2RightCurve) = BPFindEdge2TangentCurves(edge2, intersection: intersection) 268 | let edge2Length = min(edge2LeftCurve.length(), edge2RightCurve.length()) 269 | 270 | let maxOffset = min(edge1Length, edge2Length) 271 | 272 | repeat { 273 | BPComputeEdgeTangents(edge1LeftCurve, rightCurve: edge1RightCurve, offset: offset, edgeTangents: &edge1Tangents) 274 | BPComputeEdgeTangents(edge2LeftCurve, rightCurve: edge2RightCurve, offset: offset, edgeTangents: &edge2Tangents) 275 | 276 | offset += 1.0 277 | } while BPAreTangentsAmbigious(edge1Tangents, edge2Tangents: edge2Tangents) && offset < maxOffset 278 | 279 | return BPTangentsCross(edge1Tangents, edge2Tangents: edge2Tangents) 280 | } 281 | 282 | func crossesEdge(_ edge2: BPBezierCurve, atIntersectRange intersectRange: BPBezierIntersectRange) -> Bool { 283 | 284 | var edge1Tangents = BPTangentPair(left: CGPoint.zero, right: CGPoint.zero) 285 | var edge2Tangents = BPTangentPair(left: CGPoint.zero, right: CGPoint.zero) 286 | var offset = 0.0 287 | 288 | let (edge1LeftCurve, edge1RightCurve) = BPComputeEdge1RangeTangentCurves(self, intersectRange: intersectRange) 289 | 290 | let edge1Length = min(edge1LeftCurve.length(), edge1RightCurve.length()) 291 | 292 | let (edge2LeftCurve, edge2RightCurve) = BPComputeEdge2RangeTangentCurves(edge2, intersectRange: intersectRange) 293 | let edge2Length = min(edge2LeftCurve.length(), edge2RightCurve.length()) 294 | 295 | let maxOffset = min(edge1Length, edge2Length); 296 | 297 | repeat { 298 | BPComputeEdgeTangents(edge1LeftCurve, rightCurve: edge1RightCurve, offset: offset, edgeTangents: &edge1Tangents) 299 | BPComputeEdgeTangents(edge2LeftCurve, rightCurve: edge2RightCurve, offset: offset, edgeTangents: &edge2Tangents) 300 | 301 | offset += 1.0 302 | } while BPAreTangentsAmbigious(edge1Tangents, edge2Tangents: edge2Tangents) && offset < maxOffset 303 | 304 | return BPTangentsCross(edge1Tangents, edge2Tangents: edge2Tangents); 305 | } 306 | 307 | // =============================== 308 | // MARK: Private funcs 309 | // =============================== 310 | 311 | fileprivate func sortCrossings() { 312 | crossings.sort(by: { $0.order < $1.order }) 313 | for (index, crossing) in crossings.enumerated() { 314 | crossing.index = index 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /BooleanPath/BPBezierIntersectRange.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPBezierIntersectRange.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | public class BPBezierIntersectRange { 18 | var _curve1: BPBezierCurve 19 | var _parameterRange1: BPRange 20 | var _curve1LeftBezier: BPBezierCurve? 21 | var _curve1MiddleBezier: BPBezierCurve? 22 | var _curve1RightBezier: BPBezierCurve? 23 | 24 | var _curve2: BPBezierCurve 25 | var _parameterRange2: BPRange 26 | var _curve2LeftBezier: BPBezierCurve? 27 | var _curve2MiddleBezier: BPBezierCurve? 28 | var _curve2RightBezier: BPBezierCurve? 29 | 30 | var needToComputeCurve1 = true 31 | var needToComputeCurve2 = true 32 | 33 | var _reversed: Bool 34 | 35 | var curve1: BPBezierCurve { 36 | return _curve1 37 | } 38 | 39 | var parameterRange1: BPRange { 40 | return _parameterRange1 41 | } 42 | 43 | var curve2: BPBezierCurve { 44 | return _curve2 45 | } 46 | 47 | var parameterRange2: BPRange { 48 | return _parameterRange2 49 | } 50 | 51 | var reversed: Bool { 52 | return _reversed 53 | } 54 | 55 | init(curve1: BPBezierCurve, parameterRange1: BPRange, curve2: BPBezierCurve, parameterRange2: BPRange, reversed: Bool) { 56 | _curve1 = curve1 57 | _parameterRange1 = parameterRange1 58 | _curve2 = curve2 59 | _parameterRange2 = parameterRange2 60 | _reversed = reversed 61 | } 62 | 63 | var curve1LeftBezier: BPBezierCurve { 64 | computeCurve1() 65 | return _curve1LeftBezier! 66 | } 67 | 68 | var curve1OverlappingBezier: BPBezierCurve { 69 | computeCurve1() 70 | return _curve1MiddleBezier! 71 | } 72 | 73 | var curve1RightBezier: BPBezierCurve { 74 | computeCurve1() 75 | return _curve1RightBezier! 76 | } 77 | 78 | var curve2LeftBezier: BPBezierCurve { 79 | computeCurve2() 80 | return _curve2LeftBezier! 81 | } 82 | 83 | var curve2OverlappingBezier: BPBezierCurve { 84 | computeCurve2() 85 | return _curve2MiddleBezier! 86 | } 87 | 88 | var curve2RightBezier: BPBezierCurve { 89 | computeCurve2() 90 | return _curve2RightBezier! 91 | } 92 | 93 | var isAtStartOfCurve1: Bool { 94 | return BPAreValuesCloseWithOptions(_parameterRange1.minimum, value2: 0.0, threshold: BPParameterCloseThreshold) 95 | } 96 | 97 | var isAtStopOfCurve1: Bool { 98 | return BPAreValuesCloseWithOptions(_parameterRange1.maximum, value2: 1.0, threshold: BPParameterCloseThreshold) 99 | } 100 | 101 | var isAtStartOfCurve2: Bool { 102 | return BPAreValuesCloseWithOptions(_parameterRange2.minimum, value2: 0.0, threshold: BPParameterCloseThreshold) 103 | } 104 | 105 | var isAtStopOfCurve2: Bool { 106 | return BPAreValuesCloseWithOptions(_parameterRange2.maximum, value2: 1.0, threshold: BPParameterCloseThreshold) 107 | } 108 | 109 | var middleIntersection: BPBezierIntersection { 110 | return BPBezierIntersection ( 111 | curve1: _curve1, 112 | param1: (_parameterRange1.minimum + _parameterRange1.maximum) / 2.0, 113 | curve2: _curve2, 114 | param2: (_parameterRange2.minimum + _parameterRange2.maximum) / 2.0 115 | ) 116 | } 117 | 118 | func merge(_ other: BPBezierIntersectRange) { 119 | _parameterRange1 = BPRangeUnion(_parameterRange1, range2: other._parameterRange1); 120 | _parameterRange2 = BPRangeUnion(_parameterRange2, range2: other._parameterRange2); 121 | 122 | clearCache() 123 | } 124 | 125 | fileprivate func clearCache() { 126 | needToComputeCurve1 = true 127 | needToComputeCurve2 = true 128 | 129 | _curve1LeftBezier = nil 130 | _curve1MiddleBezier = nil 131 | _curve1RightBezier = nil 132 | _curve2LeftBezier = nil 133 | _curve2MiddleBezier = nil 134 | _curve2RightBezier = nil 135 | } 136 | 137 | fileprivate func computeCurve1() { 138 | if needToComputeCurve1 { 139 | let swr = _curve1.splitSubcurvesWithRange(_parameterRange1, left: true, middle: true, right: true) 140 | _curve1LeftBezier = swr.left 141 | _curve1MiddleBezier = swr.mid 142 | _curve1RightBezier = swr.right 143 | needToComputeCurve1 = false 144 | } 145 | } 146 | 147 | fileprivate func computeCurve2() { 148 | if needToComputeCurve2 { 149 | let swr = _curve2.splitSubcurvesWithRange(_parameterRange2, left: true, middle: true, right: true) 150 | _curve2LeftBezier = swr.left 151 | _curve2MiddleBezier = swr.mid 152 | _curve2RightBezier = swr.right 153 | needToComputeCurve2 = false 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /BooleanPath/BPBezierIntersection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPBezierIntersection.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | let BPPointCloseThreshold = isRunningOn64BitDevice ? 1e-7 : 1e-3 18 | let BPParameterCloseThreshold = isRunningOn64BitDevice ? 1e-4 : 1e-2 19 | 20 | public class BPBezierIntersection { 21 | fileprivate var _location: CGPoint? 22 | fileprivate var _curve1: BPBezierCurve 23 | fileprivate var _parameter1: Double 24 | fileprivate var _curve1LeftBezier: BPBezierCurve? 25 | fileprivate var _curve1RightBezier: BPBezierCurve? 26 | fileprivate var _curve2: BPBezierCurve 27 | fileprivate var _parameter2: Double 28 | fileprivate var _curve2LeftBezier: BPBezierCurve? 29 | fileprivate var _curve2RightBezier: BPBezierCurve? 30 | fileprivate var _tangent: Bool = false 31 | fileprivate var needToComputeCurve1 = true 32 | fileprivate var needToComputeCurve2 = true 33 | 34 | public var location: CGPoint { 35 | computeCurve1() 36 | return _location! 37 | } 38 | 39 | var curve1: BPBezierCurve { 40 | return _curve1 41 | } 42 | 43 | var parameter1: Double { 44 | return _parameter1 45 | } 46 | 47 | var curve2: BPBezierCurve { 48 | return _curve2 49 | } 50 | 51 | var parameter2: Double { 52 | return _parameter2 53 | } 54 | 55 | init(curve1: BPBezierCurve, param1: Double, curve2:BPBezierCurve, param2: Double) { 56 | _curve1 = curve1 57 | _parameter1 = param1 58 | _curve2 = curve2 59 | _parameter2 = param2 60 | } 61 | 62 | public var isTangent: Bool { 63 | if isAtEndPointOfCurve { 64 | return false 65 | } 66 | computeCurve1() 67 | computeCurve2() 68 | 69 | let curve1LeftTangent = BPNormalizePoint(BPSubtractPoint(_curve1LeftBezier!.controlPoint2, point2: _curve1LeftBezier!.endPoint2)) 70 | let curve1RightTangent = BPNormalizePoint(BPSubtractPoint(_curve1RightBezier!.controlPoint1, point2: _curve1RightBezier!.endPoint1)) 71 | let curve2LeftTangent = BPNormalizePoint(BPSubtractPoint(_curve2LeftBezier!.controlPoint2, point2: _curve2LeftBezier!.endPoint2)) 72 | let curve2RightTangent = BPNormalizePoint(BPSubtractPoint(_curve2RightBezier!.controlPoint1, point2: _curve2RightBezier!.endPoint1)) 73 | 74 | return BPArePointsCloseWithOptions(curve1LeftTangent, point2: curve2LeftTangent, threshold: BPPointCloseThreshold) 75 | || BPArePointsCloseWithOptions(curve1LeftTangent, point2: curve2RightTangent, threshold: BPPointCloseThreshold) 76 | || BPArePointsCloseWithOptions(curve1RightTangent, point2: curve2LeftTangent, threshold: BPPointCloseThreshold) 77 | || BPArePointsCloseWithOptions(curve1RightTangent, point2: curve2RightTangent, threshold: BPPointCloseThreshold) 78 | } 79 | 80 | var curve1LeftBezier: BPBezierCurve { 81 | computeCurve1() 82 | return _curve1LeftBezier! 83 | } 84 | 85 | var curve1RightBezier: BPBezierCurve { 86 | computeCurve1() 87 | return _curve1RightBezier! 88 | } 89 | 90 | var curve2LeftBezier: BPBezierCurve { 91 | computeCurve2() 92 | return _curve2LeftBezier! 93 | } 94 | 95 | var curve2RightBezier: BPBezierCurve { 96 | computeCurve2() 97 | return _curve2RightBezier! 98 | } 99 | 100 | var isAtStartOfCurve1: Bool { 101 | return BPAreValuesCloseWithOptions(_parameter1, value2: 0.0, threshold: BPParameterCloseThreshold) || _curve1.isPoint 102 | } 103 | 104 | var isAtStopOfCurve1: Bool { 105 | return BPAreValuesCloseWithOptions(_parameter1, value2: 1.0, threshold: BPParameterCloseThreshold) || _curve1.isPoint 106 | } 107 | 108 | var isAtEndPointOfCurve1: Bool { 109 | return self.isAtStartOfCurve1 || self.isAtStopOfCurve1 110 | } 111 | 112 | var isAtStartOfCurve2: Bool { 113 | return BPAreValuesCloseWithOptions(_parameter2, value2: 0.0, threshold: BPParameterCloseThreshold) || _curve2.isPoint 114 | } 115 | 116 | var isAtStopOfCurve2: Bool { 117 | return BPAreValuesCloseWithOptions(_parameter2, value2: 1.0, threshold: BPParameterCloseThreshold) || _curve2.isPoint 118 | } 119 | 120 | var isAtEndPointOfCurve2: Bool { 121 | return self.isAtStartOfCurve2 || self.isAtStopOfCurve2 122 | } 123 | 124 | var isAtEndPointOfCurve: Bool { 125 | return self.isAtEndPointOfCurve1 || self.isAtEndPointOfCurve2 126 | } 127 | 128 | fileprivate func computeCurve1() { 129 | if needToComputeCurve1 { 130 | let pap = _curve1.pointAtParameter(_parameter1) 131 | _location = pap.point 132 | _curve1LeftBezier = pap.leftBezierCurve 133 | _curve1RightBezier = pap.rightBezierCurve 134 | needToComputeCurve1 = false 135 | } 136 | } 137 | 138 | fileprivate func computeCurve2() { 139 | if needToComputeCurve2 { 140 | let pap = _curve2.pointAtParameter(_parameter2) 141 | _curve2LeftBezier = pap.leftBezierCurve 142 | _curve2RightBezier = pap.rightBezierCurve 143 | needToComputeCurve2 = false 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /BooleanPath/BPContourOverlap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPContourOverlap.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | class BPContourOverlap { 18 | fileprivate var runs: [BPEdgeOverlapRun] = [] 19 | 20 | func addOverlap(_ range: BPBezierIntersectRange, forEdge1 edge1: BPBezierCurve, edge2: BPBezierCurve) { 21 | let overlap = BPEdgeOverlap(range: range, edge1: edge1, edge2: edge2) 22 | 23 | var createNewRun = false 24 | 25 | if runs.count == 0 { 26 | createNewRun = true 27 | } else if runs.count == 1 { 28 | let inserted = runs.last!.insertOverlap(overlap) 29 | createNewRun = !inserted 30 | } else { 31 | var inserted = runs.last!.insertOverlap(overlap) 32 | if !inserted { 33 | inserted = runs[0].insertOverlap(overlap) 34 | } 35 | createNewRun = !inserted 36 | } 37 | 38 | if createNewRun { 39 | let run = BPEdgeOverlapRun() 40 | run.insertOverlap(overlap) 41 | runs.append(run) 42 | } 43 | } 44 | 45 | func doesContainCrossing(_ crossing: BPEdgeCrossing) -> Bool { 46 | if runs.count == 0 { 47 | return false 48 | } 49 | for run in runs { 50 | if run.doesContainCrossing(crossing) { 51 | return true 52 | } 53 | } 54 | return false 55 | } 56 | 57 | func doesContainParameter(_ parameter: Double, onEdge edge: BPBezierCurve) -> Bool { 58 | if runs.count == 0 { 59 | return false 60 | } 61 | for run in runs { 62 | if run.doesContainParameter(parameter, onEdge: edge) { 63 | return true 64 | } 65 | } 66 | return false 67 | } 68 | 69 | func runsWithBlock(_ block: (_ run: BPEdgeOverlapRun) -> Bool) { 70 | if runs.count == 0 { 71 | return 72 | } 73 | for run in runs { 74 | let stop = block(run) 75 | if stop { 76 | break 77 | } 78 | } 79 | } 80 | 81 | func reset() { 82 | if runs.count == 0 { 83 | return 84 | } 85 | runs.removeAll() 86 | } 87 | 88 | var isComplete: Bool { 89 | if runs.count == 0 { 90 | return false 91 | } 92 | if runs.count != 1 { 93 | return false 94 | } 95 | return runs[0].isComplete 96 | } 97 | 98 | var isEmpty: Bool { 99 | return runs.count == 0 100 | } 101 | 102 | var contour1: BPBezierContour? { 103 | if runs.count == 0 { 104 | return nil 105 | } 106 | let run = runs[0] 107 | return run.contour1 108 | } 109 | 110 | var contour2: BPBezierContour? { 111 | if runs.count == 0 { 112 | return nil 113 | } 114 | let run = runs[0] 115 | return run.contour2 116 | } 117 | 118 | func isBetweenContour(_ contour1: BPBezierContour, andContour contour2: BPBezierContour) -> Bool { 119 | let myContour1 = self.contour1 120 | let myContour2 = self.contour2 121 | return (contour1 === myContour1 && contour2 === myContour2) 122 | || (contour1 === myContour2 && contour2 === myContour1) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /BooleanPath/BPCurveLocation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPCurveLocation.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | class BPCurveLocation { 18 | var graph: BPBezierGraph? 19 | var contour: BPBezierContour? 20 | fileprivate var _edge: BPBezierCurve 21 | fileprivate var _parameter: Double 22 | fileprivate var _distance: Double 23 | 24 | init(edge: BPBezierCurve, parameter: Double, distance: Double) { 25 | _edge = edge 26 | _parameter = parameter 27 | _distance = distance 28 | } 29 | 30 | var edge: BPBezierCurve { 31 | return _edge 32 | } 33 | 34 | var parameter: Double { 35 | return _parameter 36 | } 37 | 38 | var distance: Double { 39 | return _distance 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /BooleanPath/BPEdgeCrossing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPEdgeCrossing.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | class BPEdgeCrossing { 18 | fileprivate var _intersection: BPBezierIntersection 19 | 20 | var edge: BPBezierCurve? 21 | var counterpart: BPEdgeCrossing? 22 | var fromCrossingOverlap = false 23 | var entry = false 24 | var processed = false 25 | var selfCrossing = false 26 | var index: Int = 0 27 | 28 | init(intersection: BPBezierIntersection) { 29 | _intersection = intersection 30 | } 31 | 32 | var isProcessed: Bool { 33 | return processed 34 | } 35 | 36 | var isSelfCrossing: Bool { 37 | return selfCrossing 38 | } 39 | 40 | var isEntry: Bool { 41 | return entry 42 | } 43 | 44 | func removeFromEdge() { 45 | if let edge = edge { 46 | edge.removeCrossing(self) 47 | } 48 | } 49 | 50 | var order: Double { 51 | return parameter 52 | } 53 | 54 | var next: BPEdgeCrossing? { 55 | if let edge = edge { 56 | return edge.nextCrossing(self) 57 | } else { 58 | return nil 59 | } 60 | } 61 | 62 | var previous: BPEdgeCrossing? { 63 | if let edge = edge { 64 | return edge.previousCrossing(self) 65 | } else { 66 | return nil 67 | } 68 | } 69 | 70 | var nextNonself: BPEdgeCrossing? { 71 | var nextNon: BPEdgeCrossing? = next 72 | while nextNon != nil && nextNon!.isSelfCrossing { 73 | nextNon = nextNon!.next 74 | } 75 | return nextNon 76 | } 77 | 78 | var previousNonself: BPEdgeCrossing? { 79 | var prevNon: BPEdgeCrossing? = previous 80 | while prevNon != nil && prevNon!.isSelfCrossing { 81 | prevNon = prevNon!.previous 82 | } 83 | return prevNon 84 | } 85 | 86 | var parameter: Double { 87 | if edge == _intersection.curve1 { 88 | return _intersection.parameter1 89 | } else { 90 | return _intersection.parameter2 91 | } 92 | } 93 | 94 | var location: CGPoint { 95 | return _intersection.location 96 | } 97 | 98 | var curve: BPBezierCurve? { 99 | return edge 100 | } 101 | 102 | var leftCurve: BPBezierCurve? { 103 | if isAtStart { 104 | return nil 105 | } 106 | if edge == _intersection.curve1 { 107 | return _intersection.curve1LeftBezier 108 | } else { 109 | return _intersection.curve2LeftBezier 110 | } 111 | } 112 | 113 | var rightCurve: BPBezierCurve? { 114 | if isAtEnd { 115 | return nil 116 | } 117 | if edge == _intersection.curve1 { 118 | return _intersection.curve1RightBezier 119 | } else { 120 | return _intersection.curve2RightBezier 121 | } 122 | } 123 | 124 | var isAtStart: Bool { 125 | if edge == _intersection.curve1 { 126 | return _intersection.isAtStartOfCurve1 127 | } else { 128 | return _intersection.isAtStartOfCurve2 129 | } 130 | } 131 | 132 | var isAtEnd: Bool { 133 | if edge == _intersection.curve1 { 134 | return _intersection.isAtStopOfCurve1 135 | } else { 136 | return _intersection.isAtStopOfCurve2 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /BooleanPath/BPEdgeOverlap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPEdgeOverlap.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | let BPOverlapThreshold = isRunningOn64BitDevice ? 1e-2 : 1e-1 18 | 19 | class BPEdgeOverlap { 20 | var edge1: BPBezierCurve 21 | var edge2: BPBezierCurve 22 | fileprivate var _range: BPBezierIntersectRange 23 | 24 | var range: BPBezierIntersectRange { 25 | return _range 26 | } 27 | 28 | init(range: BPBezierIntersectRange, edge1: BPBezierCurve, edge2: BPBezierCurve) { 29 | _range = range 30 | self.edge1 = edge1 31 | self.edge2 = edge2 32 | } 33 | 34 | func fitsBefore(_ nextOverlap: BPEdgeOverlap) -> Bool { 35 | if BPAreValuesCloseWithOptions(range.parameterRange1.maximum, value2: 1.0, threshold: BPOverlapThreshold) { 36 | let nextEdge = edge1.next 37 | return nextOverlap.edge1 == nextEdge && BPAreValuesCloseWithOptions(nextOverlap.range.parameterRange1.minimum, value2: 0.0, threshold: BPOverlapThreshold) 38 | } 39 | 40 | return nextOverlap.edge1 == edge1 && BPAreValuesCloseWithOptions(nextOverlap.range.parameterRange1.minimum, value2: range.parameterRange1.maximum, threshold: BPOverlapThreshold) 41 | } 42 | 43 | func fitsAfter(_ previousOverlap: BPEdgeOverlap) -> Bool { 44 | if BPAreValuesCloseWithOptions(range.parameterRange1.minimum, value2: 0.0, threshold: BPOverlapThreshold) { 45 | let previousEdge = edge1.previous 46 | 47 | return previousOverlap.edge1 == previousEdge && BPAreValuesCloseWithOptions(previousOverlap.range.parameterRange1.maximum, value2: 1.0, threshold: BPOverlapThreshold) 48 | } 49 | 50 | return previousOverlap.edge1 == edge1 && BPAreValuesCloseWithOptions(previousOverlap.range.parameterRange1.maximum, value2: range.parameterRange1.minimum, threshold: BPOverlapThreshold) 51 | } 52 | 53 | func addMiddleCrossing() { 54 | let intersection = _range.middleIntersection 55 | 56 | let ourCrossing = BPEdgeCrossing(intersection: intersection) 57 | let theirCrossing = BPEdgeCrossing(intersection: intersection) 58 | 59 | ourCrossing.counterpart = theirCrossing 60 | theirCrossing.counterpart = ourCrossing 61 | 62 | ourCrossing.fromCrossingOverlap = true 63 | theirCrossing.fromCrossingOverlap = true 64 | 65 | edge1.addCrossing(ourCrossing) 66 | edge2.addCrossing(theirCrossing) 67 | } 68 | 69 | func doesContainParameter(_ parameter: Double, 70 | onEdge edge:BPBezierCurve, 71 | startExtends extendsBeforeStart: Bool, 72 | endExtends extendsAfterEnd: Bool) -> Bool { 73 | if extendsBeforeStart && extendsAfterEnd { 74 | return true 75 | } 76 | 77 | var parameterRange: BPRange 78 | if edge == edge1 { 79 | parameterRange = _range.parameterRange1 80 | } else { 81 | parameterRange = _range.parameterRange2 82 | } 83 | 84 | let inLeftSide = extendsBeforeStart ? parameter >= 0.0 : parameter > parameterRange.minimum 85 | let inRightSide = extendsAfterEnd ? parameter <= 1.0 : parameter < parameterRange.maximum 86 | 87 | return inLeftSide && inRightSide 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /BooleanPath/BPEdgeOverlapRun.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPEdgeOverlapRun.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | class BPEdgeOverlapRun { 18 | var overlaps: [BPEdgeOverlap] = [] 19 | 20 | @discardableResult 21 | func insertOverlap(_ overlap: BPEdgeOverlap) -> Bool { 22 | if overlaps.count == 0 { 23 | overlaps.append(overlap) 24 | return true 25 | } 26 | if let lastOverlap = overlaps.last { 27 | if lastOverlap.fitsBefore(overlap) { 28 | overlaps.append(overlap) 29 | return true 30 | } 31 | } 32 | if let firstOverlap = overlaps.first { 33 | if firstOverlap.fitsAfter(overlap) { 34 | overlaps.insert(overlap, at: 0) 35 | return true 36 | } 37 | } 38 | return false 39 | } 40 | 41 | var isComplete: Bool { 42 | if overlaps.count == 0 { 43 | return false 44 | } 45 | if let lastOverlap = overlaps.last { 46 | let firstOverlap = overlaps[0] 47 | return lastOverlap.fitsBefore(firstOverlap) 48 | } 49 | return false 50 | } 51 | 52 | func doesContainCrossing(_ crossing: BPEdgeCrossing) -> Bool { 53 | if let crossingEdge = crossing.edge { 54 | return doesContainParameter(crossing.parameter, onEdge: crossingEdge) 55 | } else { 56 | return false 57 | } 58 | } 59 | 60 | func doesContainParameter(_ parameter: Double, onEdge edge: BPBezierCurve) -> Bool { 61 | if overlaps.count == 0 { 62 | return false 63 | } 64 | var containingOverlap: BPEdgeOverlap? 65 | for overlap in overlaps { 66 | if overlap.edge1 == edge || overlap.edge2 == edge { 67 | containingOverlap = overlap 68 | break 69 | } 70 | } 71 | 72 | if let containingOverlap = containingOverlap { 73 | 74 | let lastOverlap = overlaps.last 75 | let firstOverlap = overlaps[0] 76 | 77 | let atTheStart = containingOverlap === firstOverlap 78 | let extendsBeforeStart = !atTheStart || (atTheStart && lastOverlap!.fitsBefore(firstOverlap)) 79 | 80 | let atTheEnd = containingOverlap === lastOverlap 81 | let extendsAfterEnd = !atTheEnd || (atTheEnd && firstOverlap.fitsAfter(lastOverlap!)) 82 | 83 | return containingOverlap.doesContainParameter(parameter, onEdge: edge, startExtends: extendsBeforeStart, endExtends: extendsAfterEnd) 84 | } else { 85 | return false 86 | } 87 | } 88 | 89 | var isCrossing: Bool { 90 | let firstOverlap = overlaps[0] 91 | if let lastOverlap = overlaps.last { 92 | var edge1Tangents = BPTangentPair(left: CGPoint.zero, right: CGPoint.zero) 93 | var edge2Tangents = BPTangentPair(left: CGPoint.zero, right: CGPoint.zero) 94 | 95 | var offset = 0.0 96 | var maxOffset = 0.0 97 | 98 | repeat { 99 | let length1 = BPComputeEdge1Tangents(firstOverlap, lastOverlap: lastOverlap, offset: offset, edge1Tangents: &edge1Tangents) 100 | let length2 = BPComputeEdge2Tangents(firstOverlap, lastOverlap: lastOverlap, offset: offset, edge2Tangents: &edge2Tangents) 101 | maxOffset = min(length1, length2); 102 | 103 | offset += 1.0 104 | } while ( BPAreTangentsAmbigious(edge1Tangents, edge2Tangents: edge2Tangents) && offset < maxOffset); 105 | 106 | if BPTangentsCross(edge1Tangents, edge2Tangents: edge2Tangents) { 107 | return true 108 | } 109 | var testPoints = BPTangentPair(left: CGPoint.zero, right: CGPoint.zero) 110 | BPComputeEdge1TestPoints(firstOverlap, lastOverlap: lastOverlap, offset: 1.0, testPoints: &testPoints) 111 | if let contour2 = firstOverlap.edge2.contour { 112 | let testPoint1Inside = contour2.containsPoint(testPoints.left) 113 | let testPoint2Inside = contour2.containsPoint(testPoints.right) 114 | return testPoint1Inside != testPoint2Inside 115 | } 116 | } 117 | return false 118 | } 119 | 120 | func addCrossings() { 121 | if overlaps.count == 0 { 122 | return 123 | } 124 | 125 | let middleOverlap = overlaps[overlaps.count / 2] 126 | middleOverlap.addMiddleCrossing() 127 | } 128 | 129 | var contour1: BPBezierContour? { 130 | if overlaps.count == 0 { 131 | return nil 132 | } 133 | let overlap = overlaps[0] 134 | return overlap.edge1.contour 135 | } 136 | 137 | var contour2: BPBezierContour? { 138 | if overlaps.count == 0 { 139 | return nil 140 | } 141 | let overlap = overlaps[0] 142 | return overlap.edge2.contour 143 | } 144 | 145 | } 146 | 147 | // ============================= 148 | // MARK: Utility functions 149 | // ============================= 150 | 151 | func BPComputeEdge1Tangents(_ firstOverlap: BPEdgeOverlap, 152 | lastOverlap: BPEdgeOverlap, 153 | offset: Double, 154 | edge1Tangents: inout BPTangentPair) -> Double { 155 | var firstLength = 0.0 156 | var lastLength = 0.0 157 | 158 | if firstOverlap.range.isAtStartOfCurve1 { 159 | let otherEdge1 = firstOverlap.edge1.previousNonpoint 160 | edge1Tangents.left = otherEdge1.tangentFromRightOffset(offset) 161 | firstLength = otherEdge1.length() 162 | } else { 163 | edge1Tangents.left = firstOverlap.range.curve1LeftBezier.tangentFromRightOffset(offset) 164 | firstLength = firstOverlap.range.curve1LeftBezier.length() 165 | } 166 | 167 | if lastOverlap.range.isAtStopOfCurve1 { 168 | let otherEdge1 = lastOverlap.edge1.nextNonpoint 169 | edge1Tangents.right = otherEdge1.tangentFromLeftOffset(offset) 170 | lastLength = otherEdge1.length() 171 | } else { 172 | edge1Tangents.right = lastOverlap.range.curve1RightBezier.tangentFromLeftOffset(offset) 173 | lastLength = lastOverlap.range.curve1RightBezier.length() 174 | } 175 | 176 | return min(firstLength, lastLength) 177 | } 178 | 179 | func BPComputeEdge2Tangents(_ firstOverlap: BPEdgeOverlap, 180 | lastOverlap: BPEdgeOverlap, 181 | offset: Double, 182 | edge2Tangents: inout BPTangentPair) -> Double { 183 | var firstLength = 0.0 184 | var lastLength = 0.0 185 | 186 | if !firstOverlap.range.reversed { 187 | if firstOverlap.range.isAtStartOfCurve2 { 188 | let otherEdge2 = firstOverlap.edge2.previousNonpoint 189 | edge2Tangents.left = otherEdge2.tangentFromRightOffset(offset) 190 | firstLength = otherEdge2.length() 191 | } else { 192 | edge2Tangents.left = firstOverlap.range.curve2LeftBezier.tangentFromRightOffset(offset) 193 | firstLength = firstOverlap.range.curve2LeftBezier.length() 194 | } 195 | 196 | if lastOverlap.range.isAtStopOfCurve2 { 197 | let otherEdge2 = lastOverlap.edge2.nextNonpoint 198 | edge2Tangents.right = otherEdge2.tangentFromLeftOffset(offset) 199 | lastLength = otherEdge2.length() 200 | } else { 201 | edge2Tangents.right = lastOverlap.range.curve2RightBezier.tangentFromLeftOffset(offset) 202 | lastLength = lastOverlap.range.curve2RightBezier.length() 203 | } 204 | 205 | } else { 206 | if firstOverlap.range.isAtStopOfCurve2 { 207 | let otherEdge2 = firstOverlap.edge2.nextNonpoint 208 | edge2Tangents.left = otherEdge2.tangentFromLeftOffset(offset) 209 | firstLength = otherEdge2.length() 210 | } else { 211 | edge2Tangents.left = firstOverlap.range.curve2RightBezier.tangentFromLeftOffset(offset) 212 | firstLength = firstOverlap.range.curve2RightBezier.length() 213 | } 214 | 215 | if lastOverlap.range.isAtStartOfCurve2 { 216 | let otherEdge2 = lastOverlap.edge2.previousNonpoint 217 | edge2Tangents.right = otherEdge2.tangentFromRightOffset(offset) 218 | lastLength = otherEdge2.length() 219 | } else { 220 | edge2Tangents.right = lastOverlap.range.curve2LeftBezier.tangentFromRightOffset(offset) 221 | lastLength = lastOverlap.range.curve2LeftBezier.length() 222 | } 223 | } 224 | 225 | return min(firstLength, lastLength) 226 | } 227 | 228 | func BPComputeEdge1TestPoints(_ firstOverlap: BPEdgeOverlap, 229 | lastOverlap: BPEdgeOverlap, 230 | offset: Double, 231 | testPoints: inout BPTangentPair) { 232 | if firstOverlap.range.isAtStartOfCurve1 { 233 | let otherEdge1 = firstOverlap.edge1.previousNonpoint 234 | testPoints.left = otherEdge1.pointFromRightOffset(offset) 235 | } else { 236 | testPoints.left = firstOverlap.range.curve1LeftBezier.pointFromRightOffset(offset) 237 | } 238 | 239 | if lastOverlap.range.isAtStopOfCurve1 { 240 | let otherEdge1 = lastOverlap.edge1.nextNonpoint 241 | testPoints.right = otherEdge1.pointFromLeftOffset(offset) 242 | } else { 243 | testPoints.right = lastOverlap.range.curve1RightBezier.pointFromLeftOffset(offset) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /BooleanPath/BPGeometry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BPGeometry.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | // =================================== 18 | // MARK: Point Helpers 19 | // =================================== 20 | 21 | let isRunningOn64BitDevice = MemoryLayout.size == MemoryLayout.size 22 | 23 | var BPPointClosenessThreshold: Double { 24 | if isRunningOn64BitDevice { 25 | return 1e-10 26 | } else { 27 | return 1e-2 28 | } 29 | } 30 | 31 | var BPTangentClosenessThreshold: Double { 32 | if isRunningOn64BitDevice { 33 | return 1e-12 34 | } else { 35 | return 1e-2 36 | } 37 | } 38 | 39 | var BPBoundsClosenessThreshold: Double { 40 | if isRunningOn64BitDevice { 41 | return 1e-9 42 | } else { 43 | return 1e-2 44 | } 45 | } 46 | 47 | func BPDistanceBetweenPoints(_ point1: CGPoint, point2: CGPoint) -> Double { 48 | let xDelta = Double(point2.x - point1.x) 49 | let yDelta = Double(point2.y - point1.y) 50 | 51 | return sqrt(xDelta * xDelta + yDelta * yDelta); 52 | } 53 | 54 | func BPDistancePointToLine(_ point: CGPoint, lineStartPoint: CGPoint, lineEndPoint: CGPoint) -> Double { 55 | let lineLength = BPDistanceBetweenPoints(lineStartPoint, point2: lineEndPoint) 56 | if lineLength == 0.0 { 57 | return 0.0 58 | } 59 | 60 | let xDelta = Double(lineEndPoint.x - lineStartPoint.x) 61 | let yDelta = Double(lineEndPoint.y - lineStartPoint.y) 62 | 63 | let num = Double(point.x - lineStartPoint.x) * xDelta + Double(point.y - lineStartPoint.y) * yDelta 64 | 65 | let u = num / (lineLength * lineLength) 66 | 67 | let intersectionPoint = CGPoint(x: lineStartPoint.x + CGFloat(u * xDelta), 68 | y: lineStartPoint.y + CGFloat(u * yDelta) 69 | ) 70 | 71 | return BPDistanceBetweenPoints(point, point2: intersectionPoint) 72 | } 73 | 74 | func BPAddPoint(_ point1: CGPoint, point2: CGPoint) -> CGPoint { 75 | return CGPoint(x: point1.x + point2.x, 76 | y: point1.y + point2.y) 77 | } 78 | 79 | func BPUnitScalePoint(_ point: CGPoint, scale: Double) -> CGPoint { 80 | 81 | var result = point 82 | let length = BPPointLength(point) 83 | if length != 0.0 { 84 | result.x = CGFloat(Double(result.x) * (scale/length)) 85 | result.y = CGFloat(Double(result.y) * (scale/length)) 86 | } 87 | return result 88 | } 89 | 90 | func BPScalePoint(_ point: CGPoint, scale: CGFloat) -> CGPoint { 91 | return CGPoint(x: point.x * scale, 92 | y: point.y * scale) 93 | } 94 | 95 | func BPDotMultiplyPoint(_ point1: CGPoint, point2: CGPoint) -> Double { 96 | let dotX = Double(point1.x) * Double(point2.x) 97 | let dotY = Double(point1.y) * Double(point2.y) 98 | return dotX + dotY 99 | } 100 | 101 | func BPSubtractPoint(_ point1: CGPoint, point2: CGPoint) -> CGPoint { 102 | return CGPoint(x: point1.x - point2.x, 103 | y: point1.y - point2.y) 104 | } 105 | 106 | func BPPointLength(_ point: CGPoint) -> Double { 107 | let xSq = Double(point.x) * Double(point.x) 108 | let ySq = Double(point.y) * Double(point.y) 109 | return sqrt(xSq + ySq) 110 | } 111 | 112 | func BPPointSquaredLength(_ point: CGPoint) -> Double { 113 | let xSq = Double(point.x) * Double(point.x) 114 | let ySq = Double(point.y) * Double(point.y) 115 | return xSq + ySq 116 | } 117 | 118 | func BPNormalizePoint(_ point: CGPoint) -> CGPoint { 119 | var result = point 120 | let length = BPPointLength(point) 121 | if length != 0.0 { 122 | result.x = CGFloat(Double(result.x) / length) 123 | result.y = CGFloat(Double(result.y) / length) 124 | } 125 | return result 126 | } 127 | 128 | func BPNegatePoint(_ point: CGPoint) -> CGPoint { 129 | return CGPoint(x: -point.x, 130 | y: -point.y) 131 | } 132 | 133 | func BPRoundPoint(_ point: CGPoint) -> CGPoint { 134 | return CGPoint(x: round(point.x), 135 | y: round(point.y)) 136 | } 137 | 138 | func BPLineNormal(_ lineStart: CGPoint, lineEnd: CGPoint) -> CGPoint { 139 | return BPNormalizePoint(CGPoint(x: lineStart.y - lineEnd.y, 140 | y: lineEnd.x - lineStart.x)) 141 | } 142 | 143 | func BPLineMidpoint(_ lineStart: CGPoint, lineEnd: CGPoint) -> CGPoint { 144 | let distance = BPDistanceBetweenPoints(lineStart, point2: lineEnd) 145 | let tangent = BPNormalizePoint(BPSubtractPoint(lineEnd, point2: lineStart)) 146 | return BPAddPoint(lineStart, point2: BPUnitScalePoint(tangent, scale: distance / 2.0)) 147 | } 148 | 149 | func BPRectGetTopLeft(_ rect: CGRect) -> CGPoint { 150 | return CGPoint(x: rect.minX, 151 | y: rect.minY) 152 | } 153 | 154 | func BPRectGetTopRight(_ rect: CGRect) -> CGPoint { 155 | return CGPoint(x: rect.maxX, 156 | y: rect.minY) 157 | } 158 | 159 | func BPRectGetBottomLeft(_ rect: CGRect) -> CGPoint { 160 | return CGPoint(x: rect.minX, 161 | y: rect.maxY) 162 | } 163 | 164 | func BPRectGetBottomRight(_ rect: CGRect) -> CGPoint { 165 | return CGPoint(x: rect.maxX, 166 | y: rect.maxY) 167 | } 168 | 169 | func BPExpandBoundsByPoint(_ topLeft: inout CGPoint, bottomRight: inout CGPoint, point: CGPoint) { 170 | if point.x < topLeft.x { topLeft.x = point.x } 171 | if point.x > bottomRight.x { bottomRight.x = point.x } 172 | if point.y < topLeft.y { topLeft.y = point.y } 173 | if point.y > bottomRight.y { bottomRight.y = point.y } 174 | } 175 | 176 | func BPUnionRect(_ rect1: CGRect, rect2: CGRect) -> CGRect { 177 | var topLeft = BPRectGetTopLeft(rect1) 178 | var bottomRight = BPRectGetBottomRight(rect1) 179 | BPExpandBoundsByPoint(&topLeft, bottomRight: &bottomRight, point: BPRectGetTopLeft(rect2)) 180 | BPExpandBoundsByPoint(&topLeft, bottomRight: &bottomRight, point: BPRectGetTopRight(rect2)) 181 | BPExpandBoundsByPoint(&topLeft, bottomRight: &bottomRight, point: BPRectGetBottomRight(rect2)) 182 | BPExpandBoundsByPoint(&topLeft, bottomRight: &bottomRight, point: BPRectGetBottomLeft(rect2)) 183 | 184 | return CGRect(x: topLeft.x, 185 | y: topLeft.y, 186 | width: bottomRight.x - topLeft.x, 187 | height: bottomRight.y - topLeft.y) 188 | } 189 | 190 | // =================================== 191 | // MARK: -- Distance Helper methods -- 192 | // =================================== 193 | 194 | func BPArePointsClose(_ point1: CGPoint, point2: CGPoint) -> Bool { 195 | return BPArePointsCloseWithOptions(point1, point2: point2, threshold: BPPointClosenessThreshold) 196 | } 197 | 198 | func BPArePointsCloseWithOptions(_ point1: CGPoint, point2: CGPoint, threshold: Double) -> Bool { 199 | return BPAreValuesCloseWithOptions(Double(point1.x), value2: Double(point2.x), threshold: threshold) && BPAreValuesCloseWithOptions(Double(point1.y), value2: Double(point2.y), threshold: threshold); 200 | } 201 | 202 | func BPAreValuesClose(_ value1: CGFloat, value2: CGFloat) -> Bool { 203 | return BPAreValuesCloseWithOptions(Double(value1), value2: Double(value2), threshold: BPPointClosenessThreshold) 204 | } 205 | 206 | func BPAreValuesClose(_ value1: Double, value2: Double) -> Bool { 207 | return BPAreValuesCloseWithOptions(value1, value2: value2, threshold: Double(BPPointClosenessThreshold)) 208 | } 209 | 210 | func BPAreValuesCloseWithOptions(_ value1: Double, value2: Double, threshold: Double) -> Bool { 211 | let delta = value1 - value2 212 | return (delta <= threshold) && (delta >= -threshold) 213 | } 214 | 215 | // =================================== 216 | // MARK: ---- Angle Helpers ---- 217 | // =================================== 218 | 219 | ////////////////////////////////////////////////////////////////////////// 220 | // Helper methods for angles 221 | // 222 | let Two_π = 2.0 * Double.pi 223 | let π = Double.pi 224 | let Half_π = Double.pi / 2 225 | 226 | func NormalizeAngle(_ value: Double) -> Double { 227 | var value = value 228 | while value < 0.0 { value = value + Two_π } 229 | while value >= Two_π { value = value - Two_π } 230 | 231 | return value 232 | } 233 | 234 | func PolarAngle(_ point: CGPoint) -> Double { 235 | var value = 0.0 236 | let dpx = Double(point.x) 237 | let dpy = Double(point.y) 238 | 239 | if point.x > 0.0 { 240 | value = atan(dpy / dpx) 241 | } 242 | else if point.x < 0.0 { 243 | if point.y >= 0.0 { 244 | value = atan(dpy / dpx) + π 245 | } else { 246 | value = atan(dpy / dpx) - π 247 | } 248 | } else { 249 | if point.y > 0.0 { 250 | value = Half_π 251 | } 252 | else if point.y < 0.0 { 253 | value = -Half_π 254 | } 255 | else { 256 | value = 0.0 257 | } 258 | } 259 | return NormalizeAngle(value) 260 | } 261 | 262 | // =================================== 263 | // MARK: ---- Angle Range ---- 264 | // =================================== 265 | 266 | ////////////////////////////////////////////////////////////////////////// 267 | // Angle Range structure provides a simple way to store angle ranges 268 | // and determine if a specific angle falls within. 269 | // 270 | struct BPAngleRange { 271 | var minimum: Double 272 | var maximum: Double 273 | } 274 | 275 | func BPIsValueGreaterThan(_ value: CGFloat, minimum: CGFloat) -> Bool { 276 | return BPIsValueGreaterThanWithOptions(Double(value), minimum: Double(minimum), threshold: BPTangentClosenessThreshold) 277 | } 278 | 279 | func BPIsValueGreaterThanWithOptions(_ value: Double, minimum: Double, threshold: Double) -> Bool { 280 | if BPAreValuesCloseWithOptions(value, value2: minimum, threshold: threshold) { 281 | return false 282 | } 283 | return value > minimum 284 | } 285 | 286 | func BPIsValueGreaterThan(_ value: Double, minimum: Double) -> Bool { 287 | return BPIsValueGreaterThanWithOptions(value, minimum: minimum, threshold: Double(BPTangentClosenessThreshold)) 288 | } 289 | 290 | func BPIsValueLessThan(_ value: CGFloat, maximum: CGFloat) -> Bool { 291 | if BPAreValuesCloseWithOptions(Double(value), value2: Double(maximum), threshold: BPTangentClosenessThreshold) { 292 | return false 293 | } 294 | return value < maximum 295 | } 296 | 297 | func BPIsValueLessThan(_ value: Double, maximum: Double) -> Bool { 298 | if BPAreValuesCloseWithOptions(value, value2: maximum, threshold: Double(BPTangentClosenessThreshold)) { 299 | return false 300 | } 301 | return value < maximum 302 | } 303 | 304 | func BPIsValueGreaterThanEqual(_ value: CGFloat, minimum: CGFloat) -> Bool { 305 | if BPAreValuesCloseWithOptions(Double(value), value2: Double(minimum), threshold: BPTangentClosenessThreshold) { 306 | return true 307 | } 308 | return value >= minimum 309 | } 310 | 311 | func BPIsValueGreaterThanEqual(_ value: Double, minimum: Double) -> Bool { 312 | if BPAreValuesCloseWithOptions(value, value2: minimum, threshold: Double(BPTangentClosenessThreshold)) { 313 | return true 314 | } 315 | return value >= minimum 316 | } 317 | 318 | func BPIsValueLessThanEqualWithOptions(_ value: Double, maximum: Double, threshold: Double) -> Bool { 319 | if BPAreValuesCloseWithOptions(value, value2: maximum, threshold: threshold) { 320 | return true 321 | } 322 | return value <= maximum 323 | } 324 | 325 | func BPIsValueLessThanEqual(_ value: CGFloat, maximum: CGFloat) -> Bool { 326 | return BPIsValueLessThanEqualWithOptions(Double(value), maximum: Double(maximum), threshold: BPTangentClosenessThreshold) 327 | } 328 | 329 | func BPIsValueLessThanEqual(_ value: Double, maximum: Double) -> Bool { 330 | return BPIsValueLessThanEqualWithOptions(value, maximum: maximum, threshold: BPTangentClosenessThreshold) 331 | } 332 | 333 | func BPAngleRangeContainsAngle(_ range: BPAngleRange, angle: Double) -> Bool { 334 | if range.minimum <= range.maximum { 335 | return BPIsValueGreaterThan(angle, minimum: range.minimum) && BPIsValueLessThan(angle, maximum: range.maximum) 336 | } 337 | if BPIsValueGreaterThan(angle, minimum: range.minimum) && angle <= Two_π { 338 | return true 339 | } 340 | return angle >= 0.0 && BPIsValueLessThan(angle, maximum: range.maximum) 341 | } 342 | 343 | // =================================== 344 | // MARK: Parameter ranges 345 | // =================================== 346 | 347 | struct BPRange { 348 | var minimum: Double 349 | var maximum: Double 350 | } 351 | 352 | func BPRangeHasConverged(_ range: BPRange, decimalPlaces: Int) -> Bool { 353 | let factor = pow(10.0, Double(decimalPlaces)) 354 | let minimum = Int(range.minimum * factor) 355 | let maxiumum = Int(range.maximum * factor) 356 | return minimum == maxiumum 357 | } 358 | 359 | func BPRangeGetSize(_ range: BPRange) -> Double { 360 | return range.maximum - range.minimum 361 | } 362 | 363 | func BPRangeAverage(_ range: BPRange) -> Double { 364 | return (range.minimum + range.maximum) / 2.0 365 | } 366 | 367 | func BPRangeScaleNormalizedValue(_ range: BPRange, value: Double) -> Double { 368 | return (range.maximum - range.minimum) * value + range.minimum 369 | } 370 | 371 | func BPRangeUnion(_ range1: BPRange, range2: BPRange) -> BPRange { 372 | return BPRange(minimum: min(range1.minimum, range2.minimum), maximum: max(range1.maximum, range2.maximum)) 373 | } 374 | 375 | // =================================== 376 | // MARK: Tangents 377 | // =================================== 378 | 379 | struct BPTangentPair { 380 | var left: CGPoint 381 | var right: CGPoint 382 | } 383 | 384 | func BPAreTangentsAmbigious(_ edge1Tangents: BPTangentPair, edge2Tangents: BPTangentPair) -> Bool { 385 | let normalEdge1 = BPTangentPair(left: BPNormalizePoint(edge1Tangents.left), right: BPNormalizePoint(edge1Tangents.right)) 386 | let normalEdge2 = BPTangentPair(left: BPNormalizePoint(edge2Tangents.left), right: BPNormalizePoint(edge2Tangents.right)) 387 | 388 | return BPArePointsCloseWithOptions(normalEdge1.left, point2: normalEdge2.left, threshold: BPTangentClosenessThreshold) 389 | || BPArePointsCloseWithOptions(normalEdge1.left, point2: normalEdge2.right, threshold: BPTangentClosenessThreshold) 390 | || BPArePointsCloseWithOptions(normalEdge1.right, point2: normalEdge2.left, threshold: BPTangentClosenessThreshold) 391 | || BPArePointsCloseWithOptions(normalEdge1.right, point2: normalEdge2.right, threshold: BPTangentClosenessThreshold) 392 | } 393 | 394 | struct BPAnglePair { 395 | var a: Double 396 | var b: Double 397 | } 398 | 399 | func BPTangentsCross(_ edge1Tangents: BPTangentPair, edge2Tangents: BPTangentPair) -> Bool { 400 | let edge1Angles = BPAnglePair(a: PolarAngle(edge1Tangents.left), b: PolarAngle(edge1Tangents.right)) 401 | let edge2Angles = BPAnglePair(a: PolarAngle(edge2Tangents.left), b: PolarAngle(edge2Tangents.right)) 402 | 403 | let range1 = BPAngleRange(minimum: edge1Angles.a, maximum: edge1Angles.b) 404 | var rangeCount1 = 0 405 | 406 | if BPAngleRangeContainsAngle(range1, angle: edge2Angles.a) { 407 | rangeCount1 += 1 408 | } 409 | if BPAngleRangeContainsAngle(range1, angle: edge2Angles.b) { 410 | rangeCount1 += 1 411 | } 412 | let range2 = BPAngleRange(minimum: edge1Angles.b, maximum: edge1Angles.a) 413 | var rangeCount2 = 0 414 | 415 | if BPAngleRangeContainsAngle(range2, angle: edge2Angles.a) { 416 | rangeCount2 += 1 417 | } 418 | if BPAngleRangeContainsAngle(range2, angle: edge2Angles.b) { 419 | rangeCount2 += 1 420 | } 421 | return rangeCount1 == 1 && rangeCount2 == 1 422 | } 423 | 424 | 425 | func BPLineBoundsMightOverlap(_ bounds1: CGRect, bounds2: CGRect) -> Bool { 426 | let left = Double(max(bounds1.minX, bounds2.minX)) 427 | let right = Double(min(bounds1.maxX, bounds2.maxX)) 428 | 429 | if BPIsValueGreaterThanWithOptions(left, minimum: right, threshold: BPBoundsClosenessThreshold) { 430 | return false // no horizontal overlap 431 | } 432 | 433 | let top = Double(max(bounds1.minY, bounds2.minY)) 434 | let bottom = Double(min(bounds1.maxY, bounds2.maxY)) 435 | return BPIsValueLessThanEqualWithOptions(top, maximum: bottom, threshold: BPBoundsClosenessThreshold) 436 | } 437 | -------------------------------------------------------------------------------- /BooleanPath/CGPath_Bridge/CGPath_Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPath_Extension.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import QuartzCore 16 | 17 | typealias MyPathApplier = @convention(block) (UnsafePointer) -> Void 18 | 19 | private func myPathApply(_ path: CGPath!, block: @escaping MyPathApplier) { 20 | let callback: @convention(c) (UnsafeMutableRawPointer, UnsafePointer) -> Void = { (info, element) in 21 | let block = unsafeBitCast(info, to: MyPathApplier.self) 22 | block(element) 23 | } 24 | path.apply(info: unsafeBitCast(block, to: UnsafeMutableRawPointer.self), function: unsafeBitCast(callback, to: CGPathApplierFunction.self)) 25 | } 26 | 27 | public enum PathElement { 28 | case move(to: CGPoint) 29 | case line(to: CGPoint) 30 | case quadCurve(to: CGPoint, via: CGPoint) 31 | case cubicCurve(to: CGPoint, v1: CGPoint, v2: CGPoint) 32 | case close 33 | } 34 | 35 | public extension CGPath { 36 | func apply(_ fn: @escaping (PathElement) -> Void) { 37 | myPathApply(self) { element in 38 | let points = element.pointee.points 39 | switch (element.pointee.type) { 40 | case CGPathElementType.moveToPoint: 41 | fn(.move(to: points[0])) 42 | case .addLineToPoint: 43 | fn(.line(to: points[0])) 44 | case .addQuadCurveToPoint: 45 | fn(.quadCurve(to: points[1], via: points[0])) 46 | case .addCurveToPoint: 47 | fn(.cubicCurve(to: points[2], v1: points[0], v2: points[1])) 48 | case .closeSubpath: 49 | fn(.close) 50 | } 51 | } 52 | } 53 | } 54 | 55 | 56 | -------------------------------------------------------------------------------- /BooleanPath/CGPath_Bridge/LRTBezierPathWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LRTBezierPathWrapper.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | public class LRTBezierPathWrapper { 18 | private(set) public var elements: [PathElement] 19 | fileprivate var _bezierPath: NSBezierPath 20 | 21 | var bezierPath: NSBezierPath { 22 | return _bezierPath 23 | } 24 | 25 | public init(_ bezierPath:NSBezierPath) { 26 | elements = [] 27 | _bezierPath = bezierPath 28 | createElementsFromCGPath() 29 | } 30 | 31 | func createElementsFromCGPath() { 32 | let cgPath = _bezierPath.cgPath 33 | cgPath.apply({ 34 | (e : PathElement) -> Void in 35 | self.elements.append(e) 36 | }) 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /BooleanPath/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2019 Takuto Nakamura. All rights reserved. 23 | 24 | 25 | -------------------------------------------------------------------------------- /BooleanPath/Int_Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int_Extension.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Foundation 16 | 17 | extension Int { 18 | public var isEven: Bool { 19 | return self % 2 == 0 20 | } 21 | 22 | public var isOdd: Bool { 23 | return self % 2 == 1 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BooleanPath/Legendre.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Legendre.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Foundation 16 | 17 | // Legendre-Gauss 18 | let BPLegendreGaussAbscissaeValues: [[Double]] = [ 19 | [], [], 20 | 21 | [-0.5773502691896257310588680411456152796745, 22 | 0.5773502691896257310588680411456152796745], 23 | 24 | [0.0000000000000000000000000000000000000000, 25 | -0.7745966692414834042779148148838430643082, 26 | 0.7745966692414834042779148148838430643082], 27 | 28 | [-0.3399810435848562573113440521410666406155, 29 | 0.3399810435848562573113440521410666406155, 30 | -0.8611363115940525725378051902225706726313, 31 | 0.8611363115940525725378051902225706726313], 32 | 33 | [0.0000000000000000000000000000000000000000, 34 | -0.5384693101056831077144693153968546539545, 35 | 0.5384693101056831077144693153968546539545, 36 | -0.9061798459386639637003213465504813939333, 37 | 0.9061798459386639637003213465504813939333], 38 | 39 | [0.6612093864662644815410885712481103837490, 40 | -0.6612093864662644815410885712481103837490, 41 | -0.2386191860831969047129774708082550205290, 42 | 0.2386191860831969047129774708082550205290, 43 | -0.9324695142031520500580654697841964662075, 44 | 0.9324695142031520500580654697841964662075], 45 | 46 | [0.0000000000000000000000000000000000000000, 47 | 0.4058451513773971841558818596240598708391, 48 | -0.4058451513773971841558818596240598708391, 49 | -0.7415311855993944600839995473506860435009, 50 | 0.7415311855993944600839995473506860435009, 51 | -0.9491079123427584862682238053821492940187, 52 | 0.9491079123427584862682238053821492940187], 53 | 54 | [-0.1834346424956498078362443493460887111723, 55 | 0.1834346424956498078362443493460887111723, 56 | -0.5255324099163289908176466269651427865028, 57 | 0.5255324099163289908176466269651427865028, 58 | -0.7966664774136267279658341067261062562466, 59 | 0.7966664774136267279658341067261062562466, 60 | -0.9602898564975362871720676594122778624296, 61 | 0.9602898564975362871720676594122778624296], 62 | 63 | [0.0000000000000000000000000000000000000000, 64 | -0.8360311073266357695388251158874481916428, 65 | 0.8360311073266357695388251158874481916428, 66 | -0.9681602395076260858530758923734538257122, 67 | 0.9681602395076260858530758923734538257122, 68 | -0.3242534234038089158147499801998492330313, 69 | 0.3242534234038089158147499801998492330313, 70 | -0.6133714327005903577116896485676988959312, 71 | 0.6133714327005903577116896485676988959312], 72 | 73 | [-0.1488743389816312157059030596428783610463, 74 | 0.1488743389816312157059030596428783610463, 75 | -0.4333953941292472133994806426926515996456, 76 | 0.4333953941292472133994806426926515996456, 77 | -0.6794095682990244355892173189204186201096, 78 | 0.6794095682990244355892173189204186201096, 79 | -0.8650633666889845363456856830453034490347, 80 | 0.8650633666889845363456856830453034490347, 81 | -0.9739065285171717434309357486199587583542, 82 | 0.9739065285171717434309357486199587583542], 83 | 84 | [0.0000000000000000000000000000000000000000, 85 | -0.2695431559523449593918087430211016908288, 86 | 0.2695431559523449593918087430211016908288, 87 | -0.5190961292068118071441062966187018901110, 88 | 0.5190961292068118071441062966187018901110, 89 | -0.7301520055740493564400139803183265030384, 90 | 0.7301520055740493564400139803183265030384, 91 | -0.8870625997680953167545681026240345090628, 92 | 0.8870625997680953167545681026240345090628, 93 | -0.9782286581460569729884468870295677334070, 94 | 0.9782286581460569729884468870295677334070], 95 | 96 | [-0.1252334085114689132822718420356977730989, 97 | 0.1252334085114689132822718420356977730989, 98 | -0.3678314989981801841345543380157323554158, 99 | 0.3678314989981801841345543380157323554158, 100 | -0.5873179542866174829285341729701030999422, 101 | 0.5873179542866174829285341729701030999422, 102 | -0.7699026741943046925342741815256886184216, 103 | 0.7699026741943046925342741815256886184216, 104 | -0.9041172563704749087776235683122649788857, 105 | 0.9041172563704749087776235683122649788857, 106 | -0.9815606342467192435563561048184055835009, 107 | 0.9815606342467192435563561048184055835009], 108 | 109 | [0.0000000000000000000000000000000000000000, 110 | -0.2304583159551348015003924274424207396805, 111 | 0.2304583159551348015003924274424207396805, 112 | -0.4484927510364468683512484403763664886355, 113 | 0.4484927510364468683512484403763664886355, 114 | -0.6423493394403402279024817289609927684069, 115 | 0.6423493394403402279024817289609927684069, 116 | -0.8015780907333098781464286730624735355377, 117 | 0.8015780907333098781464286730624735355377, 118 | -0.9175983992229779229177211163914762437344, 119 | 0.9175983992229779229177211163914762437344, 120 | -0.9841830547185881350458203087328001856804, 121 | 0.9841830547185881350458203087328001856804], 122 | 123 | [-0.1080549487073436676354276642086915671825, 124 | 0.1080549487073436676354276642086915671825, 125 | -0.3191123689278897446186533670697826892138, 126 | 0.3191123689278897446186533670697826892138, 127 | -0.5152486363581540995681962158414535224438, 128 | 0.5152486363581540995681962158414535224438, 129 | -0.6872929048116854788830210054584313184023, 130 | 0.6872929048116854788830210054584313184023, 131 | -0.8272013150697650196718768711434677243233, 132 | 0.8272013150697650196718768711434677243233, 133 | -0.9284348836635735180422557277779560536146, 134 | 0.9284348836635735180422557277779560536146, 135 | -0.9862838086968123141318187663273420184851, 136 | 0.9862838086968123141318187663273420184851], 137 | 138 | [0.0000000000000000000000000000000000000000, 139 | -0.2011940939974345143870237961891689337790, 140 | 0.2011940939974345143870237961891689337790, 141 | -0.3941513470775633853904196257644798606634, 142 | 0.3941513470775633853904196257644798606634, 143 | -0.5709721726085388304738899023504927754402, 144 | 0.5709721726085388304738899023504927754402, 145 | -0.7244177313601700696210627938853576779366, 146 | 0.7244177313601700696210627938853576779366, 147 | -0.8482065834104272061821916395274456590414, 148 | 0.8482065834104272061821916395274456590414, 149 | -0.9372733924007059513883177714888006448746, 150 | 0.9372733924007059513883177714888006448746, 151 | -0.9879925180204853774057482951320707798004, 152 | 0.9879925180204853774057482951320707798004], 153 | 154 | [-0.0950125098376374405129141109682677779347, 155 | 0.0950125098376374405129141109682677779347, 156 | -0.2816035507792589154263396267197094857693, 157 | 0.2816035507792589154263396267197094857693, 158 | -0.4580167776572273696800152720243204385042, 159 | 0.4580167776572273696800152720243204385042, 160 | -0.6178762444026437705701937375124543905258, 161 | 0.6178762444026437705701937375124543905258, 162 | -0.7554044083550029986540153004170861095190, 163 | 0.7554044083550029986540153004170861095190, 164 | -0.8656312023878317551961458775622304528952, 165 | 0.8656312023878317551961458775622304528952, 166 | -0.9445750230732326002680565579794347286224, 167 | 0.9445750230732326002680565579794347286224, 168 | -0.9894009349916499385102497399202547967434, 169 | 0.9894009349916499385102497399202547967434], 170 | 171 | [0.0000000000000000000000000000000000000000, 172 | -0.1784841814958478545261044700964703224599, 173 | 0.1784841814958478545261044700964703224599, 174 | -0.3512317634538763000406902392569463700056, 175 | 0.3512317634538763000406902392569463700056, 176 | -0.5126905370864769384553483178024180233479, 177 | 0.5126905370864769384553483178024180233479, 178 | -0.6576711592166907260903485621383879333735, 179 | 0.6576711592166907260903485621383879333735, 180 | -0.7815140038968013680431567991035990417004, 181 | 0.7815140038968013680431567991035990417004, 182 | -0.8802391537269859123071569229068700224161, 183 | 0.8802391537269859123071569229068700224161, 184 | -0.9506755217687677950166857954172883182764, 185 | 0.9506755217687677950166857954172883182764, 186 | -0.9905754753144173641032921295845881104469, 187 | 0.9905754753144173641032921295845881104469], 188 | 189 | [-0.0847750130417353059408824833553808275610, 190 | 0.0847750130417353059408824833553808275610, 191 | -0.2518862256915054831374334298743633553386, 192 | 0.2518862256915054831374334298743633553386, 193 | -0.4117511614628426297457508553634397685528, 194 | 0.4117511614628426297457508553634397685528, 195 | -0.5597708310739475390249708652845583856106, 196 | 0.5597708310739475390249708652845583856106, 197 | -0.6916870430603532238222896921797655522823, 198 | 0.6916870430603532238222896921797655522823, 199 | -0.8037049589725231424353069087374024093151, 200 | 0.8037049589725231424353069087374024093151, 201 | -0.8926024664975557021406871172075625509024, 202 | 0.8926024664975557021406871172075625509024, 203 | -0.9558239495713977129653926567698363214731, 204 | 0.9558239495713977129653926567698363214731, 205 | -0.9915651684209308980300079383596312254667, 206 | 0.9915651684209308980300079383596312254667], 207 | 208 | [0.0000000000000000000000000000000000000000, 209 | -0.1603586456402253668240831530056311748922, 210 | 0.1603586456402253668240831530056311748922, 211 | -0.3165640999636298302810644145210972055793, 212 | 0.3165640999636298302810644145210972055793, 213 | -0.4645707413759609383241411251219687983394, 214 | 0.4645707413759609383241411251219687983394, 215 | -0.6005453046616809897884081692609470337629, 216 | 0.6005453046616809897884081692609470337629, 217 | -0.7209661773352293856476080691209062933922, 218 | 0.7209661773352293856476080691209062933922, 219 | -0.8227146565371428188484514976153150200844, 220 | 0.8227146565371428188484514976153150200844, 221 | -0.9031559036148179009373393455462064594030, 222 | 0.9031559036148179009373393455462064594030, 223 | -0.9602081521348300174878431789693422615528, 224 | 0.9602081521348300174878431789693422615528, 225 | -0.9924068438435843519940249279898125678301, 226 | 0.9924068438435843519940249279898125678301], 227 | 228 | [-0.0765265211334973383117130651953630149364, 229 | 0.0765265211334973383117130651953630149364, 230 | -0.2277858511416450681963397073559463024139, 231 | 0.2277858511416450681963397073559463024139, 232 | -0.3737060887154195487624974703066982328892, 233 | 0.3737060887154195487624974703066982328892, 234 | -0.5108670019508271264996324134699534624815, 235 | 0.5108670019508271264996324134699534624815, 236 | -0.6360536807265150249790508496516849845648, 237 | 0.6360536807265150249790508496516849845648, 238 | -0.7463319064601507957235071444301865994930, 239 | 0.7463319064601507957235071444301865994930, 240 | -0.8391169718222187823286617458506952971220, 241 | 0.8391169718222187823286617458506952971220, 242 | -0.9122344282513259461353527512983419001102, 243 | 0.9122344282513259461353527512983419001102, 244 | -0.9639719272779138092843709273438435047865, 245 | 0.9639719272779138092843709273438435047865, 246 | -0.9931285991850948846604296704754233360291, 247 | 0.9931285991850948846604296704754233360291], 248 | 249 | [0.0000000000000000000000000000000000000000, 250 | -0.1455618541608950933241573011400760151446, 251 | 0.1455618541608950933241573011400760151446, 252 | -0.2880213168024011172185794293909566476941, 253 | 0.2880213168024011172185794293909566476941, 254 | -0.4243421202074387776903563462838064879179, 255 | 0.4243421202074387776903563462838064879179, 256 | -0.5516188358872198271853903861483559012413, 257 | 0.5516188358872198271853903861483559012413, 258 | -0.6671388041974123384036943207320291548967, 259 | 0.6671388041974123384036943207320291548967, 260 | -0.7684399634756778896260698275000322610140, 261 | 0.7684399634756778896260698275000322610140, 262 | -0.8533633645833172964856316866644192487001, 263 | 0.8533633645833172964856316866644192487001, 264 | -0.9200993341504007938524978271743748337030, 265 | 0.9200993341504007938524978271743748337030, 266 | -0.9672268385663063128276917268522083759308, 267 | 0.9672268385663063128276917268522083759308, 268 | -0.9937521706203894522602126926358323544264, 269 | 0.9937521706203894522602126926358323544264], 270 | 271 | [-0.0697392733197222253194169638845778536052, 272 | 0.0697392733197222253194169638845778536052, 273 | -0.2078604266882212725509049278116435743868, 274 | 0.2078604266882212725509049278116435743868, 275 | -0.3419358208920842412403828802780481055379, 276 | 0.3419358208920842412403828802780481055379, 277 | -0.4693558379867570073962212973128771409392, 278 | 0.4693558379867570073962212973128771409392, 279 | -0.5876404035069116016387624767958186566830, 280 | 0.5876404035069116016387624767958186566830, 281 | -0.6944872631866827461522007070016115903854, 282 | 0.6944872631866827461522007070016115903854, 283 | -0.7878168059792081123759999172762036323547, 284 | 0.7878168059792081123759999172762036323547, 285 | -0.8658125777203001804949167308222968131304, 286 | 0.8658125777203001804949167308222968131304, 287 | -0.9269567721871739829353487039043102413416, 288 | 0.9269567721871739829353487039043102413416, 289 | -0.9700604978354286922481719557254109531641, 290 | 0.9700604978354286922481719557254109531641, 291 | -0.9942945854823992402060639506089501082897, 292 | 0.9942945854823992402060639506089501082897], 293 | 294 | [0.0000000000000000000000000000000000000000, 295 | -0.1332568242984661088801345840693102218211, 296 | 0.1332568242984661088801345840693102218211, 297 | -0.2641356809703449548543119362875586375594, 298 | 0.2641356809703449548543119362875586375594, 299 | -0.3903010380302908144400930723350029438734, 300 | 0.3903010380302908144400930723350029438734, 301 | -0.5095014778460075222099590064317453652620, 302 | 0.5095014778460075222099590064317453652620, 303 | -0.6196098757636461229481028567533940076828, 304 | 0.6196098757636461229481028567533940076828, 305 | -0.7186613631319501704908248029823880642653, 306 | 0.7186613631319501704908248029823880642653, 307 | -0.8048884016188398993207897547108586877584, 308 | 0.8048884016188398993207897547108586877584, 309 | -0.8767523582704416229560706597112584859133, 310 | 0.8767523582704416229560706597112584859133, 311 | -0.9329710868260161493736859483760781586170, 312 | 0.9329710868260161493736859483760781586170, 313 | -0.9725424712181152120393790028174407780170, 314 | 0.9725424712181152120393790028174407780170, 315 | -0.9947693349975521570627279288601130247116, 316 | 0.9947693349975521570627279288601130247116], 317 | 318 | [-0.0640568928626056299791002857091370970011, 319 | 0.0640568928626056299791002857091370970011, 320 | -0.1911188674736163106704367464772076345980, 321 | 0.1911188674736163106704367464772076345980, 322 | -0.3150426796961633968408023065421730279922, 323 | 0.3150426796961633968408023065421730279922, 324 | -0.4337935076260451272567308933503227308393, 325 | 0.4337935076260451272567308933503227308393, 326 | -0.5454214713888395626995020393223967403173, 327 | 0.5454214713888395626995020393223967403173, 328 | -0.6480936519369755455244330732966773211956, 329 | 0.6480936519369755455244330732966773211956, 330 | -0.7401241915785543579175964623573236167431, 331 | 0.7401241915785543579175964623573236167431, 332 | -0.8200019859739029470802051946520805358887, 333 | 0.8200019859739029470802051946520805358887, 334 | -0.8864155270044010714869386902137193828821, 335 | 0.8864155270044010714869386902137193828821, 336 | -0.9382745520027327978951348086411599069834, 337 | 0.9382745520027327978951348086411599069834, 338 | -0.9747285559713094738043537290650419890881, 339 | 0.9747285559713094738043537290650419890881, 340 | -0.9951872199970213106468008845695294439793, 341 | 0.9951872199970213106468008845695294439793] 342 | ] 343 | 344 | let BPLegendreGaussWeightValues: [[Double]] = [ 345 | [], [], 346 | 347 | [1.0000000000000000000000000000000000000000, 348 | 1.0000000000000000000000000000000000000000], 349 | 350 | [0.8888888888888888395456433499930426478386, 351 | 0.5555555555555555802271783250034786760807, 352 | 0.5555555555555555802271783250034786760807], 353 | 354 | [0.6521451548625460947761212082696147263050, 355 | 0.6521451548625460947761212082696147263050, 356 | 0.3478548451374538497127275604725582525134, 357 | 0.3478548451374538497127275604725582525134], 358 | 359 | [0.5688888888888888883954564334999304264784, 360 | 0.4786286704993664709029133064177585765719, 361 | 0.4786286704993664709029133064177585765719, 362 | 0.2369268850561890848993584768322762101889, 363 | 0.2369268850561890848993584768322762101889], 364 | 365 | [0.3607615730481386062677984227775596082211, 366 | 0.3607615730481386062677984227775596082211, 367 | 0.4679139345726910370615314604947343468666, 368 | 0.4679139345726910370615314604947343468666, 369 | 0.1713244923791703566706701167277060449123, 370 | 0.1713244923791703566706701167277060449123], 371 | 372 | [0.4179591836734694032529091600736137479544, 373 | 0.3818300505051189230876218516641529276967, 374 | 0.3818300505051189230876218516641529276967, 375 | 0.2797053914892766446342875497066415846348, 376 | 0.2797053914892766446342875497066415846348, 377 | 0.1294849661688697028960604029634851031005, 378 | 0.1294849661688697028960604029634851031005], 379 | 380 | [0.3626837833783619902128236844873754307628, 381 | 0.3626837833783619902128236844873754307628, 382 | 0.3137066458778872690693617641954915598035, 383 | 0.3137066458778872690693617641954915598035, 384 | 0.2223810344533744820516574236535234376788, 385 | 0.2223810344533744820516574236535234376788, 386 | 0.1012285362903762586661571276636095717549, 387 | 0.1012285362903762586661571276636095717549], 388 | 389 | [0.3302393550012597822629345500899944454432, 390 | 0.1806481606948573959137149813614087179303, 391 | 0.1806481606948573959137149813614087179303, 392 | 0.0812743883615744122650426106702070683241, 393 | 0.0812743883615744122650426106702070683241, 394 | 0.3123470770400028628799304897256661206484, 395 | 0.3123470770400028628799304897256661206484, 396 | 0.2606106964029354378098446431977208703756, 397 | 0.2606106964029354378098446431977208703756], 398 | 399 | [0.2955242247147528700246255084493895992637, 400 | 0.2955242247147528700246255084493895992637, 401 | 0.2692667193099963496294435572053771466017, 402 | 0.2692667193099963496294435572053771466017, 403 | 0.2190863625159820415877476307286997325718, 404 | 0.2190863625159820415877476307286997325718, 405 | 0.1494513491505805868886369580650352872908, 406 | 0.1494513491505805868886369580650352872908, 407 | 0.0666713443086881379917585377370414789766, 408 | 0.0666713443086881379917585377370414789766], 409 | 410 | [0.2729250867779006162194832540990319103003, 411 | 0.2628045445102466515230332788632949814200, 412 | 0.2628045445102466515230332788632949814200, 413 | 0.2331937645919904822378043718344997614622, 414 | 0.2331937645919904822378043718344997614622, 415 | 0.1862902109277342621584949711177614517510, 416 | 0.1862902109277342621584949711177614517510, 417 | 0.1255803694649046120535018644659430719912, 418 | 0.1255803694649046120535018644659430719912, 419 | 0.0556685671161736631007421749472996452823, 420 | 0.0556685671161736631007421749472996452823], 421 | 422 | [0.2491470458134027732288728884668671526015, 423 | 0.2491470458134027732288728884668671526015, 424 | 0.2334925365383548057085505433860816992819, 425 | 0.2334925365383548057085505433860816992819, 426 | 0.2031674267230659247651658461109036579728, 427 | 0.2031674267230659247651658461109036579728, 428 | 0.1600783285433462210800570346691529266536, 429 | 0.1600783285433462210800570346691529266536, 430 | 0.1069393259953184266430881166343169752508, 431 | 0.1069393259953184266430881166343169752508, 432 | 0.0471753363865118277575838590109924552962, 433 | 0.0471753363865118277575838590109924552962], 434 | 435 | [0.2325515532308738975153517003491288051009, 436 | 0.2262831802628972321933531475224299356341, 437 | 0.2262831802628972321933531475224299356341, 438 | 0.2078160475368885096170146198346628807485, 439 | 0.2078160475368885096170146198346628807485, 440 | 0.1781459807619457380578609217991470359266, 441 | 0.1781459807619457380578609217991470359266, 442 | 0.1388735102197872495199959530509659089148, 443 | 0.1388735102197872495199959530509659089148, 444 | 0.0921214998377284516317686779984796885401, 445 | 0.0921214998377284516317686779984796885401, 446 | 0.0404840047653158771612247335269785253331, 447 | 0.0404840047653158771612247335269785253331], 448 | 449 | [0.2152638534631577948985636794532183557749, 450 | 0.2152638534631577948985636794532183557749, 451 | 0.2051984637212956041896205761076998896897, 452 | 0.2051984637212956041896205761076998896897, 453 | 0.1855383974779378219999159682629397138953, 454 | 0.1855383974779378219999159682629397138953, 455 | 0.1572031671581935463599677404999965801835, 456 | 0.1572031671581935463599677404999965801835, 457 | 0.1215185706879031851679329179205524269491, 458 | 0.1215185706879031851679329179205524269491, 459 | 0.0801580871597602079292599341897584963590, 460 | 0.0801580871597602079292599341897584963590, 461 | 0.0351194603317518602714208952875196700916, 462 | 0.0351194603317518602714208952875196700916], 463 | 464 | [0.2025782419255612865072180284187197685242, 465 | 0.1984314853271115786093048427574103698134, 466 | 0.1984314853271115786093048427574103698134, 467 | 0.1861610000155622113293674146916600875556, 468 | 0.1861610000155622113293674146916600875556, 469 | 0.1662692058169939202105780395868350751698, 470 | 0.1662692058169939202105780395868350751698, 471 | 0.1395706779261543240000520427201990969479, 472 | 0.1395706779261543240000520427201990969479, 473 | 0.1071592204671719394948325998484506271780, 474 | 0.1071592204671719394948325998484506271780, 475 | 0.0703660474881081243747615872052847407758, 476 | 0.0703660474881081243747615872052847407758, 477 | 0.0307532419961172691358353148416426847689, 478 | 0.0307532419961172691358353148416426847689], 479 | 480 | [0.1894506104550685021692402187909465283155, 481 | 0.1894506104550685021692402187909465283155, 482 | 0.1826034150449235837765371570640127174556, 483 | 0.1826034150449235837765371570640127174556, 484 | 0.1691565193950025358660127494658809155226, 485 | 0.1691565193950025358660127494658809155226, 486 | 0.1495959888165767359691216142891789786518, 487 | 0.1495959888165767359691216142891789786518, 488 | 0.1246289712555338768940060845125117339194, 489 | 0.1246289712555338768940060845125117339194, 490 | 0.0951585116824927856882254673109855502844, 491 | 0.0951585116824927856882254673109855502844, 492 | 0.0622535239386478936318702892549481475726, 493 | 0.0622535239386478936318702892549481475726, 494 | 0.0271524594117540964133272751723779947497, 495 | 0.0271524594117540964133272751723779947497], 496 | 497 | [0.1794464703562065333031227964966092258692, 498 | 0.1765627053669926449508409405098063871264, 499 | 0.1765627053669926449508409405098063871264, 500 | 0.1680041021564500358653759803928551264107, 501 | 0.1680041021564500358653759803928551264107, 502 | 0.1540457610768102836296122859494062140584, 503 | 0.1540457610768102836296122859494062140584, 504 | 0.1351363684685254751283167706787935458124, 505 | 0.1351363684685254751283167706787935458124, 506 | 0.1118838471934039680011352402289048768580, 507 | 0.1118838471934039680011352402289048768580, 508 | 0.0850361483171791776580761279547004960477, 509 | 0.0850361483171791776580761279547004960477, 510 | 0.0554595293739872027827253475606994470581, 511 | 0.0554595293739872027827253475606994470581, 512 | 0.0241483028685479314545681006620725383982, 513 | 0.0241483028685479314545681006620725383982], 514 | 515 | [0.1691423829631436004383715498988749459386, 516 | 0.1691423829631436004383715498988749459386, 517 | 0.1642764837458327298325144738555536605418, 518 | 0.1642764837458327298325144738555536605418, 519 | 0.1546846751262652419622867228099494241178, 520 | 0.1546846751262652419622867228099494241178, 521 | 0.1406429146706506538855308008351130411029, 522 | 0.1406429146706506538855308008351130411029, 523 | 0.1225552067114784593471199514169711619616, 524 | 0.1225552067114784593471199514169711619616, 525 | 0.1009420441062871681703327908508072141558, 526 | 0.1009420441062871681703327908508072141558, 527 | 0.0764257302548890515847546112127020023763, 528 | 0.0764257302548890515847546112127020023763, 529 | 0.0497145488949697969549568199454370187595, 530 | 0.0497145488949697969549568199454370187595, 531 | 0.0216160135264833117019200869890482863411, 532 | 0.0216160135264833117019200869890482863411], 533 | 534 | [0.1610544498487836984068621859478298574686, 535 | 0.1589688433939543399375793342187535017729, 536 | 0.1589688433939543399375793342187535017729, 537 | 0.1527660420658596696075193221986410208046, 538 | 0.1527660420658596696075193221986410208046, 539 | 0.1426067021736066031678547005867585539818, 540 | 0.1426067021736066031678547005867585539818, 541 | 0.1287539625393362141547726196222356520593, 542 | 0.1287539625393362141547726196222356520593, 543 | 0.1115666455473339896409257221421285066754, 544 | 0.1115666455473339896409257221421285066754, 545 | 0.0914900216224499990280705219447554554790, 546 | 0.0914900216224499990280705219447554554790, 547 | 0.0690445427376412262931992813719261903316, 548 | 0.0690445427376412262931992813719261903316, 549 | 0.0448142267656995996194524423117400147021, 550 | 0.0448142267656995996194524423117400147021, 551 | 0.0194617882297264781221723950466184760444, 552 | 0.0194617882297264781221723950466184760444], 553 | 554 | [0.1527533871307258372951309866039082407951, 555 | 0.1527533871307258372951309866039082407951, 556 | 0.1491729864726037413369397199858212843537, 557 | 0.1491729864726037413369397199858212843537, 558 | 0.1420961093183820411756101975697674788535, 559 | 0.1420961093183820411756101975697674788535, 560 | 0.1316886384491766370796739238357986323535, 561 | 0.1316886384491766370796739238357986323535, 562 | 0.1181945319615184120110029653005767613649, 563 | 0.1181945319615184120110029653005767613649, 564 | 0.1019301198172404415709380032240005675703, 565 | 0.1019301198172404415709380032240005675703, 566 | 0.0832767415767047547436874310733401216567, 567 | 0.0832767415767047547436874310733401216567, 568 | 0.0626720483341090678353069165495980996639, 569 | 0.0626720483341090678353069165495980996639, 570 | 0.0406014298003869386621822457072994438931, 571 | 0.0406014298003869386621822457072994438931, 572 | 0.0176140071391521178811867542890468030237, 573 | 0.0176140071391521178811867542890468030237], 574 | 575 | [0.1460811336496904144777175815761438570917, 576 | 0.1445244039899700461138110085812513716519, 577 | 0.1445244039899700461138110085812513716519, 578 | 0.1398873947910731496691028041823301464319, 579 | 0.1398873947910731496691028041823301464319, 580 | 0.1322689386333374683690777828815043903887, 581 | 0.1322689386333374683690777828815043903887, 582 | 0.1218314160537285334440227302366110961884, 583 | 0.1218314160537285334440227302366110961884, 584 | 0.1087972991671483785625085261017375160009, 585 | 0.1087972991671483785625085261017375160009, 586 | 0.0934444234560338621298214434318651910871, 587 | 0.0934444234560338621298214434318651910871, 588 | 0.0761001136283793039316591944043466355652, 589 | 0.0761001136283793039316591944043466355652, 590 | 0.0571344254268572049326735395879950374365, 591 | 0.0571344254268572049326735395879950374365, 592 | 0.0369537897708524937234741969405149575323, 593 | 0.0369537897708524937234741969405149575323, 594 | 0.0160172282577743345377552230957007850520, 595 | 0.0160172282577743345377552230957007850520], 596 | 597 | [0.1392518728556319806966001806358690373600, 598 | 0.1392518728556319806966001806358690373600, 599 | 0.1365414983460151721050834794368711300194, 600 | 0.1365414983460151721050834794368711300194, 601 | 0.1311735047870623838139891859100316651165, 602 | 0.1311735047870623838139891859100316651165, 603 | 0.1232523768105124178928733158500108402222, 604 | 0.1232523768105124178928733158500108402222, 605 | 0.1129322960805392156435900119504367467016, 606 | 0.1129322960805392156435900119504367467016, 607 | 0.1004141444428809648581335522976587526500, 608 | 0.1004141444428809648581335522976587526500, 609 | 0.0859416062170677286236042391465161927044, 610 | 0.0859416062170677286236042391465161927044, 611 | 0.0697964684245204886048341563764552120119, 612 | 0.0697964684245204886048341563764552120119, 613 | 0.0522933351526832859712534684604179346934, 614 | 0.0522933351526832859712534684604179346934, 615 | 0.0337749015848141515006020085820637177676, 616 | 0.0337749015848141515006020085820637177676, 617 | 0.0146279952982721998810955454928262042813, 618 | 0.0146279952982721998810955454928262042813], 619 | 620 | [0.1336545721861061852830943053049850277603, 621 | 0.1324620394046966131984532921705977059901, 622 | 0.1324620394046966131984532921705977059901, 623 | 0.1289057221880821613169132433540653437376, 624 | 0.1289057221880821613169132433540653437376, 625 | 0.1230490843067295336776822978208656422794, 626 | 0.1230490843067295336776822978208656422794, 627 | 0.1149966402224113642960290349037677515298, 628 | 0.1149966402224113642960290349037677515298, 629 | 0.1048920914645414120824895576333801727742, 630 | 0.1048920914645414120824895576333801727742, 631 | 0.0929157660600351542612429511791560798883, 632 | 0.0929157660600351542612429511791560798883, 633 | 0.0792814117767189491248203125906002242118, 634 | 0.0792814117767189491248203125906002242118, 635 | 0.0642324214085258499151720457120973151177, 636 | 0.0642324214085258499151720457120973151177, 637 | 0.0480376717310846690356385124687221832573, 638 | 0.0480376717310846690356385124687221832573, 639 | 0.0309880058569794447631551292943186126649, 640 | 0.0309880058569794447631551292943186126649, 641 | 0.0134118594871417712993677540112003043760, 642 | 0.0134118594871417712993677540112003043760], 643 | 644 | [0.1279381953467521593204025975865079089999, 645 | 0.1279381953467521593204025975865079089999, 646 | 0.1258374563468283025002847352880053222179, 647 | 0.1258374563468283025002847352880053222179, 648 | 0.1216704729278033914052770114722079597414, 649 | 0.1216704729278033914052770114722079597414, 650 | 0.1155056680537255991980671865348995197564, 651 | 0.1155056680537255991980671865348995197564, 652 | 0.1074442701159656343712356374453520402312, 653 | 0.1074442701159656343712356374453520402312, 654 | 0.0976186521041138843823858906034729443491, 655 | 0.0976186521041138843823858906034729443491, 656 | 0.0861901615319532743431096832864568568766, 657 | 0.0861901615319532743431096832864568568766, 658 | 0.0733464814110802998392557583429152145982, 659 | 0.0733464814110802998392557583429152145982, 660 | 0.0592985849154367833380163688161701429635, 661 | 0.0592985849154367833380163688161701429635, 662 | 0.0442774388174198077483545432642131345347, 663 | 0.0442774388174198077483545432642131345347, 664 | 0.0285313886289336633705904233693217975087, 665 | 0.0285313886289336633705904233693217975087, 666 | 0.0123412297999872001830201639904771582223, 667 | 0.0123412297999872001830201639904771582223] 668 | ] 669 | -------------------------------------------------------------------------------- /BooleanPath/NSBezierPath_Boolean.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSBezierPath_Boolean.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | public extension NSBezierPath { 18 | func union(_ path: NSBezierPath) -> NSBezierPath { 19 | let thisGraph = BPBezierGraph(path: self) 20 | let otherGraph = BPBezierGraph(path: path) 21 | let result = thisGraph.union(with: otherGraph).bezierPath 22 | result.copyAttributesFrom(self) 23 | return result 24 | } 25 | 26 | func intersection(_ path: NSBezierPath) -> NSBezierPath { 27 | let thisGraph = BPBezierGraph(path: self) 28 | let otherGraph = BPBezierGraph(path: path) 29 | let result = thisGraph.intersect(with: otherGraph).bezierPath 30 | result.copyAttributesFrom(self) 31 | return result 32 | } 33 | 34 | func subtraction(_ path: NSBezierPath) -> NSBezierPath { 35 | let thisGraph = BPBezierGraph(path: self) 36 | let otherGraph = BPBezierGraph(path: path) 37 | let result = thisGraph.subtract(with: otherGraph).bezierPath 38 | result.copyAttributesFrom(self) 39 | return result 40 | } 41 | 42 | func difference(_ path: NSBezierPath) -> NSBezierPath { 43 | let thisGraph = BPBezierGraph(path: self) 44 | let otherGraph = BPBezierGraph(path: path) 45 | let result = NSBezierPath() 46 | result.append(thisGraph.subtract(with: otherGraph).bezierPath) 47 | result.append(otherGraph.subtract(with: thisGraph).bezierPath) 48 | result.copyAttributesFrom(self) 49 | return result 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BooleanPath/NSBezierPath_Utilities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSBezierPath_Utilities.swift 3 | // BooleanPath 4 | // 5 | // Oligin is NSBezierPath+Boolean - Created by Andrew Finnell on 2011/05/31. 6 | // Copyright 2011 Fortunate Bear, LLC. All rights reserved. 7 | // 8 | // Based on VectorBoolean - Created by Leslie Titze on 2015/05/19. 9 | // Copyright (c) 2015 Leslie Titze. All rights reserved. 10 | // 11 | // Created by Takuto Nakamura on 2019/03/10. 12 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 13 | // 14 | 15 | import Cocoa 16 | 17 | public struct NSBezierElement { 18 | var kind: CGPathElementType 19 | var point: CGPoint 20 | var controlPoints: [CGPoint] 21 | } 22 | 23 | let BPDebugPointSize: CGFloat = 10.0 24 | let BPDebugSmallPointSize: CGFloat = 3.0 25 | 26 | public extension NSBezierPath { 27 | var cgPath: CGPath { 28 | let path: CGMutablePath = CGMutablePath() 29 | var points = [NSPoint](repeating: NSPoint.zero, count: 3) 30 | for i in (0 ..< self.elementCount) { 31 | switch self.element(at: i, associatedPoints: &points) { 32 | case .moveTo: 33 | path.move(to: CGPoint(x: points[0].x, y: points[0].y)) 34 | case .lineTo: 35 | path.addLine(to: CGPoint(x: points[0].x, y: points[0].y)) 36 | case .curveTo: 37 | path.addCurve(to: CGPoint(x: points[2].x, y: points[2].y), 38 | control1: CGPoint(x: points[0].x, y: points[0].y), 39 | control2: CGPoint(x: points[1].x, y: points[1].y)) 40 | case .closePath: 41 | path.closeSubpath() 42 | } 43 | } 44 | return path 45 | } 46 | 47 | var dividedPaths: [NSBezierPath] { 48 | var path: NSBezierPath? 49 | var paths = [NSBezierPath]() 50 | var points = [NSPoint](repeating: NSPoint.zero, count: 3) 51 | for i in (0 ..< self.elementCount) { 52 | switch self.element(at: i, associatedPoints: &points) { 53 | case .moveTo: 54 | path = NSBezierPath() 55 | path?.move(to: NSPoint(x: points[0].x, y: points[0].y)) 56 | case .lineTo: 57 | path?.line(to: NSPoint(x: points[0].x, y: points[0].y)) 58 | case .curveTo: 59 | path?.curve(to: NSPoint(x: points[2].x, y: points[2].y), 60 | controlPoint1: NSPoint(x: points[0].x, y: points[0].y), 61 | controlPoint2: NSPoint(x: points[1].x, y: points[1].y)) 62 | case .closePath: 63 | if let path = path { 64 | paths.append(path) 65 | } 66 | path = nil 67 | } 68 | } 69 | return paths 70 | } 71 | 72 | func copyAttributesFrom(_ path: NSBezierPath) { 73 | self.lineWidth = path.lineWidth 74 | self.lineCapStyle = path.lineCapStyle 75 | self.lineJoinStyle = path.lineJoinStyle 76 | self.miterLimit = path.miterLimit 77 | self.flatness = path.flatness 78 | } 79 | 80 | static func circleAtPoint(_ point: CGPoint) -> NSBezierPath { 81 | let rect = CGRect( 82 | x: point.x - BPDebugPointSize * 0.5, 83 | y: point.y - BPDebugPointSize * 0.5, 84 | width: BPDebugPointSize, 85 | height: BPDebugPointSize); 86 | return NSBezierPath(ovalIn: rect) 87 | } 88 | 89 | static func rectAtPoint(_ point: CGPoint) -> NSBezierPath { 90 | let rect = CGRect( 91 | x: point.x - BPDebugPointSize * 0.5, 92 | y: point.y - BPDebugPointSize * 0.5, 93 | width: BPDebugPointSize, 94 | height: BPDebugPointSize); 95 | return NSBezierPath(rect: rect) 96 | } 97 | 98 | static func smallCircleAtPoint(_ point: CGPoint) -> NSBezierPath { 99 | let rect = CGRect( 100 | x: point.x - BPDebugSmallPointSize * 0.5, 101 | y: point.y - BPDebugSmallPointSize * 0.5, 102 | width: BPDebugSmallPointSize, 103 | height: BPDebugSmallPointSize); 104 | return NSBezierPath(ovalIn: rect) 105 | } 106 | 107 | static func smallRectAtPoint(_ point: CGPoint) -> NSBezierPath { 108 | let rect = CGRect( 109 | x: point.x - BPDebugSmallPointSize * 0.5, 110 | y: point.y - BPDebugSmallPointSize * 0.5, 111 | width: BPDebugSmallPointSize, 112 | height: BPDebugSmallPointSize); 113 | return NSBezierPath(rect: rect) 114 | } 115 | 116 | static func triangleAtPoint(_ point: CGPoint, direction tangent: CGPoint) -> NSBezierPath { 117 | let endPoint = BPAddPoint(point, point2: BPScalePoint(tangent, scale: BPDebugPointSize * 1.5)) 118 | let normal1 = BPLineNormal(point, lineEnd: endPoint) 119 | let normal2 = CGPoint(x: -normal1.x, y: -normal1.y) 120 | let basePoint1 = BPAddPoint(point, point2: BPScalePoint(normal1, scale: BPDebugPointSize * 0.5)) 121 | let basePoint2 = BPAddPoint(point, point2: BPScalePoint(normal2, scale: BPDebugPointSize * 0.5)) 122 | let path = NSBezierPath() 123 | path.move(to: basePoint1) 124 | path.line(to: endPoint) 125 | path.line(to: basePoint2) 126 | path.line(to: basePoint1) 127 | path.close() 128 | return path 129 | } 130 | 131 | func callPath() { 132 | var points = [NSPoint](repeating: NSPoint.zero, count: 3) 133 | for i in (0 ..< self.elementCount) { 134 | switch self.element(at: i, associatedPoints: &points) { 135 | case .moveTo: 136 | Swift.print("moveTo: (\(points[0].x), \(points[0].y))") 137 | case .lineTo: 138 | Swift.print("lineTo: (\(points[0].x), \(points[0].y))") 139 | case .curveTo: 140 | Swift.print("moveTo: (\(points[2].x), \(points[2].y)), (\(points[0].x), \(points[0].y)), (\(points[1].x), \(points[1].y))") 141 | case .closePath: 142 | Swift.print("close") 143 | } 144 | } 145 | } 146 | } 147 | 148 | 149 | -------------------------------------------------------------------------------- /BooleanPath_Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BooleanPath_Demo 4 | // 5 | // Created by Takuto Nakamura on 2019/03/10. 6 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | func applicationDidFinishLaunching(_ aNotification: Notification) { 15 | // Insert code here to initialize your application 16 | } 17 | 18 | func applicationWillTerminate(_ aNotification: Notification) { 19 | // Insert code here to tear down your application 20 | } 21 | 22 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 23 | return true 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /BooleanPath_Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /BooleanPath_Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /BooleanPath_Demo/BooleanPath_Demo.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /BooleanPath_Demo/DemoView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoView.swift 3 | // BooleanPath_Demo 4 | // 5 | // Created by Takuto Nakamura on 2019/03/10. 6 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import BooleanPath 11 | 12 | class DemoView: NSView { 13 | 14 | required init?(coder decoder: NSCoder) { 15 | super.init(coder: decoder) 16 | self.wantsLayer = true 17 | self.layer?.backgroundColor = NSColor.white.cgColor 18 | } 19 | 20 | override func draw(_ dirtyRect: NSRect) { 21 | super.draw(dirtyRect) 22 | 23 | let w: CGFloat = self.frame.width 24 | let h: CGFloat = self.frame.height 25 | NSColor.black.set() 26 | 27 | let pathA = NSBezierPath(rect: NSRect(x: w / 10 - 30, y: h / 2 - 30, width: 60, height: 60)) 28 | let pathB = NSBezierPath(ovalIn: NSRect(x: w / 10, y: h / 2, width: 50, height: 50)) 29 | pathA.stroke() 30 | pathB.stroke() 31 | 32 | let unionPath: NSBezierPath = pathA.union(pathB) 33 | unionPath.transform(using: AffineTransform(translationByX: w / 5, byY: 0)) 34 | unionPath.fill() 35 | 36 | let intersectionPath: NSBezierPath = pathA.intersection(pathB) 37 | intersectionPath.transform(using: AffineTransform(translationByX: 2 * w / 5, byY: 0)) 38 | intersectionPath.fill() 39 | 40 | let subtractionPath: NSBezierPath = pathA.subtraction(pathB) 41 | subtractionPath.transform(using: AffineTransform(translationByX: 3 * w / 5, byY: 0)) 42 | subtractionPath.fill() 43 | 44 | let differencePath: NSBezierPath = pathA.difference(pathB) 45 | differencePath.transform(using: AffineTransform(translationByX: 4 * w / 5, byY: 0)) 46 | differencePath.fill() 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /BooleanPath_Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2019 Takuto Nakamura. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /BooleanPath_Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // BooleanPath_Demo 4 | // 5 | // Created by Takuto Nakamura on 2019/03/10. 6 | // Copyright © 2019 Takuto Nakamura. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | } 16 | 17 | override var representedObject: Any? { 18 | didSet { 19 | } 20 | } 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework.dSYM/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleIdentifier 8 | com.apple.xcode.dsym.com.kyome.BooleanPath 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundlePackageType 12 | dSYM 13 | CFBundleSignature 14 | ???? 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleVersion 18 | 1 19 | 20 | 21 | -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework.dSYM/Contents/Resources/DWARF/BooleanPath: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kyome22/BooleanPath/bf711b546cbea2bbe62a14dbdf501685593437c6/Carthage/Build/Mac/BooleanPath.framework.dSYM/Contents/Resources/DWARF/BooleanPath -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/BooleanPath: -------------------------------------------------------------------------------- 1 | Versions/Current/BooleanPath -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Modules: -------------------------------------------------------------------------------- 1 | Versions/Current/Modules -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Versions/A/BooleanPath: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kyome22/BooleanPath/bf711b546cbea2bbe62a14dbdf501685593437c6/Carthage/Build/Mac/BooleanPath.framework/Versions/A/BooleanPath -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Versions/A/Headers/BooleanPath-Swift.h: -------------------------------------------------------------------------------- 1 | // Generated by Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1) 2 | #pragma clang diagnostic push 3 | #pragma clang diagnostic ignored "-Wgcc-compat" 4 | 5 | #if !defined(__has_include) 6 | # define __has_include(x) 0 7 | #endif 8 | #if !defined(__has_attribute) 9 | # define __has_attribute(x) 0 10 | #endif 11 | #if !defined(__has_feature) 12 | # define __has_feature(x) 0 13 | #endif 14 | #if !defined(__has_warning) 15 | # define __has_warning(x) 0 16 | #endif 17 | 18 | #if __has_include() 19 | # include 20 | #endif 21 | 22 | #pragma clang diagnostic ignored "-Wauto-import" 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #if !defined(SWIFT_TYPEDEFS) 29 | # define SWIFT_TYPEDEFS 1 30 | # if __has_include() 31 | # include 32 | # elif !defined(__cplusplus) 33 | typedef uint_least16_t char16_t; 34 | typedef uint_least32_t char32_t; 35 | # endif 36 | typedef float swift_float2 __attribute__((__ext_vector_type__(2))); 37 | typedef float swift_float3 __attribute__((__ext_vector_type__(3))); 38 | typedef float swift_float4 __attribute__((__ext_vector_type__(4))); 39 | typedef double swift_double2 __attribute__((__ext_vector_type__(2))); 40 | typedef double swift_double3 __attribute__((__ext_vector_type__(3))); 41 | typedef double swift_double4 __attribute__((__ext_vector_type__(4))); 42 | typedef int swift_int2 __attribute__((__ext_vector_type__(2))); 43 | typedef int swift_int3 __attribute__((__ext_vector_type__(3))); 44 | typedef int swift_int4 __attribute__((__ext_vector_type__(4))); 45 | typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); 46 | typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); 47 | typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); 48 | #endif 49 | 50 | #if !defined(SWIFT_PASTE) 51 | # define SWIFT_PASTE_HELPER(x, y) x##y 52 | # define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) 53 | #endif 54 | #if !defined(SWIFT_METATYPE) 55 | # define SWIFT_METATYPE(X) Class 56 | #endif 57 | #if !defined(SWIFT_CLASS_PROPERTY) 58 | # if __has_feature(objc_class_property) 59 | # define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ 60 | # else 61 | # define SWIFT_CLASS_PROPERTY(...) 62 | # endif 63 | #endif 64 | 65 | #if __has_attribute(objc_runtime_name) 66 | # define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) 67 | #else 68 | # define SWIFT_RUNTIME_NAME(X) 69 | #endif 70 | #if __has_attribute(swift_name) 71 | # define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) 72 | #else 73 | # define SWIFT_COMPILE_NAME(X) 74 | #endif 75 | #if __has_attribute(objc_method_family) 76 | # define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) 77 | #else 78 | # define SWIFT_METHOD_FAMILY(X) 79 | #endif 80 | #if __has_attribute(noescape) 81 | # define SWIFT_NOESCAPE __attribute__((noescape)) 82 | #else 83 | # define SWIFT_NOESCAPE 84 | #endif 85 | #if __has_attribute(warn_unused_result) 86 | # define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) 87 | #else 88 | # define SWIFT_WARN_UNUSED_RESULT 89 | #endif 90 | #if __has_attribute(noreturn) 91 | # define SWIFT_NORETURN __attribute__((noreturn)) 92 | #else 93 | # define SWIFT_NORETURN 94 | #endif 95 | #if !defined(SWIFT_CLASS_EXTRA) 96 | # define SWIFT_CLASS_EXTRA 97 | #endif 98 | #if !defined(SWIFT_PROTOCOL_EXTRA) 99 | # define SWIFT_PROTOCOL_EXTRA 100 | #endif 101 | #if !defined(SWIFT_ENUM_EXTRA) 102 | # define SWIFT_ENUM_EXTRA 103 | #endif 104 | #if !defined(SWIFT_CLASS) 105 | # if __has_attribute(objc_subclassing_restricted) 106 | # define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA 107 | # define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA 108 | # else 109 | # define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA 110 | # define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA 111 | # endif 112 | #endif 113 | 114 | #if !defined(SWIFT_PROTOCOL) 115 | # define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA 116 | # define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA 117 | #endif 118 | 119 | #if !defined(SWIFT_EXTENSION) 120 | # define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) 121 | #endif 122 | 123 | #if !defined(OBJC_DESIGNATED_INITIALIZER) 124 | # if __has_attribute(objc_designated_initializer) 125 | # define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) 126 | # else 127 | # define OBJC_DESIGNATED_INITIALIZER 128 | # endif 129 | #endif 130 | #if !defined(SWIFT_ENUM_ATTR) 131 | # if defined(__has_attribute) && __has_attribute(enum_extensibility) 132 | # define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility))) 133 | # else 134 | # define SWIFT_ENUM_ATTR(_extensibility) 135 | # endif 136 | #endif 137 | #if !defined(SWIFT_ENUM) 138 | # define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type 139 | # if __has_feature(generalized_swift_name) 140 | # define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type 141 | # else 142 | # define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) 143 | # endif 144 | #endif 145 | #if !defined(SWIFT_UNAVAILABLE) 146 | # define SWIFT_UNAVAILABLE __attribute__((unavailable)) 147 | #endif 148 | #if !defined(SWIFT_UNAVAILABLE_MSG) 149 | # define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) 150 | #endif 151 | #if !defined(SWIFT_AVAILABILITY) 152 | # define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) 153 | #endif 154 | #if !defined(SWIFT_DEPRECATED) 155 | # define SWIFT_DEPRECATED __attribute__((deprecated)) 156 | #endif 157 | #if !defined(SWIFT_DEPRECATED_MSG) 158 | # define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) 159 | #endif 160 | #if __has_feature(attribute_diagnose_if_objc) 161 | # define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning"))) 162 | #else 163 | # define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg) 164 | #endif 165 | #if __has_feature(modules) 166 | @import AppKit; 167 | #endif 168 | 169 | #pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" 170 | #pragma clang diagnostic ignored "-Wduplicate-method-arg" 171 | #if __has_warning("-Wpragma-clang-attribute") 172 | # pragma clang diagnostic ignored "-Wpragma-clang-attribute" 173 | #endif 174 | #pragma clang diagnostic ignored "-Wunknown-pragmas" 175 | #pragma clang diagnostic ignored "-Wnullability" 176 | 177 | #if __has_attribute(external_source_symbol) 178 | # pragma push_macro("any") 179 | # undef any 180 | # pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="BooleanPath",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) 181 | # pragma pop_macro("any") 182 | #endif 183 | 184 | 185 | 186 | 187 | 188 | #if __has_attribute(external_source_symbol) 189 | # pragma clang attribute pop 190 | #endif 191 | #pragma clang diagnostic pop 192 | -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Versions/A/Modules/BooleanPath.swiftmodule/x86_64.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kyome22/BooleanPath/bf711b546cbea2bbe62a14dbdf501685593437c6/Carthage/Build/Mac/BooleanPath.framework/Versions/A/Modules/BooleanPath.swiftmodule/x86_64.swiftdoc -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Versions/A/Modules/BooleanPath.swiftmodule/x86_64.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kyome22/BooleanPath/bf711b546cbea2bbe62a14dbdf501685593437c6/Carthage/Build/Mac/BooleanPath.framework/Versions/A/Modules/BooleanPath.swiftmodule/x86_64.swiftmodule -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Versions/A/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module BooleanPath { 2 | header "BooleanPath-Swift.h" 3 | requires objc 4 | } 5 | -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 18D109 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | BooleanPath 11 | CFBundleIdentifier 12 | com.kyome.BooleanPath 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | BooleanPath 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSupportedPlatforms 22 | 23 | MacOSX 24 | 25 | CFBundleVersion 26 | 1 27 | DTCompiler 28 | com.apple.compilers.llvm.clang.1_0 29 | DTPlatformBuild 30 | 10B61 31 | DTPlatformVersion 32 | GM 33 | DTSDKBuild 34 | 18B71 35 | DTSDKName 36 | macosx10.14 37 | DTXcode 38 | 1010 39 | DTXcodeBuild 40 | 10B61 41 | NSHumanReadableCopyright 42 | Copyright © 2019 Takuto Nakamura. All rights reserved. 43 | 44 | 45 | -------------------------------------------------------------------------------- /Carthage/Build/Mac/BooleanPath.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2011 Andrew Finnell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BooleanPath for macOS 2 | Add boolean operations to NSBezierPath like the pathfinder of Adobe Illustrator. 3 | 4 | ## About BooleanPath 5 | This is a rewrite of [VectorBoolean](https://github.com/lrtitze/Swift-VectorBoolean) written by Leslie Titze's. 6 | BooleanPath is written by Swift for macOS. 7 | 8 | ## Installation 9 | ### CocoaPods 10 | ``` 11 | pod 'BooleanPath' 12 | ``` 13 | 14 | ### Carthage 15 | ``` 16 | github "Kyome22/BooleanPath" 17 | ``` 18 | 19 | ## Demo 20 | 21 | The sample code is in the project. 22 | 23 | ![sample](https://github.com/Kyome22/BooleanPath/blob/master/images/sample.png) 24 | 25 | ## Usage (Example) 26 | 27 | ```swift 28 | import Cocoa 29 | import BooleanPath 30 | 31 | let rectPath = NSBezierPath(rect: NSRect(x: 10, y: 30, width: 60, height: 60)) 32 | let circlePath = NSBezierPath(ovalIn: NSRect(x: 25, y: 15, width: 50, height: 50)) 33 | 34 | // Union 35 | let unionPath: NSBezierPath = rectPath.union(circlePath) 36 | unionPath.fill() 37 | 38 | // Intersection 39 | let intersectionPath: NSBezierPath = rectPath.intersection(circlePath) 40 | intersectionPath.fill() 41 | 42 | // Subtraction 43 | let subtractionPath: NSBezierPath = rectPath.subtraction(circlePath) 44 | subtractionPath.fill() 45 | 46 | // Difference 47 | let differencePath: NSBezierPath = rectPath.difference(circlePath) 48 | differencePath.fill() 49 | ``` 50 | -------------------------------------------------------------------------------- /images/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kyome22/BooleanPath/bf711b546cbea2bbe62a14dbdf501685593437c6/images/sample.png --------------------------------------------------------------------------------