├── .gitignore ├── .swift-version ├── .travis.yml ├── Art ├── effect.jpg └── logo.png ├── LICENSE ├── Porygon-Demo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── camera.imageset │ │ ├── Contents.json │ │ └── camera.jpg ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── Porygon.podspec ├── Porygon.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ ├── Porygon.xcscheme │ │ └── Tests.xcscheme └── xcuserdata │ └── devinshine.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── Porygon-Demo.xcscheme │ └── xcschememanagement.plist ├── README.md ├── Sources ├── DVSPorygon.h ├── DVSPorygon.m ├── Info.plist ├── Poisson.cpp ├── Poisson.hpp ├── PoissonGenerator.cpp ├── PoissonGenerator.hpp ├── Porygon.h ├── UIImage+DVSPixel.h ├── UIImage+DVSPixel.m ├── delaunay.c └── delaunay.h └── Tests ├── Info.plist ├── Tests.m └── camera.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8.3 3 | xcode_project: Porygon.xcodeproj 4 | xcode_scheme: Porygon 5 | 6 | script: 7 | - xcodebuild clean build -sdk iphonesimulator -scheme Porygon CODE_SIGNING_REQUIRED=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES 8 | after_success: 9 | - bash <(curl -s https://codecov.io/bash) 10 | -------------------------------------------------------------------------------- /Art/effect.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinShine/Porygon/8b9b0a4a6ffee3dfbdba1f591afa64882f4e1c3a/Art/effect.jpg -------------------------------------------------------------------------------- /Art/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinShine/Porygon/8b9b0a4a6ffee3dfbdba1f591afa64882f4e1c3a/Art/logo.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 DevinShine 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 | -------------------------------------------------------------------------------- /Porygon-Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Porygon-Demo 4 | // 5 | // Created by DevinShine on 2017/6/12. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @interface AppDelegate : UIResponder 30 | 31 | @property (strong, nonatomic) UIWindow *window; 32 | 33 | 34 | @end 35 | 36 | -------------------------------------------------------------------------------- /Porygon-Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Porygon-Demo 4 | // 5 | // Created by DevinShine on 2017/6/12. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "AppDelegate.h" 28 | 29 | @interface AppDelegate () 30 | 31 | @end 32 | 33 | @implementation AppDelegate 34 | 35 | 36 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 37 | // Override point for customization after application launch. 38 | return YES; 39 | } 40 | 41 | 42 | - (void)applicationWillResignActive:(UIApplication *)application { 43 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 44 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 45 | } 46 | 47 | 48 | - (void)applicationDidEnterBackground:(UIApplication *)application { 49 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 50 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 51 | } 52 | 53 | 54 | - (void)applicationWillEnterForeground:(UIApplication *)application { 55 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 56 | } 57 | 58 | 59 | - (void)applicationDidBecomeActive:(UIApplication *)application { 60 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 61 | } 62 | 63 | 64 | - (void)applicationWillTerminate:(UIApplication *)application { 65 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 66 | } 67 | 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /Porygon-Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /Porygon-Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Porygon-Demo/Assets.xcassets/camera.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "camera.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Porygon-Demo/Assets.xcassets/camera.imageset/camera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinShine/Porygon/8b9b0a4a6ffee3dfbdba1f591afa64882f4e1c3a/Porygon-Demo/Assets.xcassets/camera.imageset/camera.jpg -------------------------------------------------------------------------------- /Porygon-Demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Porygon-Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Porygon-Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 0.0.3 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Porygon-Demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Porygon-Demo 4 | // 5 | // Created by DevinShine on 2017/6/12. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @interface ViewController : UIViewController 30 | 31 | 32 | @end 33 | 34 | -------------------------------------------------------------------------------- /Porygon-Demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Porygon-Demo 4 | // 5 | // Created by DevinShine on 2017/6/12. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "ViewController.h" 28 | #import "DVSPorygon.h" 29 | @interface ViewController () 30 | @property (weak, nonatomic) IBOutlet UIImageView *imageView; 31 | @property (weak, nonatomic) IBOutlet UISlider *slider; 32 | @property (nonatomic) DVSPorygon *porygon; 33 | @end 34 | 35 | @implementation ViewController 36 | 37 | - (void)viewDidLoad { 38 | [super viewDidLoad]; 39 | _porygon = [[DVSPorygon alloc] init]; 40 | 41 | _slider.minimumValue = DVS_MIN_VERTEX_COUNT; 42 | _slider.maximumValue = DVS_MAX_VERTEX_COUNT; 43 | _slider.value = _porygon.vertexCount; 44 | _imageView.image = [_porygon lowPolyWithImage:[UIImage imageNamed:@"camera"]]; 45 | 46 | [_slider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; 47 | } 48 | 49 | - (IBAction)sliderValueChanged:(UISlider *)sender { 50 | int count = sender.value; 51 | _porygon.vertexCount = count; 52 | _imageView.image = [_porygon lowPolyWithImage:[UIImage imageNamed:@"camera"]]; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Porygon-Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Porygon-Demo 4 | // 5 | // Created by DevinShine on 2017/6/12. 6 | // Copyright © 2017年 DevinShine. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Porygon.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Porygon" 3 | s.version = "0.0.2" 4 | s.summary = "Generate low-poly style images." 5 | s.description = <<-DESC 6 | Porygon is a library for generate low-poly style images. 7 | DESC 8 | 9 | s.homepage = "https://github.com/DevinShine/Porygon" 10 | s.license = { :type => "MIT", :file => "LICENSE" } 11 | s.author = { "DevinShine" => "devin.xdw@gmail.com" } 12 | s.platform = :ios, "8.0" 13 | s.source = { :git => "https://github.com/DevinShine/Porygon.git", :tag => s.version } 14 | s.source_files = "Sources/*.{h,m,c}" 15 | s.public_header_files = "Sources/*.h" 16 | s.framework = "UIKit" 17 | s.requires_arc = true 18 | end 19 | -------------------------------------------------------------------------------- /Porygon.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2BD182161EED8F3500271843 /* Porygon.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BD182141EED8F3500271843 /* Porygon.h */; }; 11 | 2BD182581EED92BA00271843 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BD182571EED92BA00271843 /* Tests.m */; }; 12 | 2BD1825A1EED92BA00271843 /* Porygon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BD182031EED8CF200271843 /* Porygon.framework */; }; 13 | 2BD182621EED93E600271843 /* delaunay.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BD182601EED93E600271843 /* delaunay.c */; }; 14 | 2BD182631EED93E600271843 /* delaunay.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BD182611EED93E600271843 /* delaunay.h */; }; 15 | 2BD182691EEE35E900271843 /* UIImage+DVSPixel.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BD182671EEE35E900271843 /* UIImage+DVSPixel.h */; }; 16 | 2BD1826A1EEE35E900271843 /* UIImage+DVSPixel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BD182681EEE35E900271843 /* UIImage+DVSPixel.m */; }; 17 | 2BD1826D1EEE37D200271843 /* DVSPorygon.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BD1826B1EEE37D200271843 /* DVSPorygon.h */; }; 18 | 2BD1826E1EEE37D200271843 /* DVSPorygon.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BD1826C1EEE37D200271843 /* DVSPorygon.m */; }; 19 | 2BD182701EEE42C200271843 /* camera.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 2BD1826F1EEE42C200271843 /* camera.jpg */; }; 20 | 2BD182791EEE454200271843 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BD182781EEE454200271843 /* main.m */; }; 21 | 2BD1827C1EEE454200271843 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BD1827B1EEE454200271843 /* AppDelegate.m */; }; 22 | 2BD1827F1EEE454200271843 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BD1827E1EEE454200271843 /* ViewController.m */; }; 23 | 2BD182821EEE454200271843 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2BD182801EEE454200271843 /* Main.storyboard */; }; 24 | 2BD182841EEE454200271843 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2BD182831EEE454200271843 /* Assets.xcassets */; }; 25 | 2BD182871EEE454200271843 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2BD182851EEE454200271843 /* LaunchScreen.storyboard */; }; 26 | 2BD182901EEE47E600271843 /* Porygon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BD182031EED8CF200271843 /* Porygon.framework */; }; 27 | 2BD183061EF2462300271843 /* Poisson.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BD183021EF2462300271843 /* Poisson.cpp */; }; 28 | 2BD183071EF2462300271843 /* Poisson.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2BD183031EF2462300271843 /* Poisson.hpp */; }; 29 | 2BD183081EF2462300271843 /* PoissonGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BD183041EF2462300271843 /* PoissonGenerator.cpp */; }; 30 | 2BD183091EF2462300271843 /* PoissonGenerator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2BD183051EF2462300271843 /* PoissonGenerator.hpp */; }; 31 | 2BD1830C1EF2465100271843 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BD1830B1EF2465100271843 /* UIKit.framework */; }; 32 | 2BD1830E1EF2465600271843 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BD1830D1EF2465600271843 /* CoreGraphics.framework */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXContainerItemProxy section */ 36 | 2BD1825B1EED92BA00271843 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 2BD181FA1EED8CF200271843 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 2BD182021EED8CF200271843; 41 | remoteInfo = Porygon; 42 | }; 43 | 2BD182911EEE483200271843 /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 2BD181FA1EED8CF200271843 /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = 2BD182021EED8CF200271843; 48 | remoteInfo = Porygon; 49 | }; 50 | /* End PBXContainerItemProxy section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | 2BD182031EED8CF200271843 /* Porygon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Porygon.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 2BD182131EED8F3500271843 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | 2BD182141EED8F3500271843 /* Porygon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Porygon.h; sourceTree = ""; }; 56 | 2BD182551EED92BA00271843 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 2BD182571EED92BA00271843 /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; 58 | 2BD182591EED92BA00271843 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 2BD182601EED93E600271843 /* delaunay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = delaunay.c; sourceTree = ""; }; 60 | 2BD182611EED93E600271843 /* delaunay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = delaunay.h; sourceTree = ""; }; 61 | 2BD182671EEE35E900271843 /* UIImage+DVSPixel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+DVSPixel.h"; sourceTree = ""; }; 62 | 2BD182681EEE35E900271843 /* UIImage+DVSPixel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+DVSPixel.m"; sourceTree = ""; }; 63 | 2BD1826B1EEE37D200271843 /* DVSPorygon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVSPorygon.h; sourceTree = ""; }; 64 | 2BD1826C1EEE37D200271843 /* DVSPorygon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DVSPorygon.m; sourceTree = ""; }; 65 | 2BD1826F1EEE42C200271843 /* camera.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = camera.jpg; sourceTree = ""; }; 66 | 2BD182751EEE454200271843 /* Porygon-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Porygon-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | 2BD182781EEE454200271843 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 68 | 2BD1827A1EEE454200271843 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 69 | 2BD1827B1EEE454200271843 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 70 | 2BD1827D1EEE454200271843 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 71 | 2BD1827E1EEE454200271843 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 72 | 2BD182811EEE454200271843 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 73 | 2BD182831EEE454200271843 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 74 | 2BD182861EEE454200271843 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 75 | 2BD182881EEE454200271843 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 76 | 2BD183021EF2462300271843 /* Poisson.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Poisson.cpp; sourceTree = ""; }; 77 | 2BD183031EF2462300271843 /* Poisson.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Poisson.hpp; sourceTree = ""; }; 78 | 2BD183041EF2462300271843 /* PoissonGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PoissonGenerator.cpp; sourceTree = ""; }; 79 | 2BD183051EF2462300271843 /* PoissonGenerator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PoissonGenerator.hpp; sourceTree = ""; }; 80 | 2BD1830B1EF2465100271843 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 81 | 2BD1830D1EF2465600271843 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 82 | /* End PBXFileReference section */ 83 | 84 | /* Begin PBXFrameworksBuildPhase section */ 85 | 2BD181FF1EED8CF200271843 /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | 2BD1830E1EF2465600271843 /* CoreGraphics.framework in Frameworks */, 90 | 2BD1830C1EF2465100271843 /* UIKit.framework in Frameworks */, 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | 2BD182521EED92BA00271843 /* Frameworks */ = { 95 | isa = PBXFrameworksBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | 2BD1825A1EED92BA00271843 /* Porygon.framework in Frameworks */, 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | 2BD182721EEE454200271843 /* Frameworks */ = { 103 | isa = PBXFrameworksBuildPhase; 104 | buildActionMask = 2147483647; 105 | files = ( 106 | 2BD182901EEE47E600271843 /* Porygon.framework in Frameworks */, 107 | ); 108 | runOnlyForDeploymentPostprocessing = 0; 109 | }; 110 | /* End PBXFrameworksBuildPhase section */ 111 | 112 | /* Begin PBXGroup section */ 113 | 2BD181F91EED8CF200271843 = { 114 | isa = PBXGroup; 115 | children = ( 116 | 2BD182121EED8F3500271843 /* Sources */, 117 | 2BD182561EED92BA00271843 /* Tests */, 118 | 2BD182761EEE454200271843 /* Porygon-Demo */, 119 | 2BD182041EED8CF200271843 /* Products */, 120 | 2BD1830A1EF2465100271843 /* Frameworks */, 121 | ); 122 | sourceTree = ""; 123 | }; 124 | 2BD182041EED8CF200271843 /* Products */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 2BD182031EED8CF200271843 /* Porygon.framework */, 128 | 2BD182551EED92BA00271843 /* Tests.xctest */, 129 | 2BD182751EEE454200271843 /* Porygon-Demo.app */, 130 | ); 131 | name = Products; 132 | sourceTree = ""; 133 | }; 134 | 2BD182121EED8F3500271843 /* Sources */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 2BD182601EED93E600271843 /* delaunay.c */, 138 | 2BD182611EED93E600271843 /* delaunay.h */, 139 | 2BD182671EEE35E900271843 /* UIImage+DVSPixel.h */, 140 | 2BD182681EEE35E900271843 /* UIImage+DVSPixel.m */, 141 | 2BD1826B1EEE37D200271843 /* DVSPorygon.h */, 142 | 2BD1826C1EEE37D200271843 /* DVSPorygon.m */, 143 | 2BD182131EED8F3500271843 /* Info.plist */, 144 | 2BD182141EED8F3500271843 /* Porygon.h */, 145 | 2BD183021EF2462300271843 /* Poisson.cpp */, 146 | 2BD183031EF2462300271843 /* Poisson.hpp */, 147 | 2BD183041EF2462300271843 /* PoissonGenerator.cpp */, 148 | 2BD183051EF2462300271843 /* PoissonGenerator.hpp */, 149 | ); 150 | path = Sources; 151 | sourceTree = ""; 152 | }; 153 | 2BD182561EED92BA00271843 /* Tests */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 2BD1826F1EEE42C200271843 /* camera.jpg */, 157 | 2BD182571EED92BA00271843 /* Tests.m */, 158 | 2BD182591EED92BA00271843 /* Info.plist */, 159 | ); 160 | path = Tests; 161 | sourceTree = ""; 162 | }; 163 | 2BD182761EEE454200271843 /* Porygon-Demo */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 2BD1827A1EEE454200271843 /* AppDelegate.h */, 167 | 2BD1827B1EEE454200271843 /* AppDelegate.m */, 168 | 2BD1827D1EEE454200271843 /* ViewController.h */, 169 | 2BD1827E1EEE454200271843 /* ViewController.m */, 170 | 2BD182801EEE454200271843 /* Main.storyboard */, 171 | 2BD182831EEE454200271843 /* Assets.xcassets */, 172 | 2BD182851EEE454200271843 /* LaunchScreen.storyboard */, 173 | 2BD182881EEE454200271843 /* Info.plist */, 174 | 2BD182771EEE454200271843 /* Supporting Files */, 175 | ); 176 | path = "Porygon-Demo"; 177 | sourceTree = ""; 178 | }; 179 | 2BD182771EEE454200271843 /* Supporting Files */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 2BD182781EEE454200271843 /* main.m */, 183 | ); 184 | name = "Supporting Files"; 185 | sourceTree = ""; 186 | }; 187 | 2BD1830A1EF2465100271843 /* Frameworks */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 2BD1830D1EF2465600271843 /* CoreGraphics.framework */, 191 | 2BD1830B1EF2465100271843 /* UIKit.framework */, 192 | ); 193 | name = Frameworks; 194 | sourceTree = ""; 195 | }; 196 | /* End PBXGroup section */ 197 | 198 | /* Begin PBXHeadersBuildPhase section */ 199 | 2BD182001EED8CF200271843 /* Headers */ = { 200 | isa = PBXHeadersBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | 2BD182161EED8F3500271843 /* Porygon.h in Headers */, 204 | 2BD183071EF2462300271843 /* Poisson.hpp in Headers */, 205 | 2BD183091EF2462300271843 /* PoissonGenerator.hpp in Headers */, 206 | 2BD1826D1EEE37D200271843 /* DVSPorygon.h in Headers */, 207 | 2BD182691EEE35E900271843 /* UIImage+DVSPixel.h in Headers */, 208 | 2BD182631EED93E600271843 /* delaunay.h in Headers */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXHeadersBuildPhase section */ 213 | 214 | /* Begin PBXNativeTarget section */ 215 | 2BD182021EED8CF200271843 /* Porygon */ = { 216 | isa = PBXNativeTarget; 217 | buildConfigurationList = 2BD1820B1EED8CF200271843 /* Build configuration list for PBXNativeTarget "Porygon" */; 218 | buildPhases = ( 219 | 2BD181FE1EED8CF200271843 /* Sources */, 220 | 2BD181FF1EED8CF200271843 /* Frameworks */, 221 | 2BD182001EED8CF200271843 /* Headers */, 222 | 2BD182011EED8CF200271843 /* Resources */, 223 | ); 224 | buildRules = ( 225 | ); 226 | dependencies = ( 227 | ); 228 | name = Porygon; 229 | productName = Porygon; 230 | productReference = 2BD182031EED8CF200271843 /* Porygon.framework */; 231 | productType = "com.apple.product-type.framework"; 232 | }; 233 | 2BD182541EED92BA00271843 /* Tests */ = { 234 | isa = PBXNativeTarget; 235 | buildConfigurationList = 2BD1825D1EED92BA00271843 /* Build configuration list for PBXNativeTarget "Tests" */; 236 | buildPhases = ( 237 | 2BD182511EED92BA00271843 /* Sources */, 238 | 2BD182521EED92BA00271843 /* Frameworks */, 239 | 2BD182531EED92BA00271843 /* Resources */, 240 | ); 241 | buildRules = ( 242 | ); 243 | dependencies = ( 244 | 2BD1825C1EED92BA00271843 /* PBXTargetDependency */, 245 | ); 246 | name = Tests; 247 | productName = Tests; 248 | productReference = 2BD182551EED92BA00271843 /* Tests.xctest */; 249 | productType = "com.apple.product-type.bundle.unit-test"; 250 | }; 251 | 2BD182741EEE454200271843 /* Porygon-Demo */ = { 252 | isa = PBXNativeTarget; 253 | buildConfigurationList = 2BD182891EEE454200271843 /* Build configuration list for PBXNativeTarget "Porygon-Demo" */; 254 | buildPhases = ( 255 | 2BD182711EEE454200271843 /* Sources */, 256 | 2BD182721EEE454200271843 /* Frameworks */, 257 | 2BD182731EEE454200271843 /* Resources */, 258 | ); 259 | buildRules = ( 260 | ); 261 | dependencies = ( 262 | 2BD182921EEE483200271843 /* PBXTargetDependency */, 263 | ); 264 | name = "Porygon-Demo"; 265 | productName = "Porygon-Demo"; 266 | productReference = 2BD182751EEE454200271843 /* Porygon-Demo.app */; 267 | productType = "com.apple.product-type.application"; 268 | }; 269 | /* End PBXNativeTarget section */ 270 | 271 | /* Begin PBXProject section */ 272 | 2BD181FA1EED8CF200271843 /* Project object */ = { 273 | isa = PBXProject; 274 | attributes = { 275 | LastUpgradeCheck = 0830; 276 | ORGANIZATIONNAME = DevinShine; 277 | TargetAttributes = { 278 | 2BD182021EED8CF200271843 = { 279 | CreatedOnToolsVersion = 8.3.1; 280 | ProvisioningStyle = Manual; 281 | }; 282 | 2BD182541EED92BA00271843 = { 283 | CreatedOnToolsVersion = 8.3.1; 284 | ProvisioningStyle = Manual; 285 | }; 286 | 2BD182741EEE454200271843 = { 287 | CreatedOnToolsVersion = 8.3.1; 288 | ProvisioningStyle = Automatic; 289 | }; 290 | }; 291 | }; 292 | buildConfigurationList = 2BD181FD1EED8CF200271843 /* Build configuration list for PBXProject "Porygon" */; 293 | compatibilityVersion = "Xcode 3.2"; 294 | developmentRegion = English; 295 | hasScannedForEncodings = 0; 296 | knownRegions = ( 297 | en, 298 | Base, 299 | ); 300 | mainGroup = 2BD181F91EED8CF200271843; 301 | productRefGroup = 2BD182041EED8CF200271843 /* Products */; 302 | projectDirPath = ""; 303 | projectRoot = ""; 304 | targets = ( 305 | 2BD182021EED8CF200271843 /* Porygon */, 306 | 2BD182541EED92BA00271843 /* Tests */, 307 | 2BD182741EEE454200271843 /* Porygon-Demo */, 308 | ); 309 | }; 310 | /* End PBXProject section */ 311 | 312 | /* Begin PBXResourcesBuildPhase section */ 313 | 2BD182011EED8CF200271843 /* Resources */ = { 314 | isa = PBXResourcesBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | }; 320 | 2BD182531EED92BA00271843 /* Resources */ = { 321 | isa = PBXResourcesBuildPhase; 322 | buildActionMask = 2147483647; 323 | files = ( 324 | 2BD182701EEE42C200271843 /* camera.jpg in Resources */, 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | 2BD182731EEE454200271843 /* Resources */ = { 329 | isa = PBXResourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | 2BD182871EEE454200271843 /* LaunchScreen.storyboard in Resources */, 333 | 2BD182841EEE454200271843 /* Assets.xcassets in Resources */, 334 | 2BD182821EEE454200271843 /* Main.storyboard in Resources */, 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | }; 338 | /* End PBXResourcesBuildPhase section */ 339 | 340 | /* Begin PBXSourcesBuildPhase section */ 341 | 2BD181FE1EED8CF200271843 /* Sources */ = { 342 | isa = PBXSourcesBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | 2BD1826A1EEE35E900271843 /* UIImage+DVSPixel.m in Sources */, 346 | 2BD183081EF2462300271843 /* PoissonGenerator.cpp in Sources */, 347 | 2BD1826E1EEE37D200271843 /* DVSPorygon.m in Sources */, 348 | 2BD182621EED93E600271843 /* delaunay.c in Sources */, 349 | 2BD183061EF2462300271843 /* Poisson.cpp in Sources */, 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | 2BD182511EED92BA00271843 /* Sources */ = { 354 | isa = PBXSourcesBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | 2BD182581EED92BA00271843 /* Tests.m in Sources */, 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | 2BD182711EEE454200271843 /* Sources */ = { 362 | isa = PBXSourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | 2BD1827F1EEE454200271843 /* ViewController.m in Sources */, 366 | 2BD1827C1EEE454200271843 /* AppDelegate.m in Sources */, 367 | 2BD182791EEE454200271843 /* main.m in Sources */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | /* End PBXSourcesBuildPhase section */ 372 | 373 | /* Begin PBXTargetDependency section */ 374 | 2BD1825C1EED92BA00271843 /* PBXTargetDependency */ = { 375 | isa = PBXTargetDependency; 376 | target = 2BD182021EED8CF200271843 /* Porygon */; 377 | targetProxy = 2BD1825B1EED92BA00271843 /* PBXContainerItemProxy */; 378 | }; 379 | 2BD182921EEE483200271843 /* PBXTargetDependency */ = { 380 | isa = PBXTargetDependency; 381 | target = 2BD182021EED8CF200271843 /* Porygon */; 382 | targetProxy = 2BD182911EEE483200271843 /* PBXContainerItemProxy */; 383 | }; 384 | /* End PBXTargetDependency section */ 385 | 386 | /* Begin PBXVariantGroup section */ 387 | 2BD182801EEE454200271843 /* Main.storyboard */ = { 388 | isa = PBXVariantGroup; 389 | children = ( 390 | 2BD182811EEE454200271843 /* Base */, 391 | ); 392 | name = Main.storyboard; 393 | sourceTree = ""; 394 | }; 395 | 2BD182851EEE454200271843 /* LaunchScreen.storyboard */ = { 396 | isa = PBXVariantGroup; 397 | children = ( 398 | 2BD182861EEE454200271843 /* Base */, 399 | ); 400 | name = LaunchScreen.storyboard; 401 | sourceTree = ""; 402 | }; 403 | /* End PBXVariantGroup section */ 404 | 405 | /* Begin XCBuildConfiguration section */ 406 | 2BD182091EED8CF200271843 /* Debug */ = { 407 | isa = XCBuildConfiguration; 408 | buildSettings = { 409 | ALWAYS_SEARCH_USER_PATHS = NO; 410 | CLANG_ANALYZER_NONNULL = YES; 411 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 412 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 413 | CLANG_CXX_LIBRARY = "libc++"; 414 | CLANG_ENABLE_MODULES = YES; 415 | CLANG_ENABLE_OBJC_ARC = YES; 416 | CLANG_WARN_BOOL_CONVERSION = YES; 417 | CLANG_WARN_CONSTANT_CONVERSION = YES; 418 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 419 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 420 | CLANG_WARN_EMPTY_BODY = YES; 421 | CLANG_WARN_ENUM_CONVERSION = YES; 422 | CLANG_WARN_INFINITE_RECURSION = YES; 423 | CLANG_WARN_INT_CONVERSION = YES; 424 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 425 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 426 | CLANG_WARN_UNREACHABLE_CODE = YES; 427 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 428 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 429 | COPY_PHASE_STRIP = NO; 430 | CURRENT_PROJECT_VERSION = 1; 431 | DEBUG_INFORMATION_FORMAT = dwarf; 432 | ENABLE_STRICT_OBJC_MSGSEND = YES; 433 | ENABLE_TESTABILITY = YES; 434 | GCC_C_LANGUAGE_STANDARD = gnu99; 435 | GCC_DYNAMIC_NO_PIC = NO; 436 | GCC_NO_COMMON_BLOCKS = YES; 437 | GCC_OPTIMIZATION_LEVEL = 0; 438 | GCC_PREPROCESSOR_DEFINITIONS = ( 439 | "DEBUG=1", 440 | "$(inherited)", 441 | ); 442 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 443 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 444 | GCC_WARN_UNDECLARED_SELECTOR = YES; 445 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 446 | GCC_WARN_UNUSED_FUNCTION = YES; 447 | GCC_WARN_UNUSED_VARIABLE = YES; 448 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 449 | MTL_ENABLE_DEBUG_INFO = YES; 450 | ONLY_ACTIVE_ARCH = YES; 451 | SDKROOT = iphoneos; 452 | TARGETED_DEVICE_FAMILY = "1,2"; 453 | VERSIONING_SYSTEM = "apple-generic"; 454 | VERSION_INFO_PREFIX = ""; 455 | }; 456 | name = Debug; 457 | }; 458 | 2BD1820A1EED8CF200271843 /* Release */ = { 459 | isa = XCBuildConfiguration; 460 | buildSettings = { 461 | ALWAYS_SEARCH_USER_PATHS = NO; 462 | CLANG_ANALYZER_NONNULL = YES; 463 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 464 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 465 | CLANG_CXX_LIBRARY = "libc++"; 466 | CLANG_ENABLE_MODULES = YES; 467 | CLANG_ENABLE_OBJC_ARC = YES; 468 | CLANG_WARN_BOOL_CONVERSION = YES; 469 | CLANG_WARN_CONSTANT_CONVERSION = YES; 470 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 471 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 472 | CLANG_WARN_EMPTY_BODY = YES; 473 | CLANG_WARN_ENUM_CONVERSION = YES; 474 | CLANG_WARN_INFINITE_RECURSION = YES; 475 | CLANG_WARN_INT_CONVERSION = YES; 476 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 477 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 478 | CLANG_WARN_UNREACHABLE_CODE = YES; 479 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 480 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 481 | COPY_PHASE_STRIP = NO; 482 | CURRENT_PROJECT_VERSION = 1; 483 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 484 | ENABLE_NS_ASSERTIONS = NO; 485 | ENABLE_STRICT_OBJC_MSGSEND = YES; 486 | GCC_C_LANGUAGE_STANDARD = gnu99; 487 | GCC_NO_COMMON_BLOCKS = YES; 488 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 489 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 490 | GCC_WARN_UNDECLARED_SELECTOR = YES; 491 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 492 | GCC_WARN_UNUSED_FUNCTION = YES; 493 | GCC_WARN_UNUSED_VARIABLE = YES; 494 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 495 | MTL_ENABLE_DEBUG_INFO = NO; 496 | SDKROOT = iphoneos; 497 | TARGETED_DEVICE_FAMILY = "1,2"; 498 | VALIDATE_PRODUCT = YES; 499 | VERSIONING_SYSTEM = "apple-generic"; 500 | VERSION_INFO_PREFIX = ""; 501 | }; 502 | name = Release; 503 | }; 504 | 2BD1820C1EED8CF200271843 /* Debug */ = { 505 | isa = XCBuildConfiguration; 506 | buildSettings = { 507 | CODE_SIGN_IDENTITY = ""; 508 | DEFINES_MODULE = YES; 509 | DEVELOPMENT_TEAM = ""; 510 | DYLIB_COMPATIBILITY_VERSION = 1; 511 | DYLIB_CURRENT_VERSION = 1; 512 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 513 | GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; 514 | INFOPLIST_FILE = Sources/Info.plist; 515 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 516 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 517 | PRODUCT_BUNDLE_IDENTIFIER = com.shadev.Porygon; 518 | PRODUCT_NAME = "$(TARGET_NAME)"; 519 | PROVISIONING_PROFILE_SPECIFIER = ""; 520 | SKIP_INSTALL = YES; 521 | }; 522 | name = Debug; 523 | }; 524 | 2BD1820D1EED8CF200271843 /* Release */ = { 525 | isa = XCBuildConfiguration; 526 | buildSettings = { 527 | CODE_SIGN_IDENTITY = ""; 528 | DEFINES_MODULE = YES; 529 | DEVELOPMENT_TEAM = ""; 530 | DYLIB_COMPATIBILITY_VERSION = 1; 531 | DYLIB_CURRENT_VERSION = 1; 532 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 533 | GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; 534 | INFOPLIST_FILE = Sources/Info.plist; 535 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 536 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 537 | PRODUCT_BUNDLE_IDENTIFIER = com.shadev.Porygon; 538 | PRODUCT_NAME = "$(TARGET_NAME)"; 539 | PROVISIONING_PROFILE_SPECIFIER = ""; 540 | SKIP_INSTALL = YES; 541 | }; 542 | name = Release; 543 | }; 544 | 2BD1825E1EED92BA00271843 /* Debug */ = { 545 | isa = XCBuildConfiguration; 546 | buildSettings = { 547 | DEVELOPMENT_TEAM = ""; 548 | INFOPLIST_FILE = Tests/Info.plist; 549 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 550 | PRODUCT_BUNDLE_IDENTIFIER = com.shadev.Tests; 551 | PRODUCT_NAME = "$(TARGET_NAME)"; 552 | PROVISIONING_PROFILE_SPECIFIER = ""; 553 | }; 554 | name = Debug; 555 | }; 556 | 2BD1825F1EED92BA00271843 /* Release */ = { 557 | isa = XCBuildConfiguration; 558 | buildSettings = { 559 | DEVELOPMENT_TEAM = ""; 560 | INFOPLIST_FILE = Tests/Info.plist; 561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 562 | PRODUCT_BUNDLE_IDENTIFIER = com.shadev.Tests; 563 | PRODUCT_NAME = "$(TARGET_NAME)"; 564 | PROVISIONING_PROFILE_SPECIFIER = ""; 565 | }; 566 | name = Release; 567 | }; 568 | 2BD1828A1EEE454200271843 /* Debug */ = { 569 | isa = XCBuildConfiguration; 570 | buildSettings = { 571 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 572 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 573 | DEVELOPMENT_TEAM = ""; 574 | INFOPLIST_FILE = "Porygon-Demo/Info.plist"; 575 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 576 | PRODUCT_BUNDLE_IDENTIFIER = "com.shadev.Porygon-Demo"; 577 | PRODUCT_NAME = "$(TARGET_NAME)"; 578 | PROVISIONING_PROFILE_SPECIFIER = ""; 579 | }; 580 | name = Debug; 581 | }; 582 | 2BD1828B1EEE454200271843 /* Release */ = { 583 | isa = XCBuildConfiguration; 584 | buildSettings = { 585 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 586 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 587 | DEVELOPMENT_TEAM = ""; 588 | INFOPLIST_FILE = "Porygon-Demo/Info.plist"; 589 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 590 | PRODUCT_BUNDLE_IDENTIFIER = "com.shadev.Porygon-Demo"; 591 | PRODUCT_NAME = "$(TARGET_NAME)"; 592 | PROVISIONING_PROFILE_SPECIFIER = ""; 593 | }; 594 | name = Release; 595 | }; 596 | /* End XCBuildConfiguration section */ 597 | 598 | /* Begin XCConfigurationList section */ 599 | 2BD181FD1EED8CF200271843 /* Build configuration list for PBXProject "Porygon" */ = { 600 | isa = XCConfigurationList; 601 | buildConfigurations = ( 602 | 2BD182091EED8CF200271843 /* Debug */, 603 | 2BD1820A1EED8CF200271843 /* Release */, 604 | ); 605 | defaultConfigurationIsVisible = 0; 606 | defaultConfigurationName = Release; 607 | }; 608 | 2BD1820B1EED8CF200271843 /* Build configuration list for PBXNativeTarget "Porygon" */ = { 609 | isa = XCConfigurationList; 610 | buildConfigurations = ( 611 | 2BD1820C1EED8CF200271843 /* Debug */, 612 | 2BD1820D1EED8CF200271843 /* Release */, 613 | ); 614 | defaultConfigurationIsVisible = 0; 615 | defaultConfigurationName = Release; 616 | }; 617 | 2BD1825D1EED92BA00271843 /* Build configuration list for PBXNativeTarget "Tests" */ = { 618 | isa = XCConfigurationList; 619 | buildConfigurations = ( 620 | 2BD1825E1EED92BA00271843 /* Debug */, 621 | 2BD1825F1EED92BA00271843 /* Release */, 622 | ); 623 | defaultConfigurationIsVisible = 0; 624 | defaultConfigurationName = Release; 625 | }; 626 | 2BD182891EEE454200271843 /* Build configuration list for PBXNativeTarget "Porygon-Demo" */ = { 627 | isa = XCConfigurationList; 628 | buildConfigurations = ( 629 | 2BD1828A1EEE454200271843 /* Debug */, 630 | 2BD1828B1EEE454200271843 /* Release */, 631 | ); 632 | defaultConfigurationIsVisible = 0; 633 | defaultConfigurationName = Release; 634 | }; 635 | /* End XCConfigurationList section */ 636 | }; 637 | rootObject = 2BD181FA1EED8CF200271843 /* Project object */; 638 | } 639 | -------------------------------------------------------------------------------- /Porygon.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Porygon.xcodeproj/xcshareddata/xcschemes/Porygon.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 | -------------------------------------------------------------------------------- /Porygon.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 16 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 40 | 41 | 42 | 43 | 49 | 50 | 52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Porygon.xcodeproj/xcuserdata/devinshine.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Porygon.xcodeproj/xcuserdata/devinshine.xcuserdatad/xcschemes/Porygon-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 | -------------------------------------------------------------------------------- /Porygon.xcodeproj/xcuserdata/devinshine.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Porygon-Demo.xcscheme 8 | 9 | orderHint 10 | 2 11 | 12 | Porygon.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | Tests.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 1 21 | 22 | 23 | SuppressBuildableAutocreation 24 | 25 | 2BD182021EED8CF200271843 26 | 27 | primary 28 | 29 | 30 | 2BD182541EED92BA00271843 31 | 32 | primary 33 | 34 | 35 | 2BD182741EEE454200271843 36 | 37 | primary 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Porygon 3 |

4 | 5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | codebeat badge 13 |

14 | 15 | Porygon is a library for generate low-poly style images. It's algorithm is based on this [paper](http://ieeexplore.ieee.org/document/7314186/) and  [Polyvia](https://github.com/Ovilia/Polyvia). The Delaunay algorithm used is this [library](https://github.com/eloraiby/delaunay). 16 | 17 | 如果你看的懂中文,你可以看我的这篇[文章](http://www.jianshu.com/p/2438c99e519e),里面会介绍实现的思路。 18 | ## Effect 19 | ![](Art/effect.jpg) 20 | 21 | ### Detail 22 | 23 | * Get the edge points 24 | * Use Sobel to find the edge vertices and randomly take a few vertices 25 | * Generate random vertices using Poisson samples 26 | * Merge these vertices 27 | * Use Delaunay to form a number of triangles 28 | * Get the color from the center of the triangle and fill the color 29 | 30 | ## Requirements 31 | 32 | * iOS 6.0+ 33 | * Xcode 7.0+ 34 | 35 | ## Installation 36 | 37 | ### CocoaPods 38 | 39 | 1. Add `pod 'Porygon'` to your Podfile. 40 | 2. Run `pod install` or `pod update`. 41 | 3. Import . 42 | 43 | ### Manually 44 | 45 | 1. Download all the files in the **Sources** subdirectory. 46 | 2. Add the source files to your Xcode project. 47 | 3. Link with required frameworks: 48 | * UIKit 49 | 4. Import `DVSPorygon.h`. 50 | 51 | ## Useage 52 | 53 | ### Easy use 54 | ``` objc 55 | DVSPorygon *porygon = [[DVSPorygon alloc] init]; 56 | UIImage * lowPolyImage = [porygon lowPolyWithImage:[UIImage imageNamed:@"camera"]]; // get low poly image 57 | ``` 58 | 59 | ### Configuration parameters 60 | 61 | ``` objc 62 | DVSPorygon *porygon = [[DVSPorygon alloc] init]; 63 | // edge vertex count 64 | porygon.vertexCount = 10000; 65 | // randomly add the number of vertices 66 | porygon.randomCount = 200; 67 | // show wireframe effects 68 | porygon.isWireframe = true; 69 | UIImage * lowPolyImage = [porygon lowPolyWithImage:[UIImage imageNamed:@"camera"]]; // get low poly image 70 | ``` 71 | 72 | ## License 73 | Porygon is provided under the MIT license. See LICENSE file for details. 74 | 75 | -------------------------------------------------------------------------------- /Sources/DVSPorygon.h: -------------------------------------------------------------------------------- 1 | // 2 | // DVSPorygon.h 3 | // Porygon 4 | // 5 | // Created by DevinShine on 2017/6/12. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @interface DVSPorygon : NSObject 30 | 31 | UIKIT_EXTERN int const DVS_MAX_VERTEX_COUNT; 32 | UIKIT_EXTERN int const DVS_MIN_VERTEX_COUNT; 33 | 34 | @property (nonatomic) int randomCount; 35 | @property (nonatomic) int vertexCount; 36 | @property (nonatomic) BOOL isWireframe; 37 | // default use Poisson Disk Sampling 38 | @property (nonatomic) BOOL isPoisson; 39 | - (UIImage *)lowPolyWithImage:(UIImage *) image; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Sources/DVSPorygon.m: -------------------------------------------------------------------------------- 1 | // 2 | // DVSPorygon.m 3 | // Porygon 4 | // 5 | // Created by DevinShine on 2017/6/12. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | // 27 | // You can use this code to see the effect of sobel 28 | // CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 1.0); 29 | // CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0); 30 | // CGRect rectangle = CGRectMake(0, 0, w, h); 31 | // CGContextFillRect(context, rectangle); 32 | // for (DVSPoint *p in edgePointSet) { 33 | // CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); 34 | // CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0); 35 | // CGRect circleRect = CGRectMake(p.x, p.y, 4, 4); 36 | // CGContextFillEllipseInRect(context, circleRect); 37 | // } 38 | 39 | #import "DVSPorygon.h" 40 | #import "UIImage+DVSPixel.h" 41 | #include "delaunay.h" 42 | #import "Poisson.hpp" 43 | 44 | #define NELEMS(x) (sizeof(x) / sizeof(x[0])) 45 | 46 | int const DVS_GRAY_LIMIT_VALUE = 40; 47 | int const DVS_DEFAULT_RANDOM_COUNT = 500; 48 | int const DVS_DEFAULT_VERTEX_COUNT = 500; 49 | int const DVS_MAX_VERTEX_COUNT = 5000; 50 | int const DVS_MIN_VERTEX_COUNT = 100; 51 | 52 | @interface DVSPoint : NSObject 53 | @property (nonatomic) int x; 54 | @property (nonatomic) int y; 55 | - (instancetype)initWithX:(int)x 56 | y:(int)y; 57 | @end 58 | 59 | @implementation DVSPoint 60 | 61 | - (instancetype)initWithX:(int)x 62 | y:(int)y { 63 | self = [super init]; 64 | if (self) { 65 | _x = x; 66 | _y = y; 67 | } 68 | return self; 69 | } 70 | 71 | - (BOOL)isEqual:(id)object { 72 | return [object isKindOfClass:[DVSPoint class]] && 73 | self.x == ((DVSPoint *)object).x && 74 | self.y == ((DVSPoint *)object).y; 75 | } 76 | 77 | - (NSUInteger)hash { 78 | int times = 1; 79 | while (times <= _y) 80 | times *= 10; 81 | return _x * times + _y; 82 | } 83 | @end 84 | 85 | @implementation DVSPorygon 86 | 87 | #pragma mark Helper Code 88 | static void shuffle(void *array, size_t n, size_t size) { 89 | char tmp[size]; 90 | char *arr = (char *)array; 91 | size_t stride = size * sizeof(char); 92 | 93 | if (n > 1) { 94 | size_t i; 95 | for (i = 0; i < n - 1; ++i) { 96 | size_t rnd = (size_t)rand(); 97 | size_t j = i + rnd / (RAND_MAX / (n - i) + 1); 98 | 99 | memcpy(tmp, arr + j * stride, size); 100 | memcpy(arr + j * stride, arr + i * stride, size); 101 | memcpy(arr + i * stride, tmp, size); 102 | } 103 | } 104 | } 105 | #pragma mark Sobel Code 106 | 107 | int const SOBEL_X[3][3] = {{-1, 0, 1}, 108 | {-2, 0, 2}, 109 | {-1, 0, 1}}; 110 | int const SOBEL_Y[3][3] = {{-1, -2, -1}, 111 | {0, 0, 0}, 112 | {1, 2, 1}}; 113 | 114 | int get_color(unsigned char *pixel, int w, int h, int x, int y) { 115 | int index = (w * y + x) * 4; 116 | if (index < 0 || index >= w * h * 4) { 117 | return 0; 118 | } 119 | return pixel[index]; 120 | } 121 | 122 | int get_color_i(unsigned char *pixel, int w, int h, int x, int y, int i) { 123 | int index = (w * y + x) * 4 + i; 124 | if (index < 0 || index >= w * h * 4) { 125 | return 0; 126 | } 127 | return (int)pixel[index]; 128 | } 129 | 130 | void set_color_i(unsigned char *pixel, int w, int h, int x, int y, int val, int i) { 131 | int index = (w * y + x) * 4 + i; 132 | if (index < 0 || index >= w * h * 4) { 133 | return; 134 | } 135 | pixel[index] = val; 136 | } 137 | 138 | int get_x(unsigned char *pixel, int w, int h, int x, int y) { 139 | if (x <= 0 || y <= 0 || x + 1 == w || y + 1 == h) { 140 | return 0; 141 | } 142 | int pixel_x = ((SOBEL_X[0][0] * get_color(pixel, w, h, x - 1, y - 1)) + 143 | (SOBEL_X[0][1] * get_color(pixel, w, h, x, y - 1)) + 144 | (SOBEL_X[0][2] * get_color(pixel, w, h, x + 1, y - 1)) + 145 | (SOBEL_X[1][0] * get_color(pixel, w, h, x - 1, y)) + 146 | (SOBEL_X[1][1] * get_color(pixel, w, h, x, y)) + 147 | (SOBEL_X[1][2] * get_color(pixel, w, h, x + 1, y)) + 148 | (SOBEL_X[2][0] * get_color(pixel, w, h, x - 1, y + 1)) + 149 | (SOBEL_X[2][1] * get_color(pixel, w, h, x, y + 1)) + 150 | (SOBEL_X[2][2] * get_color(pixel, w, h, x + 1, y + 1))); 151 | return pixel_x; 152 | } 153 | 154 | int get_y(unsigned char *pixel, int w, int h, int x, int y) { 155 | if (x <= 0 || y <= 0 || x + 1 == w || y + 1 == h) { 156 | return 0; 157 | } 158 | int pixel_Y = ((SOBEL_Y[0][0] * get_color(pixel, w, h, x - 1, y - 1)) + 159 | (SOBEL_Y[0][1] * get_color(pixel, w, h, x, y - 1)) + 160 | (SOBEL_Y[0][2] * get_color(pixel, w, h, x + 1, y - 1)) + 161 | 162 | (SOBEL_Y[1][0] * get_color(pixel, w, h, x - 1, y)) + 163 | (SOBEL_Y[1][1] * get_color(pixel, w, h, x, y)) + 164 | (SOBEL_Y[1][2] * get_color(pixel, w, h, x + 1, y)) + 165 | 166 | (SOBEL_Y[2][0] * get_color(pixel, w, h, x - 1, y + 1)) + 167 | (SOBEL_Y[2][1] * get_color(pixel, w, h, x, y + 1)) + 168 | (SOBEL_Y[2][2] * get_color(pixel, w, h, x + 1, y + 1))); 169 | return pixel_Y; 170 | } 171 | 172 | #pragma mark Setter 173 | 174 | - (void)setVertexCount:(int)vertexCount { 175 | if (vertexCount < DVS_MIN_VERTEX_COUNT) { 176 | _vertexCount = DVS_MIN_VERTEX_COUNT; 177 | } else if (vertexCount > DVS_MAX_VERTEX_COUNT) { 178 | _vertexCount = DVS_MAX_VERTEX_COUNT; 179 | } else { 180 | _vertexCount = vertexCount; 181 | } 182 | } 183 | 184 | #pragma mark Public 185 | 186 | - (instancetype)init { 187 | self = [super init]; 188 | if (self) { 189 | _randomCount = DVS_DEFAULT_RANDOM_COUNT; 190 | _vertexCount = DVS_DEFAULT_VERTEX_COUNT; 191 | _isWireframe = false; 192 | _isPoisson = true; 193 | } 194 | return self; 195 | } 196 | 197 | - (UIImage *)lowPolyWithImage:(UIImage *)image { 198 | if (!image) { 199 | return nil; 200 | } 201 | 202 | struct PixelData pd = [image pixelData]; 203 | unsigned char *pixel = pd.rawData; 204 | int w = pd.width; 205 | int h = pd.height; 206 | 207 | unsigned char *grayPixel = (unsigned char *)calloc(w * h * 4, sizeof(unsigned char)); 208 | unsigned char *sobelPixel = (unsigned char *)calloc(w * h * 4, sizeof(unsigned char)); 209 | 210 | // to gray 211 | for (int i = 0; i < w * h * 4; i += 4) { 212 | int r = pixel[i]; 213 | int g = pixel[i + 1]; 214 | int b = pixel[i + 2]; 215 | int avg = (r + g + b) / 3; 216 | grayPixel[i] = avg; 217 | grayPixel[i + 1] = avg; 218 | grayPixel[i + 2] = avg; 219 | grayPixel[i + 3] = 255; 220 | } 221 | 222 | int grayCount = 0; 223 | for (int y = 0; y < h; y++) { 224 | for (int x = 0; x < w; x++) { 225 | int pixelX = get_x(grayPixel, w, h, x, y); 226 | int pixelY = get_y(grayPixel, w, h, x, y); 227 | int boundary_gray = ((unsigned int)sqrt(pixelX * pixelX + pixelY * pixelY)) >> 0; 228 | set_color_i(sobelPixel, w, h, x, y, boundary_gray, 0); 229 | set_color_i(sobelPixel, w, h, x, y, boundary_gray, 1); 230 | set_color_i(sobelPixel, w, h, x, y, boundary_gray, 2); 231 | set_color_i(sobelPixel, w, h, x, y, 255, 3); 232 | if (boundary_gray > DVS_GRAY_LIMIT_VALUE) { 233 | grayCount++; 234 | } 235 | } 236 | } 237 | 238 | // save edge ponit 239 | int j = 0; 240 | del_point2d_t *points = (del_point2d_t *)calloc(grayCount, sizeof(del_point2d_t)); 241 | for (int y = 0; y < h; y++) { 242 | for (int x = 0; x < w; x++) { 243 | if (get_color(sobelPixel, w, h, x, y) > DVS_GRAY_LIMIT_VALUE) { 244 | points[j].x = x; 245 | points[j].y = y; 246 | j++; 247 | } 248 | } 249 | } 250 | 251 | // shuffle edge point 252 | shuffle(points, grayCount, sizeof(points[0])); 253 | 254 | int shuffe_count = _vertexCount; // selected edge point count 255 | NSMutableArray *edgePointArray = [NSMutableArray arrayWithCapacity:shuffe_count]; 256 | for (int i = 0; i < shuffe_count; i++) { 257 | [edgePointArray addObject:[[DVSPoint alloc] initWithX:points[i].x y:points[i].y]]; 258 | } 259 | NSMutableSet *edgePointSet = [NSMutableSet setWithArray:edgePointArray]; 260 | 261 | // add top vertex and bottom vertex 262 | [edgePointSet addObject:[[DVSPoint alloc] initWithX:0 y:0]]; 263 | [edgePointSet addObject:[[DVSPoint alloc] initWithX:w y:0]]; 264 | [edgePointSet addObject:[[DVSPoint alloc] initWithX:w y:h]]; 265 | [edgePointSet addObject:[[DVSPoint alloc] initWithX:0 y:h]]; 266 | 267 | // add random point 268 | if (_isPoisson) { 269 | const auto Points = generatePosisson(_randomCount); 270 | for ( auto i = Points.begin(); i != Points.end(); i++ ) 271 | { 272 | DVSPoint *p = [DVSPoint new]; 273 | p.x = i->x * w; 274 | p.y = i->y * h; 275 | [edgePointSet addObject:p]; 276 | } 277 | }else{ 278 | for (int i = 0; i < _randomCount; i++) { 279 | DVSPoint *p = [DVSPoint new]; 280 | p.x = arc4random() % 100 / 100.0 * w; 281 | p.y = arc4random() % 100 / 100.0 * h; 282 | [edgePointSet addObject:p]; 283 | } 284 | } 285 | 286 | 287 | NSUInteger result_count = [edgePointSet count]; 288 | del_point2d_t *result_points = (del_point2d_t *)calloc(result_count, sizeof(del_point2d_t)); 289 | 290 | int i = 0; 291 | for (DVSPoint *p in edgePointSet) { 292 | result_points[i].x = p.x; 293 | result_points[i].y = p.y; 294 | i++; 295 | } 296 | 297 | // step 2. convert to 2d delaunay 298 | delaunay2d_t *t = delaunay2d_from(result_points, (int)result_count); 299 | tri_delaunay2d_t *tri = tri_delaunay2d_from(t); 300 | 301 | int thickness = 1; 302 | UIGraphicsBeginImageContext(CGSizeMake(w, h)); 303 | CGContextRef context = UIGraphicsGetCurrentContext(); 304 | CGContextSetAllowsAntialiasing(context, true); 305 | CGContextSetShouldAntialias(context, true); 306 | CGContextBeginPath(context); 307 | 308 | for (int i = 0, j = 0; i < tri->num_triangles; i++, j += 3) { 309 | int indice_1 = tri->tris[j]; 310 | int indice_2 = tri->tris[j + 1]; 311 | int indice_3 = tri->tris[j + 2]; 312 | int x1 = tri->points[indice_1].x; 313 | int y1 = tri->points[indice_1].y; 314 | int x2 = tri->points[indice_2].x; 315 | int y2 = tri->points[indice_2].y; 316 | int x3 = tri->points[indice_3].x; 317 | int y3 = tri->points[indice_3].y; 318 | 319 | // Get the coordinates of the color 320 | int x = (x1 + x2 + x3) / 3; 321 | int y = (y1 + y2 + y3) / 3; 322 | 323 | float r = get_color_i(pixel, w, h, x, y, 0) / 255.0; 324 | float g = get_color_i(pixel, w, h, x, y, 1) / 255.0; 325 | float b = get_color_i(pixel, w, h, x, y, 2) / 255.0; 326 | float a = get_color_i(pixel, w, h, x, y, 3) / 255.0; 327 | 328 | CGContextSetRGBFillColor(context, r, g, b, a); 329 | CGContextSetRGBStrokeColor(context, r, g, b, a); 330 | CGContextSetLineWidth(context, thickness); 331 | CGContextMoveToPoint(context, x1, y1); 332 | CGContextAddLineToPoint(context, x2, y2); 333 | CGContextAddLineToPoint(context, x3, y3); 334 | CGContextAddLineToPoint(context, x1, y1); 335 | CGContextDrawPath(context, kCGPathFillStroke); 336 | 337 | // draw wireframe 338 | if (_isWireframe) { 339 | CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0); 340 | CGContextSetLineWidth(context, thickness); 341 | CGContextMoveToPoint(context, x1, y1); 342 | CGContextAddLineToPoint(context, x2, y2); 343 | CGContextAddLineToPoint(context, x3, y3); 344 | CGContextAddLineToPoint(context, x1, y1); 345 | CGContextStrokePath(context); 346 | } 347 | } 348 | 349 | UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); 350 | UIGraphicsEndImageContext(); 351 | 352 | tri_delaunay2d_release(tri); 353 | delaunay2d_release(t); 354 | free(result_points); 355 | free(points); 356 | free(sobelPixel); 357 | free(grayPixel); 358 | free(pixel); 359 | return outputImage; 360 | } 361 | 362 | #pragma mark Private 363 | 364 | @end 365 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.0.3 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Poisson.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file Poisson.cpp 3 | * \brief 4 | * 5 | * Poisson Disk Points Generator example 6 | * 7 | * \version 1.1.3 8 | * \date 10/03/2016 9 | * \author Sergey Kosarevsky, 2014-2016 10 | * \author support@linderdaum.com http://www.linderdaum.com http://blog.linderdaum.com 11 | */ 12 | #include "Poisson.hpp" 13 | 14 | #include 15 | #import "PoissonGenerator.hpp" 16 | 17 | std::vector generatePosisson(int numPoints) { 18 | PoissonGenerator::DefaultPRNG PRNG; 19 | std::vector Points = PoissonGenerator::GeneratePoissonPoints(numPoints,PRNG); 20 | return Points; 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Poisson.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file Poisson.cpp 3 | * \brief 4 | * 5 | * Poisson Disk Points Generator example 6 | * 7 | * \version 1.1.3 8 | * \date 10/03/2016 9 | * \author Sergey Kosarevsky, 2014-2016 10 | * \author support@linderdaum.com http://www.linderdaum.com http://blog.linderdaum.com 11 | */ 12 | 13 | /* 14 | To compile: 15 | gcc Poisson.cpp -std=c++11 -lstdc++ 16 | */ 17 | 18 | #ifndef Poisson_hpp 19 | #define Poisson_hpp 20 | #include 21 | #include "PoissonGenerator.hpp" 22 | std::vector generatePosisson(int numPoints); 23 | #endif /* Poisson_hpp */ 24 | -------------------------------------------------------------------------------- /Sources/PoissonGenerator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Poisson Disk Points Generator 3 | // 4 | // \version 1.1.4 5 | // \date 19/10/2016 6 | // \author Sergey Kosarevsky, 2014-2016 7 | // \author support@linderdaum.com http://www.linderdaum.com http://blog.linderdaum.com 8 | // 9 | 10 | #include "PoissonGenerator.hpp" 11 | namespace PoissonGenerator 12 | { 13 | float DefaultPRNG::RandomFloat() 14 | { 15 | return static_cast( m_Dis( m_Gen ) ); 16 | } 17 | 18 | int DefaultPRNG::RandomInt( int Max ) 19 | { 20 | std::uniform_int_distribution<> DisInt( 0, Max ); 21 | return DisInt( m_Gen ); 22 | } 23 | 24 | float GetDistance( const sPoint& P1, const sPoint& P2 ) 25 | { 26 | return sqrt( ( P1.x - P2.x ) * ( P1.x - P2.x ) + ( P1.y - P2.y ) * ( P1.y - P2.y ) ); 27 | } 28 | 29 | sGridPoint ImageToGrid( const sPoint& P, float CellSize ) 30 | { 31 | return sGridPoint( ( int )( P.x / CellSize ), ( int )( P.y / CellSize ) ); 32 | } 33 | 34 | template 35 | sPoint PopRandom( std::vector& Points, PRNG& Generator ) 36 | { 37 | const int Idx = Generator.RandomInt( (int)Points.size()-1 ); 38 | const sPoint P = Points[ Idx ]; 39 | Points.erase( Points.begin() + Idx ); 40 | return P; 41 | } 42 | 43 | template 44 | sPoint GenerateRandomPointAround( const sPoint& P, float MinDist, PRNG& Generator ) 45 | { 46 | // start with non-uniform distribution 47 | float R1 = Generator.RandomFloat(); 48 | float R2 = Generator.RandomFloat(); 49 | 50 | // radius should be between MinDist and 2 * MinDist 51 | float Radius = MinDist * ( R1 + 1.0f ); 52 | 53 | // random angle 54 | float Angle = 2 * 3.141592653589f * R2; 55 | 56 | // the new point is generated around the point (x, y) 57 | float X = P.x + Radius * cos( Angle ); 58 | float Y = P.y + Radius * sin( Angle ); 59 | 60 | return sPoint( X, Y ); 61 | } 62 | 63 | // template 64 | std::vector GeneratePoissonPoints(size_t NumPoints, 65 | DefaultPRNG& Generator){ 66 | int NewPointsCount = 30; 67 | bool Circle = true; 68 | float MinDist = -1.0f; 69 | if ( MinDist < 0.0f ) 70 | { 71 | MinDist = sqrt( float(NumPoints) ) / float(NumPoints); 72 | } 73 | 74 | std::vector SamplePoints; 75 | std::vector ProcessList; 76 | 77 | // create the grid 78 | float CellSize = MinDist / sqrt( 2.0f ); 79 | 80 | int GridW = ( int )ceil( 1.0f / CellSize ); 81 | int GridH = ( int )ceil( 1.0f / CellSize ); 82 | 83 | sGrid Grid( GridW, GridH, CellSize ); 84 | 85 | sPoint FirstPoint; 86 | do { 87 | FirstPoint = sPoint( Generator.RandomFloat(), Generator.RandomFloat() ); 88 | } while (!(Circle ? FirstPoint.IsInCircle() : FirstPoint.IsInRectangle())); 89 | 90 | // update containers 91 | ProcessList.push_back( FirstPoint ); 92 | SamplePoints.push_back( FirstPoint ); 93 | Grid.Insert( FirstPoint ); 94 | 95 | // generate new points for each point in the queue 96 | while ( !ProcessList.empty() && SamplePoints.size() < NumPoints ) 97 | { 98 | #if POISSON_PROGRESS_INDICATOR 99 | // a progress indicator, kind of 100 | if ( SamplePoints.size() % 100 == 0 ) std::cout << "."; 101 | #endif // POISSON_PROGRESS_INDICATOR 102 | 103 | sPoint Point = PopRandom( ProcessList, Generator ); 104 | 105 | for ( int i = 0; i < NewPointsCount; i++ ) 106 | { 107 | sPoint NewPoint = GenerateRandomPointAround( Point, MinDist, Generator ); 108 | 109 | bool Fits = Circle ? NewPoint.IsInCircle() : NewPoint.IsInRectangle(); 110 | 111 | if ( Fits && !Grid.IsInNeighbourhood( NewPoint, MinDist, CellSize ) ) 112 | { 113 | ProcessList.push_back( NewPoint ); 114 | SamplePoints.push_back( NewPoint ); 115 | Grid.Insert( NewPoint ); 116 | continue; 117 | } 118 | } 119 | } 120 | 121 | #if POISSON_PROGRESS_INDICATOR 122 | std::cout << std::endl << std::endl; 123 | #endif // POISSON_PROGRESS_INDICATOR 124 | 125 | return SamplePoints; 126 | } 127 | 128 | 129 | } 130 | -------------------------------------------------------------------------------- /Sources/PoissonGenerator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Poisson Disk Points Generator 3 | // 4 | // \version 1.1.4 5 | // \date 19/10/2016 6 | // \author Sergey Kosarevsky, 2014-2016 7 | // \author support@linderdaum.com http://www.linderdaum.com http://blog.linderdaum.com 8 | // 9 | // Fast Poisson Disk Sampling in Arbitrary Dimensions 10 | // http://people.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf 11 | 12 | // Implementation based on http://devmag.org.za/2009/05/03/poisson-disk-sampling/ 13 | 14 | /* Versions history: 15 | * 1.1.4 Oct 19, 2016 POISSON_PROGRESS_INDICATOR can be defined outside of the header file, disabled by default 16 | * 1.1.3a Jun 9, 2016 Update constructor for DefaultPRNG 17 | * 1.1.3 Mar 10, 2016 Header-only library, no global mutable state 18 | * 1.1.2 Apr 9, 2015 Output a text file with XY coordinates 19 | * 1.1.1 May 23, 2014 Initialize PRNG seed, fixed uninitialized fields 20 | * 1.1 May 7, 2014 Support of density maps 21 | * 1.0 May 6, 2014 22 | */ 23 | 24 | #ifndef PoissonGenerator_hpp 25 | #define PoissonGenerator_hpp 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace PoissonGenerator 34 | { 35 | class DefaultPRNG 36 | { 37 | public: 38 | DefaultPRNG() 39 | : m_Gen( std::random_device()() ) 40 | , m_Dis( 0.0f, 1.0f ) 41 | { 42 | // prepare PRNG 43 | m_Gen.seed( (int)time( nullptr ) ); 44 | } 45 | 46 | explicit DefaultPRNG( uint32_t seed ) 47 | : m_Gen( seed ) 48 | , m_Dis( 0.0f, 1.0f ) 49 | { 50 | } 51 | 52 | float RandomFloat(); 53 | 54 | int RandomInt( int Max ); 55 | 56 | private: 57 | std::mt19937 m_Gen; 58 | std::uniform_real_distribution m_Dis; 59 | }; 60 | 61 | struct sPoint 62 | { 63 | sPoint() 64 | : x( 0 ) 65 | , y( 0 ) 66 | , m_Valid( false ) 67 | {} 68 | sPoint( float X, float Y ) 69 | : x( X ) 70 | , y( Y ) 71 | , m_Valid( true ) 72 | {} 73 | float x; 74 | float y; 75 | bool m_Valid; 76 | // 77 | bool IsInRectangle() const 78 | { 79 | return x >= 0 && y >= 0 && x <= 1 && y <= 1; 80 | } 81 | // 82 | bool IsInCircle() const 83 | { 84 | float fx = x - 0.5f; 85 | float fy = y - 0.5f; 86 | return ( fx*fx + fy*fy ) <= 0.25f; 87 | } 88 | }; 89 | 90 | struct sGridPoint 91 | { 92 | sGridPoint( int X, int Y ) 93 | : x( X ) 94 | , y( Y ) 95 | {} 96 | int x; 97 | int y; 98 | }; 99 | 100 | float GetDistance( const sPoint& P1, const sPoint& P2 ); 101 | sGridPoint ImageToGrid( const sPoint& P, float CellSize ); 102 | 103 | 104 | struct sGrid 105 | { 106 | sGrid( int W, int H, float CellSize ) 107 | : m_W( W ) 108 | , m_H( H ) 109 | , m_CellSize( CellSize ) 110 | { 111 | m_Grid.resize( m_H ); 112 | 113 | for ( auto i = m_Grid.begin(); i != m_Grid.end(); i++ ) { i->resize( m_W ); } 114 | } 115 | void Insert( const sPoint& P ) 116 | { 117 | sGridPoint G = ImageToGrid( P, m_CellSize ); 118 | m_Grid[ G.x ][ G.y ] = P; 119 | } 120 | bool IsInNeighbourhood( sPoint Point, float MinDist, float CellSize ) 121 | { 122 | sGridPoint G = ImageToGrid( Point, CellSize ); 123 | 124 | // number of adjucent cells to look for neighbour points 125 | const int D = 5; 126 | 127 | // scan the neighbourhood of the point in the grid 128 | for ( int i = G.x - D; i < G.x + D; i++ ) 129 | { 130 | for ( int j = G.y - D; j < G.y + D; j++ ) 131 | { 132 | if ( i >= 0 && i < m_W && j >= 0 && j < m_H ) 133 | { 134 | sPoint P = m_Grid[ i ][ j ]; 135 | 136 | if ( P.m_Valid && GetDistance( P, Point ) < MinDist ) { return true; } 137 | } 138 | } 139 | } 140 | 141 | 142 | return false; 143 | } 144 | 145 | private: 146 | int m_W; 147 | int m_H; 148 | float m_CellSize; 149 | 150 | std::vector< std::vector > m_Grid; 151 | }; 152 | 153 | template 154 | sPoint PopRandom( std::vector& Points, PRNG& Generator ); 155 | 156 | template 157 | sPoint GenerateRandomPointAround( const sPoint& P, float MinDist, PRNG& Generator ); 158 | 159 | /** 160 | Return a vector of generated points 161 | 162 | NewPointsCount - refer to bridson-siggraph07-poissondisk.pdf for details (the value 'k') 163 | Circle - 'true' to fill a circle, 'false' to fill a rectangle 164 | MinDist - minimal distance estimator, use negative value for default 165 | **/ 166 | // template 167 | std::vector GeneratePoissonPoints( 168 | size_t NumPoints, 169 | DefaultPRNG& Generator 170 | ); 171 | } 172 | 173 | 174 | #endif /* PoissonGenerator_hpp */ 175 | -------------------------------------------------------------------------------- /Sources/Porygon.h: -------------------------------------------------------------------------------- 1 | // 2 | // Porygon.h 3 | // Porygon 4 | // 5 | // Created by DevinShine on 2017/6/11. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | #if __has_include() 30 | FOUNDATION_EXPORT double PorygonVersionNumber; 31 | FOUNDATION_EXPORT const unsigned char PorygonVersionString[]; 32 | #import 33 | #import 34 | #else 35 | #import "DVSPorygon.h" 36 | #import "UIImage+DVSPixel" 37 | #endif 38 | -------------------------------------------------------------------------------- /Sources/UIImage+DVSPixel.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+DVSPixel.h 3 | // Porygon 4 | // 5 | // Created by DevinShine on 2017/6/12. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | struct PixelData { 30 | unsigned char *rawData; 31 | int count; 32 | int width; 33 | int height; 34 | int endIndex; 35 | }; 36 | 37 | @interface UIImage (DVSPixel) 38 | 39 | - (struct PixelData)pixelData; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Sources/UIImage+DVSPixel.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+DVSPixel.m 3 | // Porygon 4 | // 5 | // Created by DevinShine on 2017/6/12. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "UIImage+DVSPixel.h" 28 | 29 | @implementation UIImage (DVSPixel) 30 | - (struct PixelData)pixelData { 31 | CGImageRef imageRef = [self CGImage]; 32 | CGSize size = self.size; 33 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 34 | unsigned char *rawData = (unsigned char*) calloc(size.height * size.width * 4, sizeof(unsigned char)); 35 | NSUInteger bytesPerPixel = 4; 36 | NSUInteger bytesPerRow = bytesPerPixel * size.width; 37 | NSUInteger bitsPerComponent = 8; 38 | CGContextRef context = CGBitmapContextCreate(rawData, size.width, size.height, 39 | bitsPerComponent, bytesPerRow, colorSpace, 40 | kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); 41 | CGColorSpaceRelease(colorSpace); 42 | CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), imageRef); 43 | CGContextRelease(context); 44 | 45 | struct PixelData pd; 46 | pd.rawData = rawData; 47 | pd.count = size.height * size.width * 4; 48 | pd.height = size.height; 49 | pd.width = size.width; 50 | pd.endIndex = pd.count; 51 | return pd; 52 | } 53 | @end 54 | -------------------------------------------------------------------------------- /Sources/delaunay.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** delaunay.c : compute 2D delaunay triangulation in the plane. 3 | ** Copyright (C) 2005 Wael El Oraiby 4 | ** 5 | ** 6 | ** This program is free software: you can redistribute it and/or modify 7 | ** it under the terms of the GNU Affero General Public License as 8 | ** published by the Free Software Foundation, either version 3 of the 9 | ** License, or (at your option) any later version. 10 | ** 11 | ** This program is distributed in the hope that it will be useful, 12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ** GNU Affero General Public License for more details. 15 | ** 16 | ** You should have received a copy of the GNU Affero General Public License 17 | ** along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "delaunay.h" 27 | 28 | #define ON_RIGHT 1 29 | #define ON_SEG 0 30 | #define ON_LEFT -1 31 | 32 | #define OUTSIDE -1 33 | #define ON_CIRCLE 0 34 | #define INSIDE 1 35 | 36 | struct point2d_s; 37 | struct face_s; 38 | struct halfedge_s; 39 | struct delaunay_s; 40 | 41 | 42 | #define REAL_ZERO 0.0l 43 | #define REAL_ONE 1.0l 44 | #define REAL_TWO 2.0l 45 | #define REAL_FOUR 4.0l 46 | 47 | 48 | typedef struct point2d_s point2d_t; 49 | typedef struct face_s face_t; 50 | typedef struct halfedge_s halfedge_t; 51 | typedef struct delaunay_s delaunay_t; 52 | typedef struct working_set_s working_set_t; 53 | 54 | typedef long double lreal; 55 | typedef lreal mat3_t[3][3]; 56 | 57 | struct point2d_s { 58 | real x, y; /* point coordinates */ 59 | halfedge_t* he; /* point halfedge */ 60 | unsigned int idx; /* point index in input buffer */ 61 | }; 62 | 63 | struct face_s { 64 | halfedge_t* he; /* a pointing half edge */ 65 | unsigned int num_verts; /* number of vertices on this face */ 66 | }; 67 | 68 | struct halfedge_s { 69 | point2d_t* vertex; /* vertex */ 70 | halfedge_t* pair; /* pair */ 71 | halfedge_t* next; /* next */ 72 | halfedge_t* prev; /* next^-1 */ 73 | face_t* face; /* halfedge face */ 74 | }; 75 | 76 | struct delaunay_s { 77 | halfedge_t* rightmost_he; /* right most halfedge */ 78 | halfedge_t* leftmost_he; /* left most halfedge */ 79 | point2d_t* points; /* pointer to points */ 80 | face_t* faces; /* faces of delaunay */ 81 | unsigned int num_faces; /* face count */ 82 | unsigned int start_point; /* start point index */ 83 | unsigned int end_point; /* end point index */ 84 | }; 85 | 86 | struct working_set_s { 87 | halfedge_t* edges; /* all the edges (allocated in one shot) */ 88 | face_t* faces; /* all the faces (allocated in one shot) */ 89 | 90 | unsigned int max_edge; /* maximum edge count: 2 * 3 * n where n is point count */ 91 | unsigned int max_face; /* maximum face count: 2 * n where n is point count */ 92 | 93 | unsigned int num_edges; /* number of allocated edges */ 94 | unsigned int num_faces; /* number of allocated faces */ 95 | 96 | halfedge_t* free_edge; /* pointer to the first free edge */ 97 | face_t* free_face; /* pointer to the first free face */ 98 | }; 99 | 100 | /* 101 | * 3x3 matrix determinant 102 | */ 103 | static lreal det3(mat3_t m) 104 | { 105 | lreal res = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) 106 | - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) 107 | + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]); 108 | 109 | return res; 110 | } 111 | 112 | /* 113 | * allocate a halfedge 114 | */ 115 | static halfedge_t* halfedge_alloc() 116 | { 117 | halfedge_t* d; 118 | 119 | d = (halfedge_t*)malloc(sizeof(halfedge_t)); 120 | assert( NULL != d ); 121 | memset(d, 0, sizeof(halfedge_t)); 122 | 123 | return d; 124 | } 125 | 126 | /* 127 | * free a halfedge 128 | */ 129 | static void halfedge_free( halfedge_t* d ) 130 | { 131 | assert( d != NULL ); 132 | memset(d, 0, sizeof(halfedge_t)); 133 | free(d); 134 | } 135 | 136 | /* 137 | * free all delaunay halfedges 138 | */ 139 | void del_free_halfedges( delaunay_t *del ) 140 | { 141 | unsigned int i; 142 | halfedge_t *d, *sig; 143 | 144 | /* if there is nothing to do */ 145 | if( del->points == NULL ) 146 | return; 147 | 148 | for( i = 0; i <= (del->end_point - del->start_point); i++ ) 149 | { 150 | /* free all the halfedges around the point */ 151 | d = del->points[i].he; 152 | if( d != NULL ) 153 | { 154 | do { 155 | sig = d->next; 156 | halfedge_free( d ); 157 | d = sig; 158 | } while( d != del->points[i].he ); 159 | del->points[i].he = NULL; 160 | } 161 | } 162 | } 163 | 164 | /* 165 | * compare 2 points when sorting 166 | */ 167 | static int cmp_points( const void *_pt0, const void *_pt1 ) 168 | { 169 | point2d_t *pt0, *pt1; 170 | 171 | pt0 = (point2d_t*)(_pt0); 172 | pt1 = (point2d_t*)(_pt1); 173 | 174 | if( pt0->x < pt1->x ) 175 | return -1; 176 | else if( pt0->x > pt1->x ) 177 | return 1; 178 | else if( pt0->y < pt1->y ) 179 | return -1; 180 | else if( pt0->y > pt1->y ) 181 | return 1; 182 | assert(0 && "2 or more points share the same exact coordinate"); 183 | return 0; /* Should not be given! */ 184 | } 185 | 186 | /* 187 | * classify a point relative to a segment 188 | */ 189 | static int classify_point_seg( point2d_t *s, point2d_t *e, point2d_t *pt ) 190 | { 191 | lreal se_x, se_y, spt_x, spt_y; 192 | lreal res; 193 | 194 | se_x = e->x - s->x; 195 | se_y = e->y - s->y; 196 | 197 | spt_x = pt->x - s->x; 198 | spt_y = pt->y - s->y; 199 | 200 | res = (( se_x * spt_y ) - ( se_y * spt_x )); 201 | if( res < REAL_ZERO ) 202 | return ON_RIGHT; 203 | else if( res > REAL_ZERO ) 204 | return ON_LEFT; 205 | 206 | return ON_SEG; 207 | } 208 | 209 | /* 210 | * classify a point relative to a halfedge, -1 is left, 0 is on, 1 is right 211 | */ 212 | static int del_classify_point( halfedge_t *d, point2d_t *pt ) 213 | { 214 | point2d_t *s, *e; 215 | 216 | s = d->vertex; 217 | e = d->pair->vertex; 218 | 219 | return classify_point_seg(s, e, pt); 220 | } 221 | 222 | /* 223 | * test if a point is inside a circle given by 3 points, 1 if inside, 0 if outside 224 | */ 225 | static int in_circle( point2d_t *pt0, point2d_t *pt1, point2d_t *pt2, point2d_t *p ) 226 | { 227 | // reduce the computational complexity by substracting the last row of the matrix 228 | // ref: https://www.cs.cmu.edu/~quake/robust.html 229 | lreal p0p_x, p0p_y, p1p_x, p1p_y, p2p_x, p2p_y, p0p, p1p, p2p, res; 230 | mat3_t m; 231 | 232 | p0p_x = pt0->x - p->x; 233 | p0p_y = pt0->y - p->y; 234 | 235 | p1p_x = pt1->x - p->x; 236 | p1p_y = pt1->y - p->y; 237 | 238 | p2p_x = pt2->x - p->x; 239 | p2p_y = pt2->y - p->y; 240 | 241 | p0p = p0p_x * p0p_x + p0p_y * p0p_y; 242 | p1p = p1p_x * p1p_x + p1p_y * p1p_y; 243 | p2p = p2p_x * p2p_x + p2p_y * p2p_y; 244 | 245 | m[0][0] = p0p_x; 246 | m[0][1] = p0p_y; 247 | m[0][2] = p0p; 248 | 249 | m[1][0] = p1p_x; 250 | m[1][1] = p1p_y; 251 | m[1][2] = p1p; 252 | 253 | m[2][0] = p2p_x; 254 | m[2][1] = p2p_y; 255 | m[2][2] = p2p; 256 | 257 | res = -det3(m); 258 | 259 | if( res < REAL_ZERO ) 260 | return INSIDE; 261 | else if( res > REAL_ZERO ) 262 | return OUTSIDE; 263 | 264 | return ON_CIRCLE; 265 | } 266 | 267 | /* 268 | * initialize delaunay segment 269 | */ 270 | static int del_init_seg( delaunay_t *del, int start ) 271 | { 272 | halfedge_t *d0, *d1; 273 | point2d_t *pt0, *pt1; 274 | 275 | /* init delaunay */ 276 | del->start_point = start; 277 | del->end_point = start + 1; 278 | 279 | /* setup pt0 and pt1 */ 280 | pt0 = &(del->points[start]); 281 | pt1 = &(del->points[start + 1]); 282 | 283 | /* allocate the halfedges and setup them */ 284 | d0 = halfedge_alloc(); 285 | d1 = halfedge_alloc(); 286 | 287 | d0->vertex = pt0; 288 | d1->vertex = pt1; 289 | 290 | d0->next = d0->prev = d0; 291 | d1->next = d1->prev = d1; 292 | 293 | d0->pair = d1; 294 | d1->pair = d0; 295 | 296 | pt0->he = d0; 297 | pt1->he = d1; 298 | 299 | del->rightmost_he = d1; 300 | del->leftmost_he = d0; 301 | 302 | 303 | return 0; 304 | } 305 | 306 | /* 307 | * initialize delaunay triangle 308 | */ 309 | static int del_init_tri( delaunay_t *del, int start ) 310 | { 311 | halfedge_t *d0, *d1, *d2, *d3, *d4, *d5; 312 | point2d_t *pt0, *pt1, *pt2; 313 | 314 | /* initiate delaunay */ 315 | del->start_point = start; 316 | del->end_point = start + 2; 317 | 318 | /* setup the points */ 319 | pt0 = &(del->points[start]); 320 | pt1 = &(del->points[start + 1]); 321 | pt2 = &(del->points[start + 2]); 322 | 323 | /* allocate the 6 halfedges */ 324 | d0 = halfedge_alloc(); 325 | d1 = halfedge_alloc(); 326 | d2 = halfedge_alloc(); 327 | d3 = halfedge_alloc(); 328 | d4 = halfedge_alloc(); 329 | d5 = halfedge_alloc(); 330 | 331 | if( classify_point_seg(pt0, pt2, pt1) == ON_LEFT ) /* first case */ 332 | { 333 | /* set halfedges points */ 334 | d0->vertex = pt0; 335 | d1->vertex = pt2; 336 | d2->vertex = pt1; 337 | 338 | d3->vertex = pt2; 339 | d4->vertex = pt1; 340 | d5->vertex = pt0; 341 | 342 | /* set points halfedges */ 343 | pt0->he = d0; 344 | pt1->he = d2; 345 | pt2->he = d1; 346 | 347 | /* next and next -1 setup */ 348 | d0->next = d5; 349 | d0->prev = d5; 350 | 351 | d1->next = d3; 352 | d1->prev = d3; 353 | 354 | d2->next = d4; 355 | d2->prev = d4; 356 | 357 | d3->next = d1; 358 | d3->prev = d1; 359 | 360 | d4->next = d2; 361 | d4->prev = d2; 362 | 363 | d5->next = d0; 364 | d5->prev = d0; 365 | 366 | /* set halfedges pair */ 367 | d0->pair = d3; 368 | d3->pair = d0; 369 | 370 | d1->pair = d4; 371 | d4->pair = d1; 372 | 373 | d2->pair = d5; 374 | d5->pair = d2; 375 | 376 | del->rightmost_he = d1; 377 | del->leftmost_he = d0; 378 | 379 | } else /* 2nd case */ 380 | { 381 | /* set halfedges points */ 382 | d0->vertex = pt0; 383 | d1->vertex = pt1; 384 | d2->vertex = pt2; 385 | 386 | d3->vertex = pt1; 387 | d4->vertex = pt2; 388 | d5->vertex = pt0; 389 | 390 | /* set points halfedges */ 391 | pt0->he = d0; 392 | pt1->he = d1; 393 | pt2->he = d2; 394 | 395 | /* next and next -1 setup */ 396 | d0->next = d5; 397 | d0->prev = d5; 398 | 399 | d1->next = d3; 400 | d1->prev = d3; 401 | 402 | d2->next = d4; 403 | d2->prev = d4; 404 | 405 | d3->next = d1; 406 | d3->prev = d1; 407 | 408 | d4->next = d2; 409 | d4->prev = d2; 410 | 411 | d5->next = d0; 412 | d5->prev = d0; 413 | 414 | /* set halfedges pair */ 415 | d0->pair = d3; 416 | d3->pair = d0; 417 | 418 | d1->pair = d4; 419 | d4->pair = d1; 420 | 421 | d2->pair = d5; 422 | d5->pair = d2; 423 | 424 | del->rightmost_he = d2; 425 | del->leftmost_he = d0; 426 | } 427 | 428 | return 0; 429 | } 430 | 431 | /* 432 | * remove an edge given a halfedge 433 | */ 434 | static void del_remove_edge( halfedge_t *d ) 435 | { 436 | halfedge_t *next, *prev, *pair, *orig_pair; 437 | 438 | orig_pair = d->pair; 439 | 440 | next = d->next; 441 | prev = d->prev; 442 | pair = d->pair; 443 | 444 | assert(next != NULL); 445 | assert(prev != NULL); 446 | 447 | next->prev = prev; 448 | prev->next = next; 449 | 450 | 451 | /* check to see if we have already removed pair */ 452 | if( pair ) 453 | pair->pair = NULL; 454 | 455 | /* check to see if the vertex points to this halfedge */ 456 | if( d->vertex->he == d ) 457 | d->vertex->he = next; 458 | 459 | d->vertex = NULL; 460 | d->next = NULL; 461 | d->prev = NULL; 462 | d->pair = NULL; 463 | 464 | next = orig_pair->next; 465 | prev = orig_pair->prev; 466 | pair = orig_pair->pair; 467 | 468 | assert(next != NULL); 469 | assert(prev != NULL); 470 | 471 | next->prev = prev; 472 | prev->next = next; 473 | 474 | 475 | /* check to see if we have already removed pair */ 476 | if( pair ) 477 | pair->pair = NULL; 478 | 479 | /* check to see if the vertex points to this halfedge */ 480 | if( orig_pair->vertex->he == orig_pair ) 481 | orig_pair->vertex->he = next; 482 | 483 | orig_pair->vertex = NULL; 484 | orig_pair->next = NULL; 485 | orig_pair->prev = NULL; 486 | orig_pair->pair = NULL; 487 | 488 | 489 | /* finally free the halfedges */ 490 | halfedge_free(d); 491 | halfedge_free(orig_pair); 492 | } 493 | 494 | /* 495 | * pass through all the halfedges on the left side and validate them 496 | */ 497 | static halfedge_t* del_valid_left( halfedge_t* b ) 498 | { 499 | point2d_t *g, *d, *u, *v; 500 | halfedge_t *c, *du, *dg; 501 | 502 | g = b->vertex; /* base halfedge point */ 503 | dg = b; 504 | 505 | d = b->pair->vertex; /* pair(halfedge) point */ 506 | b = b->next; 507 | 508 | u = b->pair->vertex; /* next(pair(halfedge)) point */ 509 | du = b->pair; 510 | 511 | v = b->next->pair->vertex; /* pair(next(next(halfedge)) point */ 512 | 513 | if( classify_point_seg(g, d, u) == ON_LEFT ) 514 | { 515 | /* 3 points aren't colinear */ 516 | /* as long as the 4 points belong to the same circle, do the cleaning */ 517 | assert( v != u && "1: floating point precision error"); 518 | while( v != d && v != g && in_circle(g, d, u, v) == INSIDE ) 519 | { 520 | c = b->next; 521 | du = b->next->pair; 522 | del_remove_edge(b); 523 | b = c; 524 | u = du->vertex; 525 | v = b->next->pair->vertex; 526 | } 527 | 528 | assert( v != u && "2: floating point precision error"); 529 | if( v != d && v != g && in_circle(g, d, u, v) == ON_CIRCLE ) 530 | { 531 | du = du->prev; 532 | del_remove_edge(b); 533 | } 534 | } else /* treat the case where the 3 points are colinear */ 535 | du = dg; 536 | 537 | assert(du->pair); 538 | return du; 539 | } 540 | 541 | /* 542 | * pass through all the halfedges on the right side and validate them 543 | */ 544 | static halfedge_t* del_valid_right( halfedge_t *b ) 545 | { 546 | point2d_t *rv, *lv, *u, *v; 547 | halfedge_t *c, *dd, *du; 548 | 549 | b = b->pair; 550 | rv = b->vertex; 551 | dd = b; 552 | lv = b->pair->vertex; 553 | b = b->prev; 554 | u = b->pair->vertex; 555 | du = b->pair; 556 | 557 | v = b->prev->pair->vertex; 558 | 559 | if( classify_point_seg(lv, rv, u) == ON_LEFT ) 560 | { 561 | assert( v != u && "1: floating point precision error"); 562 | while( v != lv && v != rv && in_circle(lv, rv, u, v) == INSIDE ) 563 | { 564 | c = b->prev; 565 | du = c->pair; 566 | del_remove_edge(b); 567 | b = c; 568 | u = du->vertex; 569 | v = b->prev->pair->vertex; 570 | } 571 | 572 | assert( v != u && "1: floating point precision error"); 573 | if( v != lv && v != rv && in_circle(lv, rv, u, v) == ON_CIRCLE ) 574 | { 575 | du = du->next; 576 | del_remove_edge(b); 577 | } 578 | } else 579 | du = dd; 580 | 581 | assert(du->pair); 582 | return du; 583 | } 584 | 585 | 586 | /* 587 | * validate a link 588 | */ 589 | static halfedge_t* del_valid_link( halfedge_t *b ) 590 | { 591 | point2d_t *g, *g_p, *d, *d_p; 592 | halfedge_t *gd, *dd, *new_gd, *new_dd; 593 | int a; 594 | 595 | g = b->vertex; 596 | gd = del_valid_left(b); 597 | g_p = gd->vertex; 598 | 599 | assert(b->pair); 600 | d = b->pair->vertex; 601 | dd = del_valid_right(b); 602 | d_p = dd->vertex; 603 | assert(b->pair); 604 | 605 | if( g != g_p && d != d_p ) { 606 | a = in_circle(g, d, g_p, d_p); 607 | 608 | if( a != ON_CIRCLE ) { 609 | if( a == INSIDE ) { 610 | g_p = g; 611 | gd = b; 612 | } else { 613 | d_p = d; 614 | dd = b->pair; 615 | } 616 | } 617 | } 618 | 619 | /* create the 2 halfedges */ 620 | new_gd = halfedge_alloc(); 621 | new_dd = halfedge_alloc(); 622 | 623 | /* setup new_gd and new_dd */ 624 | 625 | new_gd->vertex = gd->vertex; 626 | new_gd->pair = new_dd; 627 | new_gd->prev = gd; 628 | new_gd->next = gd->next; 629 | gd->next->prev = new_gd; 630 | gd->next = new_gd; 631 | 632 | new_dd->vertex = dd->vertex; 633 | new_dd->pair = new_gd; 634 | new_dd->prev = dd->prev; 635 | dd->prev->next = new_dd; 636 | new_dd->next = dd; 637 | dd->prev = new_dd; 638 | 639 | return new_gd; 640 | } 641 | 642 | /* 643 | * find the lower tangent between the two delaunay, going from left to right (returns the left half edge) 644 | */ 645 | static halfedge_t* del_get_lower_tangent( delaunay_t *left, delaunay_t *right ) 646 | { 647 | point2d_t *pl, *pr; 648 | halfedge_t *right_d, *left_d, *new_ld, *new_rd; 649 | int sl, sr; 650 | 651 | left_d = left->rightmost_he; 652 | right_d = right->leftmost_he; 653 | 654 | do { 655 | pl = left_d->prev->pair->vertex; 656 | pr = right_d->pair->vertex; 657 | 658 | if( (sl = classify_point_seg(left_d->vertex, right_d->vertex, pl)) == ON_RIGHT ) { 659 | left_d = left_d->prev->pair; 660 | } 661 | 662 | if( (sr = classify_point_seg(left_d->vertex, right_d->vertex, pr)) == ON_RIGHT ) { 663 | right_d = right_d->pair->next; 664 | } 665 | 666 | } while( sl == ON_RIGHT || sr == ON_RIGHT ); 667 | 668 | /* create the 2 halfedges */ 669 | new_ld = halfedge_alloc(); 670 | new_rd = halfedge_alloc(); 671 | 672 | /* setup new_gd and new_dd */ 673 | new_ld->vertex = left_d->vertex; 674 | new_ld->pair = new_rd; 675 | new_ld->prev = left_d->prev; 676 | left_d->prev->next = new_ld; 677 | new_ld->next = left_d; 678 | left_d->prev = new_ld; 679 | 680 | new_rd->vertex = right_d->vertex; 681 | new_rd->pair = new_ld; 682 | new_rd->prev = right_d->prev; 683 | right_d->prev->next = new_rd; 684 | new_rd->next = right_d; 685 | right_d->prev = new_rd; 686 | 687 | return new_ld; 688 | } 689 | 690 | /* 691 | * link the 2 delaunay together 692 | */ 693 | static void del_link( delaunay_t *result, delaunay_t *left, delaunay_t *right ) 694 | { 695 | point2d_t *u, *v, *ml, *mr; 696 | halfedge_t *base; 697 | 698 | assert( left->points == right->points ); 699 | 700 | /* save the most right point and the most left point */ 701 | ml = left->leftmost_he->vertex; 702 | mr = right->rightmost_he->vertex; 703 | 704 | base = del_get_lower_tangent(left, right); 705 | 706 | u = base->next->pair->vertex; 707 | v = base->pair->prev->pair->vertex; 708 | 709 | while( del_classify_point(base, u) == ON_LEFT || 710 | del_classify_point(base, v) == ON_LEFT ) 711 | { 712 | base = del_valid_link(base); 713 | u = base->next->pair->vertex; 714 | v = base->pair->prev->pair->vertex; 715 | } 716 | 717 | right->rightmost_he = mr->he; 718 | left->leftmost_he = ml->he; 719 | 720 | /* TODO: this part is not needed, and can be optimized */ 721 | while( del_classify_point( right->rightmost_he, right->rightmost_he->prev->pair->vertex ) == ON_RIGHT ) 722 | right->rightmost_he = right->rightmost_he->prev; 723 | 724 | while( del_classify_point( left->leftmost_he, left->leftmost_he->prev->pair->vertex ) == ON_RIGHT ) 725 | left->leftmost_he = left->leftmost_he->prev; 726 | 727 | result->leftmost_he = left->leftmost_he; 728 | result->rightmost_he = right->rightmost_he; 729 | result->points = left->points; 730 | result->start_point = left->start_point; 731 | result->end_point = right->end_point; 732 | } 733 | 734 | /* 735 | * divide and conquer delaunay 736 | */ 737 | void del_divide_and_conquer( delaunay_t *del, int start, int end ) 738 | { 739 | delaunay_t left, right; 740 | int i, n; 741 | 742 | n = (end - start + 1); 743 | 744 | if( n > 3 ) { 745 | i = (n / 2) + (n & 1); 746 | left.points = del->points; 747 | right.points = del->points; 748 | del_divide_and_conquer( &left, start, start + i - 1 ); 749 | del_divide_and_conquer( &right, start + i, end ); 750 | del_link( del, &left, &right ); 751 | } else { 752 | if( n == 3 ) { 753 | del_init_tri( del, start ); 754 | } else { 755 | if( n == 2 ) { 756 | del_init_seg( del, start ); 757 | } 758 | } 759 | } 760 | } 761 | 762 | static void build_halfedge_face( delaunay_t *del, halfedge_t *d ) 763 | { 764 | halfedge_t *curr; 765 | 766 | /* test if the halfedge has already a pointing face */ 767 | if( d->face != NULL ) 768 | return; 769 | 770 | /* TODO: optimize this */ 771 | del->faces = (face_t*)realloc(del->faces, (del->num_faces + 1) * sizeof(face_t)); 772 | assert( NULL != del->faces ); 773 | 774 | face_t *f = &(del->faces[del->num_faces]); 775 | curr = d; 776 | f->he = d; 777 | f->num_verts = 0; 778 | do { 779 | curr->face = f; 780 | (f->num_verts)++; 781 | curr = curr->pair->prev; 782 | } while( curr != d ); 783 | 784 | (del->num_faces)++; 785 | } 786 | 787 | /* 788 | * build the faces for all the halfedge 789 | */ 790 | void del_build_faces( delaunay_t *del ) 791 | { 792 | unsigned int i; 793 | halfedge_t *curr; 794 | 795 | del->num_faces = 0; 796 | del->faces = NULL; 797 | 798 | /* build external face first */ 799 | build_halfedge_face(del, del->rightmost_he->pair); 800 | 801 | for( i = del->start_point; i <= del->end_point; i++ ) 802 | { 803 | curr = del->points[i].he; 804 | 805 | do { 806 | build_halfedge_face( del, curr ); 807 | curr = curr->next; 808 | } while( curr != del->points[i].he ); 809 | } 810 | } 811 | 812 | /* 813 | */ 814 | delaunay2d_t* delaunay2d_from(del_point2d_t *points, unsigned int num_points) { 815 | delaunay2d_t* res = NULL; 816 | delaunay_t del; 817 | unsigned int i, j, fbuff_size = 0; 818 | unsigned int* faces = NULL; 819 | 820 | /* allocate the points */ 821 | del.points = (point2d_t*)malloc(num_points * sizeof(point2d_t)); 822 | assert( NULL != del.points ); 823 | memset(del.points, 0, num_points * sizeof(point2d_t)); 824 | 825 | /* copy the points */ 826 | for( i = 0; i < num_points; i++ ) 827 | { 828 | del.points[i].idx = i; 829 | del.points[i].x = points[i].x; 830 | del.points[i].y = points[i].y; 831 | } 832 | 833 | qsort(del.points, num_points, sizeof(point2d_t), cmp_points); 834 | 835 | if( num_points >= 3 ) { 836 | del_divide_and_conquer( &del, 0, num_points - 1 ); 837 | 838 | del_build_faces( &del ); 839 | 840 | fbuff_size = 0; 841 | for( i = 0; i < del.num_faces; i++ ) 842 | fbuff_size += del.faces[i].num_verts + 1; 843 | 844 | faces = (unsigned int*)malloc(sizeof(unsigned int) * fbuff_size); 845 | assert( NULL != faces ); 846 | 847 | j = 0; 848 | for( i = 0; i < del.num_faces; i++ ) 849 | { 850 | halfedge_t *curr; 851 | 852 | faces[j] = del.faces[i].num_verts; 853 | j++; 854 | 855 | curr = del.faces[i].he; 856 | do { 857 | faces[j] = curr->vertex->idx; 858 | j++; 859 | curr = curr->pair->prev; 860 | } while( curr != del.faces[i].he ); 861 | } 862 | 863 | del_free_halfedges( &del ); 864 | 865 | free(del.faces); 866 | free(del.points); 867 | } 868 | 869 | res = (delaunay2d_t*)malloc(sizeof(delaunay2d_t)); 870 | assert( NULL != res ); 871 | res->num_points = num_points; 872 | res->points = (del_point2d_t*)malloc(sizeof(del_point2d_t) * num_points); 873 | assert( NULL != res->points ); 874 | memcpy(res->points, points, sizeof(del_point2d_t) * num_points); 875 | res->num_faces = del.num_faces; 876 | res->faces = faces; 877 | 878 | return res; 879 | } 880 | 881 | void delaunay2d_release(delaunay2d_t *del) { 882 | free(del->faces); 883 | free(del->points); 884 | free(del); 885 | } 886 | 887 | 888 | tri_delaunay2d_t* tri_delaunay2d_from(delaunay2d_t* del) { 889 | unsigned int v_offset = del->faces[0] + 1; /* ignore external face */ 890 | unsigned int dst_offset = 0; 891 | unsigned int i; 892 | 893 | tri_delaunay2d_t* tdel = (tri_delaunay2d_t*)malloc(sizeof(tri_delaunay2d_t)); 894 | assert( NULL != tdel ); 895 | tdel->num_triangles = 0; 896 | 897 | /* count the number of triangles */ 898 | if( 1 == del->num_faces ) { /* degenerate case: only external face exists */ 899 | unsigned int nv = del->faces[0]; 900 | tdel->num_triangles += nv - 2; 901 | } else { 902 | for( i = 1; i < del->num_faces; ++i ) { 903 | unsigned int nv = del->faces[v_offset]; 904 | tdel->num_triangles += nv - 2; 905 | v_offset += nv + 1; 906 | } 907 | } 908 | 909 | /* copy points */ 910 | tdel->num_points = del->num_points; 911 | tdel->points = (del_point2d_t*)malloc(sizeof(del_point2d_t) * del->num_points); 912 | assert( NULL != tdel->points ); 913 | memcpy(tdel->points, del->points, sizeof(del_point2d_t) * del->num_points); 914 | 915 | /* build the triangles */ 916 | tdel->tris = (unsigned int*)malloc(sizeof(unsigned int) * 3 * tdel->num_triangles); 917 | assert( NULL != tdel->tris ); 918 | 919 | v_offset = del->faces[0] + 1; /* ignore external face */ 920 | 921 | if( 1 == del->num_faces ) { 922 | /* handle the degenerated case where only the external face exists */ 923 | unsigned int nv = del->faces[0]; 924 | unsigned int j = 0; 925 | v_offset = 1; 926 | for( ; j < nv - 2; ++j ) { 927 | tdel->tris[dst_offset] = del->faces[v_offset + j]; 928 | tdel->tris[dst_offset + 1] = del->faces[(v_offset + j + 1) % nv]; 929 | tdel->tris[dst_offset + 2] = del->faces[v_offset + j]; 930 | dst_offset += 3; 931 | } 932 | } else { 933 | for( i = 1; i < del->num_faces; ++i ) { 934 | unsigned int nv = del->faces[v_offset]; 935 | unsigned int j = 0; 936 | unsigned int first = del->faces[v_offset + 1]; 937 | 938 | 939 | for( ; j < nv - 2; ++j ) { 940 | tdel->tris[dst_offset] = first; 941 | tdel->tris[dst_offset + 1] = del->faces[v_offset + j + 2]; 942 | tdel->tris[dst_offset + 2] = del->faces[v_offset + j + 3]; 943 | dst_offset += 3; 944 | } 945 | 946 | v_offset += nv + 1; 947 | } 948 | } 949 | 950 | return tdel; 951 | } 952 | 953 | 954 | void tri_delaunay2d_release(tri_delaunay2d_t* tdel) { 955 | free(tdel->tris); 956 | free(tdel->points); 957 | free(tdel); 958 | } 959 | -------------------------------------------------------------------------------- /Sources/delaunay.h: -------------------------------------------------------------------------------- 1 | #ifndef DELAUNAY_H 2 | #define DELAUNAY_H 3 | 4 | /* 5 | ** delaunay.c : compute 2D delaunay triangulation in the plane. 6 | ** Copyright (C) 2005 Wael El Oraiby 7 | ** 8 | ** 9 | ** This program is free software: you can redistribute it and/or modify 10 | ** it under the terms of the GNU Affero General Public License as 11 | ** published by the Free Software Foundation, either version 3 of the 12 | ** License, or (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU Affero General Public License for more details. 18 | ** 19 | ** You should have received a copy of the GNU Affero General Public License 20 | ** along with this program. If not, see . 21 | */ 22 | 23 | #define DEL_CIRCLE 24 | 25 | 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | typedef double real; 32 | 33 | typedef struct { 34 | real x, y; 35 | } del_point2d_t; 36 | 37 | typedef struct { 38 | /** input points count */ 39 | unsigned int num_points; 40 | 41 | /** the input points */ 42 | del_point2d_t* points; 43 | 44 | /** number of returned faces */ 45 | unsigned int num_faces; 46 | 47 | /** the faces are given as a sequence: num verts, verts indices, num verts, verts indices... 48 | * the first face is the external face */ 49 | unsigned int* faces; 50 | } delaunay2d_t; 51 | 52 | /* 53 | * build the 2D Delaunay triangulation given a set of points of at least 3 points 54 | * 55 | * @points: point set given as a sequence of tuple x0, y0, x1, y1, .... 56 | * @num_points: number of given point 57 | * @preds: the incircle predicate 58 | * @faces: the triangles given as a sequence: num verts, verts indices, num verts, verts indices. 59 | * Note that the first face is the external face 60 | * @return: the created topology 61 | */ 62 | delaunay2d_t* delaunay2d_from(del_point2d_t *points, unsigned int num_points); 63 | 64 | /* 65 | * release a delaunay2d object 66 | */ 67 | void delaunay2d_release(delaunay2d_t* del); 68 | 69 | 70 | typedef struct { 71 | /** input points count */ 72 | unsigned int num_points; 73 | 74 | /** input points */ 75 | del_point2d_t* points; 76 | 77 | /** number of triangles */ 78 | unsigned int num_triangles; 79 | 80 | /** the triangles indices v0,v1,v2, v0,v1,v2 .... */ 81 | unsigned int* tris; 82 | } tri_delaunay2d_t; 83 | 84 | /** 85 | * build a tri_delaunay2d_t out of a delaunay2d_t object 86 | */ 87 | tri_delaunay2d_t* tri_delaunay2d_from(delaunay2d_t* del); 88 | 89 | /** 90 | * release a tri_delaunay2d_t object 91 | */ 92 | void tri_delaunay2d_release(tri_delaunay2d_t* tdel); 93 | 94 | #ifdef __cplusplus 95 | } 96 | #endif 97 | 98 | #endif // DELAUNAY_H 99 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // Tests.m 3 | // Tests 4 | // 5 | // Created by DevinShine on 2017/6/11. 6 | // 7 | // Copyright (c) 2017 DevinShine 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import "DVSPorygon.h" 29 | 30 | @interface Tests : XCTestCase 31 | @property (nonatomic) DVSPorygon *porygon; 32 | @end 33 | 34 | @implementation Tests 35 | 36 | - (void)setUp { 37 | [super setUp]; 38 | self.porygon = [[DVSPorygon alloc] init]; 39 | } 40 | 41 | - (void)tearDown { 42 | self.porygon = nil; 43 | [super tearDown]; 44 | } 45 | 46 | - (void)testVertexCountSetter { 47 | self.porygon.vertexCount = -1; 48 | XCTAssertEqual(self.porygon.vertexCount, DVS_MIN_VERTEX_COUNT); 49 | self.porygon.vertexCount = 99; 50 | XCTAssertEqual(self.porygon.vertexCount, DVS_MIN_VERTEX_COUNT); 51 | self.porygon.vertexCount = 5001; 52 | XCTAssertEqual(self.porygon.vertexCount, DVS_MAX_VERTEX_COUNT); 53 | self.porygon.vertexCount = 100; 54 | XCTAssertEqual(self.porygon.vertexCount, 100); 55 | self.porygon.vertexCount = 5000; 56 | XCTAssertEqual(self.porygon.vertexCount, 5000); 57 | 58 | } 59 | 60 | - (void)testLowPoly { 61 | [self measureBlock:^{ 62 | UIImage *image = [UIImage imageNamed:@"camera.jpg" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil]; 63 | XCTAssertNotNil(image); 64 | UIImage *lowPolyImage = [self.porygon lowPolyWithImage:image]; 65 | XCTAssertNotNil(lowPolyImage); 66 | }]; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /Tests/camera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinShine/Porygon/8b9b0a4a6ffee3dfbdba1f591afa64882f4e1c3a/Tests/camera.jpg --------------------------------------------------------------------------------