├── .gitignore ├── CMUnistrokeGestureRecognizer ├── CMUnistrokeGestureRecognizer-Prefix.pch ├── CMUnistrokeGestureRecognizer.h ├── CMUnistrokeGestureRecognizer.m ├── CMUnistrokeGestureResult.h ├── CMUnistrokeGestureResult.m ├── CMUnistrokeRecognizer │ ├── CMUnistrokeRecognizer.c │ ├── CMUnistrokeRecognizer.h │ ├── CMUnistrokeRecognizerPrivate.h │ ├── CMUnistrokeRecognizerTypes.c │ └── CMUnistrokeRecognizerTypes.h └── GestureKeyboard-Bridging-Header.h ├── GestureKeyboard.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── GestureKeyboard ├── AppDelegate.swift ├── Base.lproj │ └── Main.storyboard ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── ViewController.swift └── graffiti_strokes.jpeg ├── GestureKeyboardExtension ├── Info.plist └── KeyboardViewController.swift ├── GestureKeyboardTests ├── GestureKeyboardTests.swift └── Info.plist └── graffiti.plist /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeGestureRecognizer-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CMUnistrokeGestureRecognizer' target in the 'CMUnistrokeGestureRecognizer' project 3 | // 4 | // Copyright (c) 2012 Chris Miles. All rights reserved. 5 | // 6 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | 27 | #ifdef __OBJC__ 28 | #import 29 | #import 30 | 31 | #ifdef DEBUG 32 | #define CMUGRLog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__]) 33 | #define CMUGRALog(...) [[NSAssertionHandler currentHandler] handleFailureInFunction:[NSString stringWithCString:__PRETTY_FUNCTION__ encoding:NSUTF8StringEncoding] file:[NSString stringWithCString:__FILE__ encoding:NSUTF8StringEncoding] lineNumber:__LINE__ description:__VA_ARGS__] 34 | #else 35 | #define CMUGRLog(...) do { } while (0) 36 | #ifndef NS_BLOCK_ASSERTIONS 37 | #define NS_BLOCK_ASSERTIONS 38 | #endif 39 | #define CMUGRALog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__]) 40 | #endif 41 | 42 | #define CMUGRAssert(condition, ...) do { if (!(condition)) { ALog(__VA_ARGS__); }} while(0) 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeGestureRecognizer.h: -------------------------------------------------------------------------------- 1 | // 2 | // CMUnistrokeGestureRecognizer.h 3 | // CMUnistrokeGestureRecognizer 4 | // 5 | // Created by Chris Miles on 23/09/12. 6 | // Copyright (c) 2012 Chris Miles. All rights reserved. 7 | // 8 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | // 28 | 29 | #import 30 | #import 31 | #import "CMUnistrokeGestureResult.h" 32 | 33 | 34 | @protocol CMUnistrokeGestureRecognizerDelegate; 35 | 36 | 37 | #pragma mark - CMUnistrokeGestureRecognizer 38 | 39 | @interface CMUnistrokeGestureRecognizer : UIGestureRecognizer 40 | 41 | @property (nonatomic, weak) id unistrokeDelegate; 42 | 43 | /** Minimum score required to successfully register a gesture as recognized. 44 | i.e. recognition sensitivity. 45 | Expects a value between 0.0 and 1.0. 46 | */ 47 | @property (nonatomic, assign) float minimumScoreThreshold; 48 | 49 | /** Enables the Protractor method for calculating difference between gestures. 50 | The Protractor method can be faster, but results may vary. 51 | Defaults to not enabled. 52 | */ 53 | @property (nonatomic, assign) BOOL protactorMethodEnabled; 54 | 55 | /** Determines whether gesture rotations are normalised. 56 | When enabled, gestures will be normalised to the same angle before 57 | recognition is attempted. 58 | Defaults to enabled. 59 | */ 60 | @property (nonatomic, assign) BOOL rotationNormalisationEnabled; // default: YES 61 | 62 | @property (nonatomic, strong, readonly) CMUnistrokeGestureResult *result; 63 | @property (nonatomic, strong, readonly) UIBezierPath *strokePath; 64 | 65 | - (void)registerUnistrokeWithName:(NSString *)name bezierPath:(UIBezierPath *)path; 66 | - (void)registerUnistrokeWithName:(NSString *)name bezierPath:(UIBezierPath *)bezierPath bidirectional:(BOOL)bidirectional; 67 | - (void)clearAllUnistrokes; 68 | 69 | @end 70 | 71 | 72 | #pragma mark - Protocols 73 | 74 | @protocol CMUnistrokeGestureRecognizerDelegate 75 | 76 | @optional 77 | - (void)unistrokeGestureRecognizer:(CMUnistrokeGestureRecognizer *)unistrokeGestureRecognizer isEvaluatingStrokePath:(UIBezierPath *)strokePath; 78 | - (void)unistrokeGestureRecognizerDidFailToRecognize:(CMUnistrokeGestureRecognizer *)unistrokeGestureRecognizer; 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeGestureRecognizer.m: -------------------------------------------------------------------------------- 1 | // 2 | // CMUnistrokeGestureRecognizer.m 3 | // CMUnistrokeGestureRecognizer 4 | // 5 | // Created by Chris Miles on 23/09/12. 6 | // Copyright (c) 2012 Chris Miles. All rights reserved. 7 | // 8 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | // 28 | 29 | #import "CMUnistrokeGestureRecognizer.h" 30 | #import "CMUnistrokeRecognizer.h" 31 | #import 32 | 33 | static void 34 | CMURCGPathApplierFunc(void *info, const CGPathElement *element); 35 | 36 | 37 | #pragma mark - CMUnistrokeGestureRecognizer class extension 38 | 39 | @interface CMUnistrokeGestureRecognizer () { 40 | CMUROptionsRef _options; 41 | CMURTemplatesRef _unistrokeTemplates; 42 | } 43 | @property (nonatomic, strong, readwrite) CMUnistrokeGestureResult *result; 44 | @property (nonatomic, strong, readwrite) UIBezierPath *strokePath; 45 | @property (nonatomic, strong) UITouch *trackedTouch; 46 | @end 47 | 48 | 49 | #pragma mark - CMUnistrokeGestureRecognizer implementation 50 | 51 | @implementation CMUnistrokeGestureRecognizer 52 | 53 | @dynamic protactorMethodEnabled; 54 | @dynamic rotationNormalisationEnabled; 55 | 56 | 57 | - (id)initWithTarget:(id)target action:(SEL)action 58 | { 59 | self = [super initWithTarget:target action:action]; 60 | if (self) { 61 | _unistrokeTemplates = CMURTemplatesNew(); 62 | 63 | _options = CMUROptionsNew(); 64 | _options->useProtractor = false; 65 | _options->rotationNormalisationDisabled = false; 66 | } 67 | return self; 68 | } 69 | 70 | - (void)dealloc 71 | { 72 | if (_options) { 73 | CMUROptionsDelete(_options); _options = NULL; 74 | } 75 | if (_unistrokeTemplates) { 76 | CMURTemplatesDelete(_unistrokeTemplates); _unistrokeTemplates = NULL; 77 | } 78 | } 79 | 80 | 81 | #pragma mark - Touches 82 | 83 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 84 | { 85 | [super touchesBegan:touches withEvent:event]; 86 | 87 | self.result = nil; 88 | 89 | self.trackedTouch = [touches anyObject]; 90 | self.strokePath = [[UIBezierPath alloc] init]; 91 | 92 | self.state = UIGestureRecognizerStatePossible; 93 | 94 | CGPoint location = [self.trackedTouch locationInView:self.view]; 95 | [self.strokePath moveToPoint:location]; 96 | } 97 | 98 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 99 | { 100 | [super touchesMoved:touches withEvent:event]; 101 | 102 | if ([touches containsObject:self.trackedTouch]) { 103 | CGPoint location = [self.trackedTouch locationInView:self.view]; 104 | [self.strokePath addLineToPoint:location]; 105 | 106 | id unistrokeDelegate = self.unistrokeDelegate; 107 | if ([unistrokeDelegate respondsToSelector:@selector(unistrokeGestureRecognizer:isEvaluatingStrokePath:)]) { 108 | [unistrokeDelegate unistrokeGestureRecognizer:self isEvaluatingStrokePath:self.strokePath]; 109 | } 110 | } 111 | } 112 | 113 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 114 | { 115 | [super touchesEnded:touches withEvent:event]; 116 | 117 | [self recognizeUnistroke]; 118 | } 119 | 120 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 121 | { 122 | [super touchesCancelled:touches withEvent:event]; 123 | 124 | [self recognizeUnistroke]; 125 | } 126 | 127 | 128 | #pragma mark - Reset 129 | 130 | - (void)reset 131 | { 132 | [super reset]; 133 | 134 | self.strokePath = nil; 135 | self.trackedTouch = nil; 136 | } 137 | 138 | 139 | #pragma mark - Unistroke recognizer options 140 | 141 | - (void)setProtactorMethodEnabled:(BOOL)protactorMethodEnabled 142 | { 143 | _options->useProtractor = protactorMethodEnabled; 144 | } 145 | 146 | - (BOOL)protactorMethodEnabled 147 | { 148 | return _options->useProtractor; 149 | } 150 | 151 | - (void)setRotationNormalisationEnabled:(BOOL)rotationNormalisationEnabled 152 | { 153 | _options->rotationNormalisationDisabled = (! rotationNormalisationEnabled); 154 | } 155 | 156 | - (BOOL)rotationNormalisationEnabled 157 | { 158 | return (! _options->rotationNormalisationDisabled); 159 | } 160 | 161 | 162 | #pragma mark - Recognize Unistroke 163 | 164 | - (void)recognizeUnistroke 165 | { 166 | if ([self isUnistrokeRecognized]) { 167 | self.state = UIGestureRecognizerStateRecognized; 168 | } 169 | else { 170 | self.state = UIGestureRecognizerStateFailed; 171 | 172 | id unistrokeDelegate = self.unistrokeDelegate; 173 | if ([unistrokeDelegate respondsToSelector:@selector(unistrokeGestureRecognizerDidFailToRecognize:)]) { 174 | [unistrokeDelegate unistrokeGestureRecognizerDidFailToRecognize:self]; 175 | } 176 | } 177 | } 178 | 179 | - (BOOL)isUnistrokeRecognized 180 | { 181 | CMURPathRef path = [self pathFromBezierPath:self.strokePath]; 182 | CMURResultRef result = unistrokeRecognizePathFromTemplates(path, _unistrokeTemplates, _options); 183 | CMURPathDelete(path); 184 | 185 | BOOL isRecognized; 186 | if (result && result->score >= self.minimumScoreThreshold) { 187 | isRecognized = YES; 188 | self.result = [[CMUnistrokeGestureResult alloc] initWithName:[NSString stringWithCString:result->name encoding:NSUTF8StringEncoding] score:result->score]; 189 | NSLog(@"Recognized: result->score = %f result->name = '%s'", result->score, result->name); 190 | //NSLog(@"Path: %@", self.strokePath); 191 | } 192 | else { 193 | isRecognized = NO; 194 | self.result = nil; 195 | NSLog(@"NOT Recognized"); 196 | //NSLog(@"Path: %@", self.strokePath); 197 | } 198 | 199 | CMURResultDelete(result); 200 | 201 | return isRecognized; 202 | } 203 | 204 | - (CMURPathRef)pathFromBezierPath:(UIBezierPath *)bezierPath 205 | { 206 | CMURPathRef path = CMURPathNew(); 207 | CGPathApply(bezierPath.CGPath, path, CMURCGPathApplierFunc); 208 | 209 | return path; 210 | } 211 | 212 | 213 | #pragma mark - Stroke templates 214 | 215 | - (void)registerUnistrokeWithName:(NSString *)name bezierPath:(UIBezierPath *)bezierPath 216 | { 217 | [self registerUnistrokeWithName:name bezierPath:bezierPath bidirectional:NO]; 218 | } 219 | 220 | - (void)registerUnistrokeWithName:(NSString *)name bezierPath:(UIBezierPath *)bezierPath bidirectional:(BOOL)bidirectional 221 | { 222 | CMURPathRef path = [self pathFromBezierPath:bezierPath]; 223 | CMURTemplatesAdd(_unistrokeTemplates, [name cStringUsingEncoding:NSUTF8StringEncoding], path, _options); 224 | 225 | if (bidirectional) { 226 | CMURPathReverse(path); 227 | CMURTemplatesAdd(_unistrokeTemplates, [name cStringUsingEncoding:NSUTF8StringEncoding], path, _options); 228 | } 229 | 230 | CMURPathDelete(path); 231 | } 232 | 233 | - (void)clearAllUnistrokes 234 | { 235 | if (_unistrokeTemplates) { 236 | CMURTemplatesDelete(_unistrokeTemplates); 237 | } 238 | _unistrokeTemplates = CMURTemplatesNew(); 239 | } 240 | 241 | @end 242 | 243 | 244 | static void 245 | CMURCGPathApplierFunc(void *info, const CGPathElement *element) 246 | { 247 | CMURPathRef path = (CMURPathRef)info; 248 | 249 | CGPoint *points = element->points; 250 | CGPathElementType type = element->type; 251 | 252 | switch(type) { 253 | case kCGPathElementMoveToPoint: // contains 1 point 254 | CMURPathAddPoint(path, (float)points[0].x, (float)points[0].y); 255 | break; 256 | 257 | case kCGPathElementAddLineToPoint: // contains 1 point 258 | CMURPathAddPoint(path, (float)points[0].x, (float)points[0].y); 259 | break; 260 | 261 | case kCGPathElementAddQuadCurveToPoint: // contains 2 points 262 | CMURPathAddPoint(path, (float)points[0].x, (float)points[0].y); 263 | CMURPathAddPoint(path, (float)points[1].x, (float)points[1].y); 264 | break; 265 | 266 | case kCGPathElementAddCurveToPoint: // contains 3 points 267 | CMURPathAddPoint(path, (float)points[0].x, (float)points[0].y); 268 | CMURPathAddPoint(path, (float)points[1].x, (float)points[1].y); 269 | CMURPathAddPoint(path, (float)points[2].x, (float)points[2].y); 270 | break; 271 | 272 | case kCGPathElementCloseSubpath: // contains no point 273 | break; 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeGestureResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // CMUnistrokeGestureResult.h 3 | // CMUnistrokeGestureRecognizer 4 | // 5 | // Created by Chris Miles on 17/10/12. 6 | // Copyright (c) 2012 Chris Miles. All rights reserved. 7 | // 8 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | @interface CMUnistrokeGestureResult : NSObject 32 | 33 | @property (nonatomic, strong, readonly) NSString *recognizedStrokeName; 34 | @property (nonatomic, assign, readonly) float recognizedStrokeScore; 35 | 36 | - (id)initWithName:(NSString *)name score:(float)score; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeGestureResult.m: -------------------------------------------------------------------------------- 1 | // 2 | // CMUnistrokeGestureResult.m 3 | // CMUnistrokeGestureRecognizer 4 | // 5 | // Created by Chris Miles on 17/10/12. 6 | // Copyright (c) 2012 Chris Miles. All rights reserved. 7 | // 8 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | // 28 | 29 | #import "CMUnistrokeGestureResult.h" 30 | 31 | 32 | #pragma mark - CMUnistrokeGestureResult class extension 33 | 34 | @interface CMUnistrokeGestureResult () 35 | 36 | @property (nonatomic, strong, readwrite) NSString *recognizedStrokeName; 37 | @property (nonatomic, assign, readwrite) float recognizedStrokeScore; 38 | 39 | @end 40 | 41 | 42 | @implementation CMUnistrokeGestureResult 43 | 44 | - (id)initWithName:(NSString *)name score:(float)score 45 | { 46 | self = [super init]; 47 | if (self) { 48 | self.recognizedStrokeName = name; 49 | self.recognizedStrokeScore = score; 50 | } 51 | return self; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeRecognizer/CMUnistrokeRecognizer.c: -------------------------------------------------------------------------------- 1 | // 2 | // CMUnistrokeRecognizer.c 3 | // CMUnistrokeGestureRecognizer 4 | // 5 | // Created by Chris Miles on 23/09/12. 6 | // Copyright (c) 2012 Chris Miles. All rights reserved. 7 | // 8 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | // 28 | 29 | /** 30 | * Parts based on JavaScript implemention of the $1 Unistroke Recognizer: 31 | * 32 | * Copyright (c) 2007-2011, Jacob O. Wobbrock, Andrew D. Wilson and Yang Li. 33 | * All rights reserved. 34 | * 35 | * Redistribution and use in source and binary forms, with or without 36 | * modification, are permitted provided that the following conditions are met: 37 | * * Redistributions of source code must retain the above copyright 38 | * notice, this list of conditions and the following disclaimer. 39 | * * Redistributions in binary form must reproduce the above copyright 40 | * notice, this list of conditions and the following disclaimer in the 41 | * documentation and/or other materials provided with the distribution. 42 | * * Neither the names of the University of Washington nor Microsoft, 43 | * nor the names of its contributors may be used to endorse or promote 44 | * products derived from this software without specific prior written 45 | * permission. 46 | * 47 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 48 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 49 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 50 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Jacob O. Wobbrock OR Andrew D. Wilson 51 | * OR Yang Li BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 52 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 53 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 54 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 55 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 56 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 | **/ 58 | 59 | #include "CMUnistrokeRecognizer.h" 60 | #include "CMUnistrokeRecognizerPrivate.h" 61 | #include 62 | #include 63 | 64 | #ifndef DEBUG 65 | #define NDEBUG 1 66 | #endif 67 | #include 68 | 69 | 70 | #define kDefaultPathSampleSize 64 71 | 72 | static const float BoundingBoxSize = 250.0f; 73 | static const float theta = 0.7853981634f; // 45° 74 | static const float thetaThreshold = 0.034906585f; // 2° 75 | 76 | 77 | CMURResultRef 78 | unistrokeRecognizePathFromTemplates(CMURPathRef path, CMURTemplatesRef templates, CMUROptionsRef options) 79 | { 80 | float b = MAXFLOAT; 81 | CMURTemplateRef bestTemplate = NULL; 82 | 83 | if (path->length < 2) { 84 | return NULL; 85 | } 86 | 87 | CMURPathRef resampledPath = unistrokeRecognizerResample(path, kDefaultPathSampleSize); 88 | CMURPathRef normalisedPath; 89 | 90 | if (options && options->rotationNormalisationDisabled) { 91 | normalisedPath = CMURPathCopy(resampledPath); 92 | } 93 | else { 94 | float radians = unistrokeRecognizerIndicativeAngle(resampledPath); 95 | normalisedPath = unistrokeRecognizerRotateBy(resampledPath, -radians); 96 | } 97 | 98 | unistrokeRecognizerScaleTo(normalisedPath, BoundingBoxSize); 99 | GLKVector2 origin = GLKVector2Make(0.0f, 0.0f); 100 | unistrokeRecognizerTranslateTo(normalisedPath, origin); 101 | 102 | bool useProtractor = (options && options->useProtractor); 103 | 104 | CMURPathRef vector = NULL; 105 | if (useProtractor) vector = unistrokeRecognizerVectorize(normalisedPath); 106 | 107 | for (unsigned int i=0; ilength; i++) { 108 | CMURTemplateRef template = templates->templateList[i]; 109 | 110 | float d; 111 | if (useProtractor) { 112 | // Protractor (faster) 113 | d = unistrokeRecognizerOptimalCosineDistance(template->vector, vector); 114 | } 115 | else { 116 | // Golden Section Search (original $1) 117 | d = unistrokeRecognizerDistanceAtBestAngle(normalisedPath, template, -theta, theta, thetaThreshold); 118 | } 119 | 120 | if (d < b) { 121 | b = d; 122 | bestTemplate = template; 123 | } 124 | } 125 | 126 | CMURResultRef result = NULL; 127 | if (bestTemplate) { 128 | float score; 129 | if (useProtractor) { 130 | score = 1.0f - b; 131 | } 132 | else { 133 | score = 1.0f - b / (0.5f * sqrtf(BoundingBoxSize*BoundingBoxSize + BoundingBoxSize*BoundingBoxSize)); 134 | } 135 | result = CMURResultNew(bestTemplate->name, score); 136 | } 137 | 138 | CMURPathDelete(normalisedPath); 139 | CMURPathDelete(resampledPath); 140 | if (vector) CMURPathDelete(vector); 141 | 142 | return result; 143 | } 144 | 145 | CMURTemplateRef 146 | unistrokeRecognizerResampledNormalisedTemplate(const char *name, CMURPathRef path, CMUROptionsRef options) 147 | { 148 | CMURPathRef resampledPath = unistrokeRecognizerResample(path, kDefaultPathSampleSize); 149 | CMURPathRef normalisedPath; 150 | 151 | if (options && options->rotationNormalisationDisabled) { 152 | normalisedPath = CMURPathCopy(resampledPath); 153 | } 154 | else { 155 | float radians = unistrokeRecognizerIndicativeAngle(resampledPath); 156 | normalisedPath = unistrokeRecognizerRotateBy(resampledPath, -radians); 157 | } 158 | 159 | unistrokeRecognizerScaleTo(normalisedPath, BoundingBoxSize); 160 | GLKVector2 origin = GLKVector2Make(0.0f, 0.0f); 161 | unistrokeRecognizerTranslateTo(normalisedPath, origin); 162 | 163 | CMURTemplateRef template = CMURTemplateNew(name, normalisedPath); 164 | 165 | CMURPathDelete(resampledPath); 166 | CMURPathDelete(normalisedPath); 167 | 168 | return template; 169 | } 170 | 171 | CMURPathRef 172 | unistrokeRecognizerResample(CMURPathRef path, unsigned int sampleSize) 173 | { 174 | float I = unistrokeRecognizerPathLength(path) / (float)(sampleSize -1 ); 175 | float D = 0; 176 | 177 | CMURPathRef newPath = CMURPathNewWithSize(sampleSize); 178 | CMURPathAddPoint(newPath, path->pointList[0].x, path->pointList[0].y); 179 | 180 | unsigned int i = 1; // points index 181 | 182 | GLKVector2 prevPoint = path->pointList[0]; 183 | GLKVector2 currentPoint = path->pointList[1]; 184 | 185 | while (i < path->length) { 186 | float d = GLKVector2Distance(prevPoint, currentPoint); 187 | if (D + d >= I) { 188 | float x = prevPoint.x + ((I - D) / d) * (currentPoint.x - prevPoint.x); 189 | float y = prevPoint.y + ((I - D) / d) * (currentPoint.y - prevPoint.y); 190 | CMURPathAddPoint(newPath, x, y); 191 | 192 | prevPoint.x = x; 193 | prevPoint.y = y; 194 | D = 0.0f; 195 | } 196 | else { 197 | D += d; 198 | i++; 199 | prevPoint = path->pointList[i-1]; 200 | currentPoint = path->pointList[i]; 201 | } 202 | } 203 | 204 | // sometimes we fall a rounding-error short of adding the last point, so add it if so 205 | if (newPath->length == sampleSize - 1) { 206 | CMURPathAddPoint(newPath, path->pointList[path->length - 1].x, path->pointList[path->length - 1].y); 207 | } 208 | 209 | return newPath; 210 | } 211 | 212 | float 213 | unistrokeRecognizerIndicativeAngle(CMURPathRef path) 214 | { 215 | GLKVector2 centroid = unistrokeRecognizerCentroid(path); 216 | float angle = atan2f(centroid.y - path->pointList[0].y, centroid.x - path->pointList[0].x); // for -pi <= angle <= pi 217 | return angle; 218 | } 219 | 220 | float 221 | unistrokeRecognizerDistanceAtBestAngle(CMURPathRef path, CMURTemplateRef template, float radiansA, float radiansB, float radiansDelta) 222 | { 223 | static float phi = 0.0f; 224 | 225 | if (phi == 0.0f) phi = 0.5f * (-1.0f + sqrtf(5.0f)); 226 | 227 | float x1 = phi * radiansA + (1.0f - phi) * radiansB; 228 | float f1 = unistrokeRecognizerDistanceAtAngle(path, template, x1); 229 | float x2 = (1.0f - phi) * radiansA + phi * radiansB; 230 | float f2 = unistrokeRecognizerDistanceAtAngle(path, template, x2); 231 | while (fabsf(radiansB - radiansA) > radiansDelta) { 232 | if (f1 < f2) { 233 | radiansB = x2; 234 | x2 = x1; 235 | f2 = f1; 236 | x1 = phi * radiansA + (1.0f - phi) * radiansB; 237 | f1 = unistrokeRecognizerDistanceAtAngle(path, template, x1); 238 | } 239 | else { 240 | radiansA = x1; 241 | x1 = x2; 242 | f1 = f2; 243 | x2 = (1.0f - phi) * radiansA + phi * radiansB; 244 | f2 = unistrokeRecognizerDistanceAtAngle(path, template, x2); 245 | } 246 | } 247 | return fminf(f1, f2); 248 | } 249 | 250 | GLKVector2 251 | unistrokeRecognizerCentroid(CMURPathRef path) 252 | { 253 | float sumX = 0; 254 | float sumY = 0; 255 | 256 | for (unsigned int i=0; i < path->length; i++) { 257 | sumX += path->pointList[i].x; 258 | sumY += path->pointList[i].y; 259 | } 260 | 261 | GLKVector2 centroid = GLKVector2Make(sumX / (float)path->length, sumY / (float)path->length); 262 | return centroid; 263 | } 264 | 265 | CMURPathRef 266 | unistrokeRecognizerRotateBy(CMURPathRef path, float radians) 267 | { 268 | CMURPathRef newPath = NULL; 269 | 270 | if (path->length > 0) { 271 | GLKVector2 centroid = unistrokeRecognizerCentroid(path); 272 | float cos = cosf(radians); 273 | float sin = sinf(radians); 274 | 275 | newPath = CMURPathNewWithSize(path->length); 276 | for (unsigned int i=0; i < path->length; i++) { 277 | GLKVector2 point = path->pointList[i]; 278 | float x = (point.x - centroid.x) * cos - (point.y - centroid.y) * sin + centroid.x; 279 | float y = (point.x - centroid.x) * sin + (point.y - centroid.y) * cos + centroid.y; 280 | CMURPathAddPoint(newPath, x, y); 281 | } 282 | } 283 | 284 | return newPath; 285 | } 286 | 287 | CMURRectangle 288 | unistrokeRecognizerBoundingBox(CMURPathRef path) 289 | { 290 | float minX = MAXFLOAT; 291 | float minY = MAXFLOAT; 292 | float maxX = -MAXFLOAT; 293 | float maxY = -MAXFLOAT; 294 | 295 | for (unsigned int i=0; i < path->length; i++) { 296 | if (path->pointList[i].x < minX) minX = path->pointList[i].x; 297 | if (path->pointList[i].x > maxX) maxX = path->pointList[i].x; 298 | if (path->pointList[i].y < minY) minY = path->pointList[i].y; 299 | if (path->pointList[i].y > maxY) maxY = path->pointList[i].y; 300 | } 301 | 302 | CMURRectangle result; 303 | result.x = minX; 304 | result.y = minY; 305 | result.width = maxX - minX; 306 | result.height = maxY - minY; 307 | 308 | return result; 309 | } 310 | 311 | void 312 | unistrokeRecognizerScaleTo(CMURPathRef path, float size) 313 | { 314 | CMURRectangle box = unistrokeRecognizerBoundingBox(path); 315 | 316 | for (unsigned int i=0; i < path->length; i++) { 317 | path->pointList[i].x *= size / box.width; 318 | path->pointList[i].y *= size / box.height; 319 | } 320 | } 321 | 322 | void 323 | unistrokeRecognizerTranslateTo(CMURPathRef path, GLKVector2 k) 324 | { 325 | GLKVector2 centroid = unistrokeRecognizerCentroid(path); 326 | 327 | for (unsigned int i=0; i < path->length; i++) { 328 | path->pointList[i].x += k.x - centroid.x; 329 | path->pointList[i].y += k.y - centroid.y; 330 | } 331 | } 332 | 333 | float 334 | unistrokeRecognizerDistanceAtAngle(CMURPathRef path, CMURTemplateRef template, float radians) 335 | { 336 | CMURPathRef rotatedPath = unistrokeRecognizerRotateBy(path, radians); 337 | 338 | assert(rotatedPath != NULL); 339 | if (rotatedPath == NULL) return MAXFLOAT; 340 | 341 | float d = unistrokeRecognizerPathDistance(rotatedPath, template->path); 342 | CMURPathDelete(rotatedPath); 343 | return d; 344 | } 345 | 346 | float 347 | unistrokeRecognizerPathDistance(CMURPathRef pathA, CMURPathRef pathB) 348 | { 349 | float d = 0.0f; 350 | 351 | assert(pathA->length == pathB->length); 352 | 353 | for (unsigned int i=0; i < pathA->length; i++) { 354 | float distance = GLKVector2Distance(pathA->pointList[i], pathB->pointList[i]); 355 | if (! isnan(distance)) d += distance; 356 | } 357 | 358 | return d / (float)pathA->length; 359 | } 360 | 361 | float 362 | unistrokeRecognizerPathLength(CMURPathRef path) 363 | { 364 | float d = 0.0f; 365 | for (unsigned int i=1; i < path->length; i++) 366 | d += GLKVector2Distance(path->pointList[i - 1], path->pointList[i]); 367 | return d; 368 | } 369 | 370 | 371 | /* 372 | Functions for Protractor 373 | */ 374 | 375 | CMURPathRef 376 | unistrokeRecognizerVectorize(CMURPathRef path) 377 | { 378 | float sum = 0.0f; 379 | CMURPathRef vector = CMURPathNew(); 380 | for (unsigned int i=0; i < path->length; i++) { 381 | float x = path->pointList[i].x; 382 | float y = path->pointList[i].y; 383 | CMURPathAddPoint(vector, x, y); 384 | sum += path->pointList[i].x * path->pointList[i].x + path->pointList[i].y * path->pointList[i].y; 385 | } 386 | float magnitude = sqrtf(sum); 387 | float invMagnitude = 1.0f / magnitude; 388 | 389 | for (unsigned int i=0; i < vector->length; i++) { 390 | vector->pointList[i] = GLKVector2MultiplyScalar(vector->pointList[i], invMagnitude); 391 | } 392 | return vector; 393 | } 394 | 395 | float 396 | unistrokeRecognizerOptimalCosineDistance(CMURPathRef vector1, CMURPathRef vector2) 397 | { 398 | assert(vector1->length == vector2->length); 399 | 400 | float a = 0.0f; 401 | float b = 0.0f; 402 | for (unsigned int i = 0; i < vector1->length; i++) 403 | { 404 | a += vector1->pointList[i].x * vector2->pointList[i].x + vector1->pointList[i].y * vector2->pointList[i].y; 405 | b += vector1->pointList[i].x * vector2->pointList[i].y - vector1->pointList[i].y * vector2->pointList[i].x; 406 | } 407 | float angle = atanf(b / a); 408 | float z = a * cosf(angle) + b * sinf(angle); 409 | float distance = acosf(z); 410 | return distance; 411 | } 412 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeRecognizer/CMUnistrokeRecognizer.h: -------------------------------------------------------------------------------- 1 | // 2 | // CMUnistrokeRecognizer.h 3 | // CMUnistrokeGestureRecognizer 4 | // 5 | // Created by Chris Miles on 23/09/12. 6 | // Copyright (c) 2012 Chris Miles. All rights reserved. 7 | // 8 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | // 28 | 29 | #ifndef CMUnistrokeGestureRecognizer_h 30 | #define CMUnistrokeGestureRecognizer_h 31 | 32 | #include "CMUnistrokeRecognizerTypes.h" 33 | 34 | CMURResultRef 35 | unistrokeRecognizePathFromTemplates(CMURPathRef path, CMURTemplatesRef templates, CMUROptionsRef options); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeRecognizer/CMUnistrokeRecognizerPrivate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CMUnistrokeRecognizerPrivate.h 3 | // CMUnistrokeGestureRecognizer 4 | // 5 | // Created by Chris Miles on 24/09/12. 6 | // Copyright (c) 2012 Chris Miles. All rights reserved. 7 | // 8 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | // 28 | 29 | #ifndef CMUnistrokeGestureRecognizer_Private_h 30 | #define CMUnistrokeGestureRecognizer_Private_h 31 | 32 | #include "CMUnistrokeRecognizerTypes.h" 33 | 34 | 35 | struct _CMURPath { 36 | unsigned int length; // number of points 37 | GLKVector2 *pointList; 38 | unsigned int pointListSize; // size of array 39 | }; 40 | 41 | struct _CMURTemplate { 42 | char *name; 43 | CMURPathRef path; 44 | CMURPathRef vector; 45 | }; 46 | typedef struct _CMURTemplate *CMURTemplateRef; 47 | 48 | typedef struct _CMURTemplates { 49 | unsigned int length; // number of templates 50 | CMURTemplateRef *templateList; 51 | unsigned int templateListSize; // size of array 52 | } CMURTemplates; 53 | 54 | typedef struct { 55 | float x; 56 | float y; 57 | float width; 58 | float height; 59 | } CMURRectangle; 60 | 61 | 62 | /* 63 | CMURTemplate functions 64 | */ 65 | 66 | CMURTemplateRef 67 | CMURTemplateNew(const char *name, CMURPathRef path); 68 | 69 | void 70 | CMURTemplateDelete(CMURTemplateRef template); 71 | 72 | 73 | /* 74 | Recognizer helper functions 75 | */ 76 | 77 | CMURTemplateRef 78 | unistrokeRecognizerResampledNormalisedTemplate(const char *name, CMURPathRef path, CMUROptionsRef options); 79 | 80 | CMURPathRef 81 | unistrokeRecognizerResample(CMURPathRef path, unsigned int sampleSize); 82 | 83 | float 84 | unistrokeRecognizerIndicativeAngle(CMURPathRef path); 85 | 86 | float 87 | unistrokeRecognizerDistanceAtBestAngle(CMURPathRef path, CMURTemplateRef template, float radiansA, float radiansB, float radiansDelta); 88 | 89 | GLKVector2 90 | unistrokeRecognizerCentroid(CMURPathRef path); 91 | 92 | CMURPathRef 93 | unistrokeRecognizerRotateBy(CMURPathRef path, float radians); 94 | 95 | CMURRectangle 96 | unistrokeRecognizerBoundingBox(CMURPathRef path); 97 | 98 | void 99 | unistrokeRecognizerScaleTo(CMURPathRef path, float size); 100 | 101 | void 102 | unistrokeRecognizerTranslateTo(CMURPathRef path, GLKVector2 k); 103 | 104 | float 105 | unistrokeRecognizerDistanceAtAngle(CMURPathRef path, CMURTemplateRef template, float radians); 106 | 107 | float 108 | unistrokeRecognizerPathDistance(CMURPathRef pathA, CMURPathRef pathB); 109 | 110 | float 111 | unistrokeRecognizerPathLength(CMURPathRef path); 112 | 113 | CMURPathRef 114 | unistrokeRecognizerVectorize(CMURPathRef path); 115 | 116 | float 117 | unistrokeRecognizerOptimalCosineDistance(CMURPathRef vector1, CMURPathRef vector2); 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeRecognizer/CMUnistrokeRecognizerTypes.c: -------------------------------------------------------------------------------- 1 | // 2 | // CMUnistrokeRecognizerTypes.c 3 | // CMUnistrokeGestureRecognizer 4 | // 5 | // Created by Chris Miles on 8/10/12. 6 | // Copyright (c) 2012 Chris Miles. All rights reserved. 7 | // 8 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | // 28 | 29 | #include 30 | #include 31 | 32 | #include "CMUnistrokeRecognizerTypes.h" 33 | #include "CMUnistrokeRecognizerPrivate.h" 34 | 35 | #define kDefaultPathArraySize 32 36 | #define kDefaultTemplatesArraySize 4 37 | 38 | 39 | #pragma mark - struct CMURTemplates 40 | 41 | CMURTemplatesRef 42 | CMURTemplatesNew(void) 43 | { 44 | CMURTemplatesRef templates = malloc(sizeof(CMURTemplates)); 45 | 46 | templates->templateListSize = kDefaultTemplatesArraySize; 47 | templates->templateList = malloc(kDefaultTemplatesArraySize * sizeof(CMURTemplateRef)); 48 | templates->length = 0; 49 | 50 | return templates; 51 | } 52 | 53 | void 54 | CMURTemplatesDelete(CMURTemplatesRef templates) 55 | { 56 | if (NULL == templates) return; 57 | 58 | if (templates->templateList) { 59 | for (unsigned int i=0; i < templates->length; i++) { 60 | CMURTemplateDelete(templates->templateList[i]); 61 | } 62 | free(templates->templateList); 63 | templates->templateList = NULL; 64 | } 65 | templates->length = 0; 66 | templates->templateListSize = 0; 67 | 68 | free(templates); 69 | } 70 | 71 | void 72 | CMURTemplatesAdd(CMURTemplatesRef templates, const char *name, CMURPathRef path, CMUROptionsRef options) 73 | { 74 | if (templates->length >= templates->templateListSize) { 75 | templates->templateListSize *= 2; 76 | templates->templateList = realloc(templates->templateList, templates->templateListSize * sizeof(CMURTemplateRef)); 77 | } 78 | 79 | CMURTemplateRef template = unistrokeRecognizerResampledNormalisedTemplate(name, path, options); 80 | template->vector = unistrokeRecognizerVectorize(template->path); 81 | 82 | templates->templateList[templates->length++] = template; 83 | } 84 | 85 | 86 | #pragma mark - CMURPath 87 | 88 | CMURPathRef 89 | CMURPathNew(void) 90 | { 91 | return CMURPathNewWithSize(kDefaultPathArraySize); 92 | } 93 | 94 | CMURPathRef 95 | CMURPathNewWithSize(unsigned int size) 96 | { 97 | CMURPathRef path = malloc(sizeof(struct _CMURPath)); 98 | path->length = 0; 99 | path->pointListSize = size; 100 | path->pointList = malloc(path->pointListSize * sizeof(GLKVector2)); 101 | return path; 102 | } 103 | 104 | CMURPathRef 105 | CMURPathNewWithPoints(GLKVector2 *points, unsigned int pointsLength) 106 | { 107 | CMURPathRef path = CMURPathNewWithSize(pointsLength); 108 | for (unsigned int i=0; ipointList[i] = points[i]; 110 | } 111 | path->length = pointsLength; 112 | return path; 113 | } 114 | 115 | CMURPathRef 116 | CMURPathCopy(CMURPathRef sourcePath) 117 | { 118 | CMURPathRef copiedPath = CMURPathNewWithPoints(sourcePath->pointList, sourcePath->length); 119 | return copiedPath; 120 | } 121 | 122 | void 123 | CMURPathDelete(CMURPathRef path) 124 | { 125 | if (NULL == path) return; 126 | 127 | if (path->pointList) { 128 | free(path->pointList); 129 | path->pointList = NULL; 130 | } 131 | path->length = 0; 132 | path->pointListSize = 0; 133 | 134 | free(path); 135 | } 136 | 137 | void 138 | CMURPathAddPoint(CMURPathRef path, float x, float y) 139 | { 140 | if (path->length >= path->pointListSize) { 141 | path->pointListSize *= 2; 142 | path->pointList = realloc(path->pointList, path->pointListSize * sizeof(GLKVector2)); 143 | } 144 | 145 | GLKVector2 vector = GLKVector2Make(x, y); 146 | path->pointList[path->length++] = vector; 147 | } 148 | 149 | void 150 | CMURPathReverse(CMURPathRef path) 151 | { 152 | for (unsigned int i=0; ilength/2; i++) { 153 | unsigned int indexA = i; 154 | unsigned int indexB = path->length-i-1; 155 | 156 | GLKVector2 pointA = path->pointList[indexA]; 157 | GLKVector2 pointB = path->pointList[indexB]; 158 | 159 | path->pointList[indexB] = pointA; 160 | path->pointList[indexA] = pointB; 161 | } 162 | } 163 | 164 | 165 | #pragma mark - CMURTemplate 166 | 167 | CMURTemplateRef 168 | CMURTemplateNew(const char *name, CMURPathRef path) 169 | { 170 | CMURTemplateRef template = malloc(sizeof(struct _CMURTemplate)); 171 | 172 | size_t nameSize = strlen(name); 173 | template->name = calloc(nameSize+1, sizeof(char)); 174 | strncpy(template->name, name, nameSize); 175 | 176 | template->path = CMURPathCopy(path); 177 | template->vector = NULL; 178 | 179 | return template; 180 | } 181 | 182 | void 183 | CMURTemplateDelete(CMURTemplateRef template) 184 | { 185 | if (NULL == template) return; 186 | 187 | if (template->path) { 188 | CMURPathDelete(template->path); 189 | template->path = NULL; 190 | } 191 | if (template->name) { 192 | free(template->name); 193 | template->name = NULL; 194 | } 195 | if (template->vector) { 196 | CMURPathDelete(template->vector); 197 | template->vector = NULL; 198 | } 199 | } 200 | 201 | 202 | #pragma mark - Result 203 | 204 | CMURResultRef 205 | CMURResultNew(char *name, float score) 206 | { 207 | CMURResultRef result = malloc(sizeof(struct _CMURResult)); 208 | 209 | if (name) { 210 | size_t nameSize = strlen(name); 211 | result->name = calloc(nameSize+1, sizeof(char)); 212 | strncpy(result->name, name, nameSize); 213 | } 214 | else { 215 | result->name = NULL; 216 | } 217 | 218 | result->score = score; 219 | 220 | return result; 221 | } 222 | 223 | void 224 | CMURResultDelete(CMURResultRef result) 225 | { 226 | if (NULL == result) return; 227 | 228 | if (result->name) { 229 | free(result->name); 230 | result->name = NULL; 231 | } 232 | result->score = 0.0f; 233 | 234 | free(result); 235 | } 236 | 237 | 238 | #pragma mark - Options 239 | 240 | CMUROptionsRef 241 | CMUROptionsNew(void) 242 | { 243 | CMUROptionsRef options = calloc(1, sizeof(struct _CMUROptions)); 244 | return options; 245 | } 246 | 247 | void 248 | CMUROptionsDelete(CMUROptionsRef options) 249 | { 250 | if (NULL == options) return; 251 | 252 | free(options); 253 | } 254 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/CMUnistrokeRecognizer/CMUnistrokeRecognizerTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // CMUnistrokeRecognizerTypes.h 3 | // CMUnistrokeGestureRecognizer 4 | // 5 | // Created by Chris Miles on 8/10/12. 6 | // Copyright (c) 2012 Chris Miles. All rights reserved. 7 | // 8 | // MIT Licensed (http://opensource.org/licenses/mit-license.php): 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | // 28 | 29 | #ifndef CMUnistrokeGestureRecognizer_Types_h 30 | #define CMUnistrokeGestureRecognizer_Types_h 31 | 32 | /* 33 | GLKVector2, from the GLKit framework, is used to represent points so we 34 | can take advantage of the high performance GLKit math functions. 35 | */ 36 | #include 37 | 38 | 39 | typedef struct _CMURResult { 40 | char *name; 41 | float score; 42 | } CMURResult; 43 | 44 | typedef struct _CMUROptions { 45 | bool useProtractor; 46 | bool rotationNormalisationDisabled; 47 | } CMUROptions; 48 | 49 | typedef struct _CMUROptions *CMUROptionsRef; 50 | typedef struct _CMURPath *CMURPathRef; 51 | typedef struct _CMURResult *CMURResultRef; 52 | typedef struct _CMURTemplates *CMURTemplatesRef; 53 | 54 | 55 | /* 56 | CMURTemplates functions 57 | */ 58 | 59 | CMURTemplatesRef 60 | CMURTemplatesNew(void); 61 | 62 | void 63 | CMURTemplatesDelete(CMURTemplatesRef templates); 64 | 65 | void 66 | CMURTemplatesAdd(CMURTemplatesRef templates, const char *name, CMURPathRef path, CMUROptionsRef options); 67 | 68 | 69 | /* 70 | CMURPath functions 71 | */ 72 | 73 | CMURPathRef 74 | CMURPathNew(void); 75 | 76 | CMURPathRef 77 | CMURPathNewWithSize(unsigned int size); 78 | 79 | CMURPathRef 80 | CMURPathNewWithPoints(GLKVector2 *points, unsigned int pointsLength); 81 | 82 | CMURPathRef 83 | CMURPathCopy(CMURPathRef sourcePath); 84 | 85 | void 86 | CMURPathDelete(CMURPathRef path); 87 | 88 | void 89 | CMURPathAddPoint(CMURPathRef path, float x, float y); 90 | 91 | void 92 | CMURPathReverse(CMURPathRef path); 93 | 94 | 95 | /* 96 | CMURResult functions 97 | */ 98 | 99 | CMURResultRef 100 | CMURResultNew(char *name, float score); 101 | 102 | void 103 | CMURResultDelete(CMURResultRef result); 104 | 105 | 106 | /* 107 | CMUROptions functions 108 | */ 109 | 110 | CMUROptionsRef 111 | CMUROptionsNew(void); 112 | 113 | void 114 | CMUROptionsDelete(CMUROptionsRef options); 115 | 116 | 117 | #endif /* CMUnistrokeGestureRecognizer_Types_h */ 118 | -------------------------------------------------------------------------------- /CMUnistrokeGestureRecognizer/GestureKeyboard-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // GestureKeyboard-Bridging-Header.h 3 | // GestureKeyboard 4 | // 5 | // Created by Rob Terrell on 6/8/14. 6 | // Copyright (c) 2014 TouchCentric. All rights reserved. 7 | // 8 | 9 | #import "CMUnistrokeGestureRecognizer.h" 10 | #import "CMUnistrokeGestureResult.h" 11 | 12 | -------------------------------------------------------------------------------- /GestureKeyboard.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D460B2B81943CF7600A5D298 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D460B2B71943CF7600A5D298 /* AppDelegate.swift */; }; 11 | D460B2BA1943CF7600A5D298 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D460B2B91943CF7600A5D298 /* ViewController.swift */; }; 12 | D460B2BD1943CF7600A5D298 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D460B2BB1943CF7600A5D298 /* Main.storyboard */; }; 13 | D460B2BF1943CF7600A5D298 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D460B2BE1943CF7600A5D298 /* Images.xcassets */; }; 14 | D460B2CB1943CF7700A5D298 /* GestureKeyboardTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D460B2CA1943CF7700A5D298 /* GestureKeyboardTests.swift */; }; 15 | D460B2E01944C09900A5D298 /* CMUnistrokeGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = D460B2D71944C09900A5D298 /* CMUnistrokeGestureRecognizer.m */; }; 16 | D460B2E11944C09900A5D298 /* CMUnistrokeGestureResult.m in Sources */ = {isa = PBXBuildFile; fileRef = D460B2D91944C09900A5D298 /* CMUnistrokeGestureResult.m */; }; 17 | D460B2E21944C09900A5D298 /* CMUnistrokeRecognizer.c in Sources */ = {isa = PBXBuildFile; fileRef = D460B2DB1944C09900A5D298 /* CMUnistrokeRecognizer.c */; }; 18 | D460B2E31944C09900A5D298 /* CMUnistrokeRecognizerTypes.c in Sources */ = {isa = PBXBuildFile; fileRef = D460B2DE1944C09900A5D298 /* CMUnistrokeRecognizerTypes.c */; }; 19 | D460B2E81944D69900A5D298 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2E71944D69900A5D298 /* Foundation.framework */; }; 20 | D460B2EA1944D6A100A5D298 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2E91944D6A100A5D298 /* GLKit.framework */; }; 21 | D460B2EC1944D6A600A5D298 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2EB1944D6A600A5D298 /* UIKit.framework */; }; 22 | D460B2EE1944D6F300A5D298 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2ED1944D6F300A5D298 /* CoreGraphics.framework */; }; 23 | D49E3281194528EE001B9BB1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2E71944D69900A5D298 /* Foundation.framework */; }; 24 | D49E3282194528F2001B9BB1 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2E91944D6A100A5D298 /* GLKit.framework */; }; 25 | D49E3283194528F5001B9BB1 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2EB1944D6A600A5D298 /* UIKit.framework */; }; 26 | D49E3284194528FA001B9BB1 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2ED1944D6F300A5D298 /* CoreGraphics.framework */; }; 27 | D49E328E19454F84001B9BB1 /* KeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49E328D19454F84001B9BB1 /* KeyboardViewController.swift */; }; 28 | D49E329219454F84001B9BB1 /* com.touchcentric.GestureKeyboard.GestureKeyboardExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D49E328919454F84001B9BB1 /* com.touchcentric.GestureKeyboard.GestureKeyboardExtension.appex */; }; 29 | D49E3298194552C1001B9BB1 /* CMUnistrokeGestureResult.m in Sources */ = {isa = PBXBuildFile; fileRef = D460B2D91944C09900A5D298 /* CMUnistrokeGestureResult.m */; }; 30 | D49E3299194552C6001B9BB1 /* CMUnistrokeGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = D460B2D71944C09900A5D298 /* CMUnistrokeGestureRecognizer.m */; }; 31 | D49E329A194552CB001B9BB1 /* CMUnistrokeRecognizer.c in Sources */ = {isa = PBXBuildFile; fileRef = D460B2DB1944C09900A5D298 /* CMUnistrokeRecognizer.c */; }; 32 | D49E329B194552DA001B9BB1 /* CMUnistrokeRecognizerTypes.c in Sources */ = {isa = PBXBuildFile; fileRef = D460B2DE1944C09900A5D298 /* CMUnistrokeRecognizerTypes.c */; }; 33 | D49E329C1946BAE2001B9BB1 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2E91944D6A100A5D298 /* GLKit.framework */; }; 34 | D49E329D1946BAE7001B9BB1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2E71944D69900A5D298 /* Foundation.framework */; }; 35 | D49E329E1946BAEE001B9BB1 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2ED1944D6F300A5D298 /* CoreGraphics.framework */; }; 36 | D49E329F1946BAFC001B9BB1 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D460B2EB1944D6A600A5D298 /* UIKit.framework */; }; 37 | D49E32B41946E176001B9BB1 /* graffiti_strokes.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = D49E32B31946E176001B9BB1 /* graffiti_strokes.jpeg */; }; 38 | D4B4ABBD1947520800052189 /* graffiti.plist in Resources */ = {isa = PBXBuildFile; fileRef = D4B4ABBC1947520800052189 /* graffiti.plist */; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXContainerItemProxy section */ 42 | D460B2C51943CF7700A5D298 /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = D460B2AA1943CF7600A5D298 /* Project object */; 45 | proxyType = 1; 46 | remoteGlobalIDString = D460B2B11943CF7600A5D298; 47 | remoteInfo = GestureKeyboard; 48 | }; 49 | D49E328F19454F84001B9BB1 /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = D460B2AA1943CF7600A5D298 /* Project object */; 52 | proxyType = 1; 53 | remoteGlobalIDString = D49E328819454F84001B9BB1; 54 | remoteInfo = GestureKeyboardExtension; 55 | }; 56 | D49E329319454F84001B9BB1 /* PBXContainerItemProxy */ = { 57 | isa = PBXContainerItemProxy; 58 | containerPortal = D460B2AA1943CF7600A5D298 /* Project object */; 59 | proxyType = 1; 60 | remoteGlobalIDString = D49E328819454F84001B9BB1; 61 | remoteInfo = GestureKeyboardExtension; 62 | }; 63 | /* End PBXContainerItemProxy section */ 64 | 65 | /* Begin PBXCopyFilesBuildPhase section */ 66 | D49E329119454F84001B9BB1 /* Embed App Extensions */ = { 67 | isa = PBXCopyFilesBuildPhase; 68 | buildActionMask = 2147483647; 69 | dstPath = ""; 70 | dstSubfolderSpec = 13; 71 | files = ( 72 | D49E329219454F84001B9BB1 /* com.touchcentric.GestureKeyboard.GestureKeyboardExtension.appex in Embed App Extensions */, 73 | ); 74 | name = "Embed App Extensions"; 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | /* End PBXCopyFilesBuildPhase section */ 78 | 79 | /* Begin PBXFileReference section */ 80 | D460B2B21943CF7600A5D298 /* GestureKeyboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GestureKeyboard.app; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | D460B2B61943CF7600A5D298 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 82 | D460B2B71943CF7600A5D298 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 83 | D460B2B91943CF7600A5D298 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 84 | D460B2BC1943CF7600A5D298 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 85 | D460B2BE1943CF7600A5D298 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 86 | D460B2C41943CF7700A5D298 /* GestureKeyboardTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GestureKeyboardTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 87 | D460B2C91943CF7700A5D298 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 88 | D460B2CA1943CF7700A5D298 /* GestureKeyboardTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GestureKeyboardTests.swift; sourceTree = ""; }; 89 | D460B2D51944C09900A5D298 /* CMUnistrokeGestureRecognizer-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CMUnistrokeGestureRecognizer-Prefix.pch"; sourceTree = ""; }; 90 | D460B2D61944C09900A5D298 /* CMUnistrokeGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMUnistrokeGestureRecognizer.h; sourceTree = ""; }; 91 | D460B2D71944C09900A5D298 /* CMUnistrokeGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMUnistrokeGestureRecognizer.m; sourceTree = ""; }; 92 | D460B2D81944C09900A5D298 /* CMUnistrokeGestureResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMUnistrokeGestureResult.h; sourceTree = ""; }; 93 | D460B2D91944C09900A5D298 /* CMUnistrokeGestureResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMUnistrokeGestureResult.m; sourceTree = ""; }; 94 | D460B2DB1944C09900A5D298 /* CMUnistrokeRecognizer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CMUnistrokeRecognizer.c; sourceTree = ""; }; 95 | D460B2DC1944C09900A5D298 /* CMUnistrokeRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMUnistrokeRecognizer.h; sourceTree = ""; }; 96 | D460B2DD1944C09900A5D298 /* CMUnistrokeRecognizerPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMUnistrokeRecognizerPrivate.h; sourceTree = ""; }; 97 | D460B2DE1944C09900A5D298 /* CMUnistrokeRecognizerTypes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CMUnistrokeRecognizerTypes.c; sourceTree = ""; }; 98 | D460B2DF1944C09900A5D298 /* CMUnistrokeRecognizerTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMUnistrokeRecognizerTypes.h; sourceTree = ""; }; 99 | D460B2E61944D35A00A5D298 /* GestureKeyboard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "GestureKeyboard-Bridging-Header.h"; path = "../CMUnistrokeGestureRecognizer/GestureKeyboard-Bridging-Header.h"; sourceTree = ""; }; 100 | D460B2E71944D69900A5D298 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 101 | D460B2E91944D6A100A5D298 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; }; 102 | D460B2EB1944D6A600A5D298 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 103 | D460B2ED1944D6F300A5D298 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 104 | D49E328919454F84001B9BB1 /* com.touchcentric.GestureKeyboard.GestureKeyboardExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = com.touchcentric.GestureKeyboard.GestureKeyboardExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 105 | D49E328C19454F84001B9BB1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 106 | D49E328D19454F84001B9BB1 /* KeyboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardViewController.swift; sourceTree = ""; }; 107 | D49E32B31946E176001B9BB1 /* graffiti_strokes.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; name = graffiti_strokes.jpeg; path = GestureKeyboard/graffiti_strokes.jpeg; sourceTree = ""; }; 108 | D4B4ABBC1947520800052189 /* graffiti.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = graffiti.plist; sourceTree = ""; }; 109 | /* End PBXFileReference section */ 110 | 111 | /* Begin PBXFrameworksBuildPhase section */ 112 | D460B2AF1943CF7600A5D298 /* Frameworks */ = { 113 | isa = PBXFrameworksBuildPhase; 114 | buildActionMask = 2147483647; 115 | files = ( 116 | D460B2E81944D69900A5D298 /* Foundation.framework in Frameworks */, 117 | D460B2EE1944D6F300A5D298 /* CoreGraphics.framework in Frameworks */, 118 | D460B2EC1944D6A600A5D298 /* UIKit.framework in Frameworks */, 119 | D460B2EA1944D6A100A5D298 /* GLKit.framework in Frameworks */, 120 | ); 121 | runOnlyForDeploymentPostprocessing = 0; 122 | }; 123 | D460B2C11943CF7700A5D298 /* Frameworks */ = { 124 | isa = PBXFrameworksBuildPhase; 125 | buildActionMask = 2147483647; 126 | files = ( 127 | D49E3284194528FA001B9BB1 /* CoreGraphics.framework in Frameworks */, 128 | D49E3282194528F2001B9BB1 /* GLKit.framework in Frameworks */, 129 | D49E3283194528F5001B9BB1 /* UIKit.framework in Frameworks */, 130 | D49E3281194528EE001B9BB1 /* Foundation.framework in Frameworks */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | D49E328619454F84001B9BB1 /* Frameworks */ = { 135 | isa = PBXFrameworksBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | D49E329F1946BAFC001B9BB1 /* UIKit.framework in Frameworks */, 139 | D49E329E1946BAEE001B9BB1 /* CoreGraphics.framework in Frameworks */, 140 | D49E329D1946BAE7001B9BB1 /* Foundation.framework in Frameworks */, 141 | D49E329C1946BAE2001B9BB1 /* GLKit.framework in Frameworks */, 142 | ); 143 | runOnlyForDeploymentPostprocessing = 0; 144 | }; 145 | /* End PBXFrameworksBuildPhase section */ 146 | 147 | /* Begin PBXGroup section */ 148 | D460B2A91943CF7600A5D298 = { 149 | isa = PBXGroup; 150 | children = ( 151 | D4B4ABBC1947520800052189 /* graffiti.plist */, 152 | D49E32B31946E176001B9BB1 /* graffiti_strokes.jpeg */, 153 | D460B2ED1944D6F300A5D298 /* CoreGraphics.framework */, 154 | D460B2EB1944D6A600A5D298 /* UIKit.framework */, 155 | D460B2E91944D6A100A5D298 /* GLKit.framework */, 156 | D460B2E71944D69900A5D298 /* Foundation.framework */, 157 | D460B2D41944C09900A5D298 /* CMUnistrokeGestureRecognizer */, 158 | D460B2B41943CF7600A5D298 /* GestureKeyboard */, 159 | D460B2C71943CF7700A5D298 /* GestureKeyboardTests */, 160 | D49E328A19454F84001B9BB1 /* GestureKeyboardExtension */, 161 | D460B2B31943CF7600A5D298 /* Products */, 162 | ); 163 | sourceTree = ""; 164 | }; 165 | D460B2B31943CF7600A5D298 /* Products */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | D460B2B21943CF7600A5D298 /* GestureKeyboard.app */, 169 | D460B2C41943CF7700A5D298 /* GestureKeyboardTests.xctest */, 170 | D49E328919454F84001B9BB1 /* com.touchcentric.GestureKeyboard.GestureKeyboardExtension.appex */, 171 | ); 172 | name = Products; 173 | sourceTree = ""; 174 | }; 175 | D460B2B41943CF7600A5D298 /* GestureKeyboard */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | D460B2E61944D35A00A5D298 /* GestureKeyboard-Bridging-Header.h */, 179 | D460B2B71943CF7600A5D298 /* AppDelegate.swift */, 180 | D460B2B91943CF7600A5D298 /* ViewController.swift */, 181 | D460B2BB1943CF7600A5D298 /* Main.storyboard */, 182 | D460B2BE1943CF7600A5D298 /* Images.xcassets */, 183 | D460B2B51943CF7600A5D298 /* Supporting Files */, 184 | ); 185 | path = GestureKeyboard; 186 | sourceTree = ""; 187 | }; 188 | D460B2B51943CF7600A5D298 /* Supporting Files */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | D460B2B61943CF7600A5D298 /* Info.plist */, 192 | ); 193 | name = "Supporting Files"; 194 | sourceTree = ""; 195 | }; 196 | D460B2C71943CF7700A5D298 /* GestureKeyboardTests */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | D460B2CA1943CF7700A5D298 /* GestureKeyboardTests.swift */, 200 | D460B2C81943CF7700A5D298 /* Supporting Files */, 201 | ); 202 | path = GestureKeyboardTests; 203 | sourceTree = ""; 204 | }; 205 | D460B2C81943CF7700A5D298 /* Supporting Files */ = { 206 | isa = PBXGroup; 207 | children = ( 208 | D460B2C91943CF7700A5D298 /* Info.plist */, 209 | ); 210 | name = "Supporting Files"; 211 | sourceTree = ""; 212 | }; 213 | D460B2D41944C09900A5D298 /* CMUnistrokeGestureRecognizer */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | D460B2D51944C09900A5D298 /* CMUnistrokeGestureRecognizer-Prefix.pch */, 217 | D460B2D61944C09900A5D298 /* CMUnistrokeGestureRecognizer.h */, 218 | D460B2D71944C09900A5D298 /* CMUnistrokeGestureRecognizer.m */, 219 | D460B2D81944C09900A5D298 /* CMUnistrokeGestureResult.h */, 220 | D460B2D91944C09900A5D298 /* CMUnistrokeGestureResult.m */, 221 | D460B2DA1944C09900A5D298 /* CMUnistrokeRecognizer */, 222 | ); 223 | path = CMUnistrokeGestureRecognizer; 224 | sourceTree = ""; 225 | }; 226 | D460B2DA1944C09900A5D298 /* CMUnistrokeRecognizer */ = { 227 | isa = PBXGroup; 228 | children = ( 229 | D460B2DB1944C09900A5D298 /* CMUnistrokeRecognizer.c */, 230 | D460B2DC1944C09900A5D298 /* CMUnistrokeRecognizer.h */, 231 | D460B2DD1944C09900A5D298 /* CMUnistrokeRecognizerPrivate.h */, 232 | D460B2DE1944C09900A5D298 /* CMUnistrokeRecognizerTypes.c */, 233 | D460B2DF1944C09900A5D298 /* CMUnistrokeRecognizerTypes.h */, 234 | ); 235 | path = CMUnistrokeRecognizer; 236 | sourceTree = ""; 237 | }; 238 | D49E328A19454F84001B9BB1 /* GestureKeyboardExtension */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | D49E328D19454F84001B9BB1 /* KeyboardViewController.swift */, 242 | D49E328B19454F84001B9BB1 /* Supporting Files */, 243 | ); 244 | path = GestureKeyboardExtension; 245 | sourceTree = ""; 246 | }; 247 | D49E328B19454F84001B9BB1 /* Supporting Files */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | D49E328C19454F84001B9BB1 /* Info.plist */, 251 | ); 252 | name = "Supporting Files"; 253 | sourceTree = ""; 254 | }; 255 | /* End PBXGroup section */ 256 | 257 | /* Begin PBXNativeTarget section */ 258 | D460B2B11943CF7600A5D298 /* GestureKeyboard */ = { 259 | isa = PBXNativeTarget; 260 | buildConfigurationList = D460B2CE1943CF7700A5D298 /* Build configuration list for PBXNativeTarget "GestureKeyboard" */; 261 | buildPhases = ( 262 | D460B2AE1943CF7600A5D298 /* Sources */, 263 | D460B2AF1943CF7600A5D298 /* Frameworks */, 264 | D460B2B01943CF7600A5D298 /* Resources */, 265 | D49E329119454F84001B9BB1 /* Embed App Extensions */, 266 | ); 267 | buildRules = ( 268 | ); 269 | dependencies = ( 270 | D49E329019454F84001B9BB1 /* PBXTargetDependency */, 271 | D49E329419454F84001B9BB1 /* PBXTargetDependency */, 272 | ); 273 | name = GestureKeyboard; 274 | productName = GestureKeyboard; 275 | productReference = D460B2B21943CF7600A5D298 /* GestureKeyboard.app */; 276 | productType = "com.apple.product-type.application"; 277 | }; 278 | D460B2C31943CF7700A5D298 /* GestureKeyboardTests */ = { 279 | isa = PBXNativeTarget; 280 | buildConfigurationList = D460B2D11943CF7700A5D298 /* Build configuration list for PBXNativeTarget "GestureKeyboardTests" */; 281 | buildPhases = ( 282 | D460B2C01943CF7700A5D298 /* Sources */, 283 | D460B2C11943CF7700A5D298 /* Frameworks */, 284 | D460B2C21943CF7700A5D298 /* Resources */, 285 | ); 286 | buildRules = ( 287 | ); 288 | dependencies = ( 289 | D460B2C61943CF7700A5D298 /* PBXTargetDependency */, 290 | ); 291 | name = GestureKeyboardTests; 292 | productName = GestureKeyboardTests; 293 | productReference = D460B2C41943CF7700A5D298 /* GestureKeyboardTests.xctest */; 294 | productType = "com.apple.product-type.bundle.unit-test"; 295 | }; 296 | D49E328819454F84001B9BB1 /* GestureKeyboardExtension */ = { 297 | isa = PBXNativeTarget; 298 | buildConfigurationList = D49E329519454F84001B9BB1 /* Build configuration list for PBXNativeTarget "GestureKeyboardExtension" */; 299 | buildPhases = ( 300 | D49E328519454F84001B9BB1 /* Sources */, 301 | D49E328619454F84001B9BB1 /* Frameworks */, 302 | D49E328719454F84001B9BB1 /* Resources */, 303 | ); 304 | buildRules = ( 305 | ); 306 | dependencies = ( 307 | ); 308 | name = GestureKeyboardExtension; 309 | productName = GestureKeyboardExtension; 310 | productReference = D49E328919454F84001B9BB1 /* com.touchcentric.GestureKeyboard.GestureKeyboardExtension.appex */; 311 | productType = "com.apple.product-type.app-extension"; 312 | }; 313 | /* End PBXNativeTarget section */ 314 | 315 | /* Begin PBXProject section */ 316 | D460B2AA1943CF7600A5D298 /* Project object */ = { 317 | isa = PBXProject; 318 | attributes = { 319 | LastUpgradeCheck = 0600; 320 | ORGANIZATIONNAME = TouchCentric; 321 | TargetAttributes = { 322 | D460B2B11943CF7600A5D298 = { 323 | CreatedOnToolsVersion = 6.0; 324 | }; 325 | D460B2C31943CF7700A5D298 = { 326 | CreatedOnToolsVersion = 6.0; 327 | TestTargetID = D460B2B11943CF7600A5D298; 328 | }; 329 | D49E328819454F84001B9BB1 = { 330 | CreatedOnToolsVersion = 6.0; 331 | }; 332 | }; 333 | }; 334 | buildConfigurationList = D460B2AD1943CF7600A5D298 /* Build configuration list for PBXProject "GestureKeyboard" */; 335 | compatibilityVersion = "Xcode 3.2"; 336 | developmentRegion = English; 337 | hasScannedForEncodings = 0; 338 | knownRegions = ( 339 | en, 340 | Base, 341 | ); 342 | mainGroup = D460B2A91943CF7600A5D298; 343 | productRefGroup = D460B2B31943CF7600A5D298 /* Products */; 344 | projectDirPath = ""; 345 | projectRoot = ""; 346 | targets = ( 347 | D460B2B11943CF7600A5D298 /* GestureKeyboard */, 348 | D460B2C31943CF7700A5D298 /* GestureKeyboardTests */, 349 | D49E328819454F84001B9BB1 /* GestureKeyboardExtension */, 350 | ); 351 | }; 352 | /* End PBXProject section */ 353 | 354 | /* Begin PBXResourcesBuildPhase section */ 355 | D460B2B01943CF7600A5D298 /* Resources */ = { 356 | isa = PBXResourcesBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | D460B2BD1943CF7600A5D298 /* Main.storyboard in Resources */, 360 | D460B2BF1943CF7600A5D298 /* Images.xcassets in Resources */, 361 | D49E32B41946E176001B9BB1 /* graffiti_strokes.jpeg in Resources */, 362 | D4B4ABBD1947520800052189 /* graffiti.plist in Resources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | D460B2C21943CF7700A5D298 /* Resources */ = { 367 | isa = PBXResourcesBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | ); 371 | runOnlyForDeploymentPostprocessing = 0; 372 | }; 373 | D49E328719454F84001B9BB1 /* Resources */ = { 374 | isa = PBXResourcesBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | ); 378 | runOnlyForDeploymentPostprocessing = 0; 379 | }; 380 | /* End PBXResourcesBuildPhase section */ 381 | 382 | /* Begin PBXSourcesBuildPhase section */ 383 | D460B2AE1943CF7600A5D298 /* Sources */ = { 384 | isa = PBXSourcesBuildPhase; 385 | buildActionMask = 2147483647; 386 | files = ( 387 | D460B2BA1943CF7600A5D298 /* ViewController.swift in Sources */, 388 | D460B2E21944C09900A5D298 /* CMUnistrokeRecognizer.c in Sources */, 389 | D460B2E31944C09900A5D298 /* CMUnistrokeRecognizerTypes.c in Sources */, 390 | D460B2E01944C09900A5D298 /* CMUnistrokeGestureRecognizer.m in Sources */, 391 | D460B2B81943CF7600A5D298 /* AppDelegate.swift in Sources */, 392 | D460B2E11944C09900A5D298 /* CMUnistrokeGestureResult.m in Sources */, 393 | ); 394 | runOnlyForDeploymentPostprocessing = 0; 395 | }; 396 | D460B2C01943CF7700A5D298 /* Sources */ = { 397 | isa = PBXSourcesBuildPhase; 398 | buildActionMask = 2147483647; 399 | files = ( 400 | D460B2CB1943CF7700A5D298 /* GestureKeyboardTests.swift in Sources */, 401 | ); 402 | runOnlyForDeploymentPostprocessing = 0; 403 | }; 404 | D49E328519454F84001B9BB1 /* Sources */ = { 405 | isa = PBXSourcesBuildPhase; 406 | buildActionMask = 2147483647; 407 | files = ( 408 | D49E329B194552DA001B9BB1 /* CMUnistrokeRecognizerTypes.c in Sources */, 409 | D49E329A194552CB001B9BB1 /* CMUnistrokeRecognizer.c in Sources */, 410 | D49E328E19454F84001B9BB1 /* KeyboardViewController.swift in Sources */, 411 | D49E3299194552C6001B9BB1 /* CMUnistrokeGestureRecognizer.m in Sources */, 412 | D49E3298194552C1001B9BB1 /* CMUnistrokeGestureResult.m in Sources */, 413 | ); 414 | runOnlyForDeploymentPostprocessing = 0; 415 | }; 416 | /* End PBXSourcesBuildPhase section */ 417 | 418 | /* Begin PBXTargetDependency section */ 419 | D460B2C61943CF7700A5D298 /* PBXTargetDependency */ = { 420 | isa = PBXTargetDependency; 421 | target = D460B2B11943CF7600A5D298 /* GestureKeyboard */; 422 | targetProxy = D460B2C51943CF7700A5D298 /* PBXContainerItemProxy */; 423 | }; 424 | D49E329019454F84001B9BB1 /* PBXTargetDependency */ = { 425 | isa = PBXTargetDependency; 426 | target = D49E328819454F84001B9BB1 /* GestureKeyboardExtension */; 427 | targetProxy = D49E328F19454F84001B9BB1 /* PBXContainerItemProxy */; 428 | }; 429 | D49E329419454F84001B9BB1 /* PBXTargetDependency */ = { 430 | isa = PBXTargetDependency; 431 | target = D49E328819454F84001B9BB1 /* GestureKeyboardExtension */; 432 | targetProxy = D49E329319454F84001B9BB1 /* PBXContainerItemProxy */; 433 | }; 434 | /* End PBXTargetDependency section */ 435 | 436 | /* Begin PBXVariantGroup section */ 437 | D460B2BB1943CF7600A5D298 /* Main.storyboard */ = { 438 | isa = PBXVariantGroup; 439 | children = ( 440 | D460B2BC1943CF7600A5D298 /* Base */, 441 | ); 442 | name = Main.storyboard; 443 | sourceTree = ""; 444 | }; 445 | /* End PBXVariantGroup section */ 446 | 447 | /* Begin XCBuildConfiguration section */ 448 | D460B2CC1943CF7700A5D298 /* Debug */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | ALWAYS_SEARCH_USER_PATHS = NO; 452 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 453 | CLANG_CXX_LIBRARY = "libc++"; 454 | CLANG_ENABLE_MODULES = NO; 455 | CLANG_ENABLE_OBJC_ARC = YES; 456 | CLANG_WARN_BOOL_CONVERSION = YES; 457 | CLANG_WARN_CONSTANT_CONVERSION = YES; 458 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 459 | CLANG_WARN_EMPTY_BODY = YES; 460 | CLANG_WARN_ENUM_CONVERSION = YES; 461 | CLANG_WARN_INT_CONVERSION = YES; 462 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 463 | CLANG_WARN_UNREACHABLE_CODE = YES; 464 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 465 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 466 | COPY_PHASE_STRIP = NO; 467 | ENABLE_STRICT_OBJC_MSGSEND = YES; 468 | GCC_C_LANGUAGE_STANDARD = "compiler-default"; 469 | GCC_DYNAMIC_NO_PIC = NO; 470 | GCC_OPTIMIZATION_LEVEL = 0; 471 | GCC_PREPROCESSOR_DEFINITIONS = ( 472 | "DEBUG=1", 473 | "$(inherited)", 474 | ); 475 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 476 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 477 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 478 | GCC_WARN_UNDECLARED_SELECTOR = YES; 479 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 480 | GCC_WARN_UNUSED_FUNCTION = YES; 481 | GCC_WARN_UNUSED_VARIABLE = YES; 482 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 483 | METAL_ENABLE_DEBUG_INFO = YES; 484 | ONLY_ACTIVE_ARCH = YES; 485 | PRODUCT_MODULE_NAME = GestureKeyboard; 486 | SDKROOT = iphoneos8.0; 487 | SWIFT_OBJC_BRIDGING_HEADER = "CMUnistrokeGestureRecognizer/GestureKeyboard-Bridging-Header.h"; 488 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 489 | TARGETED_DEVICE_FAMILY = "1,2"; 490 | }; 491 | name = Debug; 492 | }; 493 | D460B2CD1943CF7700A5D298 /* Release */ = { 494 | isa = XCBuildConfiguration; 495 | buildSettings = { 496 | ALWAYS_SEARCH_USER_PATHS = NO; 497 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 498 | CLANG_CXX_LIBRARY = "libc++"; 499 | CLANG_ENABLE_MODULES = NO; 500 | CLANG_ENABLE_OBJC_ARC = YES; 501 | CLANG_WARN_BOOL_CONVERSION = YES; 502 | CLANG_WARN_CONSTANT_CONVERSION = YES; 503 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 504 | CLANG_WARN_EMPTY_BODY = YES; 505 | CLANG_WARN_ENUM_CONVERSION = YES; 506 | CLANG_WARN_INT_CONVERSION = YES; 507 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 508 | CLANG_WARN_UNREACHABLE_CODE = YES; 509 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 510 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 511 | COPY_PHASE_STRIP = YES; 512 | ENABLE_NS_ASSERTIONS = NO; 513 | ENABLE_STRICT_OBJC_MSGSEND = YES; 514 | GCC_C_LANGUAGE_STANDARD = "compiler-default"; 515 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 516 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 517 | GCC_WARN_UNDECLARED_SELECTOR = YES; 518 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 519 | GCC_WARN_UNUSED_FUNCTION = YES; 520 | GCC_WARN_UNUSED_VARIABLE = YES; 521 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 522 | METAL_ENABLE_DEBUG_INFO = NO; 523 | ONLY_ACTIVE_ARCH = YES; 524 | PRODUCT_MODULE_NAME = GestureKeyboard; 525 | SDKROOT = iphoneos8.0; 526 | SWIFT_OBJC_BRIDGING_HEADER = "CMUnistrokeGestureRecognizer/GestureKeyboard-Bridging-Header.h"; 527 | TARGETED_DEVICE_FAMILY = "1,2"; 528 | VALIDATE_PRODUCT = YES; 529 | }; 530 | name = Release; 531 | }; 532 | D460B2CF1943CF7700A5D298 /* Debug */ = { 533 | isa = XCBuildConfiguration; 534 | buildSettings = { 535 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 536 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 537 | INFOPLIST_FILE = GestureKeyboard/Info.plist; 538 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | }; 541 | name = Debug; 542 | }; 543 | D460B2D01943CF7700A5D298 /* Release */ = { 544 | isa = XCBuildConfiguration; 545 | buildSettings = { 546 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 547 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 548 | INFOPLIST_FILE = GestureKeyboard/Info.plist; 549 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 550 | PRODUCT_NAME = "$(TARGET_NAME)"; 551 | }; 552 | name = Release; 553 | }; 554 | D460B2D21943CF7700A5D298 /* Debug */ = { 555 | isa = XCBuildConfiguration; 556 | buildSettings = { 557 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/GestureKeyboard.app/GestureKeyboard"; 558 | FRAMEWORK_SEARCH_PATHS = ( 559 | "$(SDKROOT)/Developer/Library/Frameworks", 560 | "$(inherited)", 561 | ); 562 | GCC_PREPROCESSOR_DEFINITIONS = ( 563 | "DEBUG=1", 564 | "$(inherited)", 565 | ); 566 | INFOPLIST_FILE = GestureKeyboardTests/Info.plist; 567 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 568 | METAL_ENABLE_DEBUG_INFO = YES; 569 | PRODUCT_NAME = "$(TARGET_NAME)"; 570 | TEST_HOST = "$(BUNDLE_LOADER)"; 571 | }; 572 | name = Debug; 573 | }; 574 | D460B2D31943CF7700A5D298 /* Release */ = { 575 | isa = XCBuildConfiguration; 576 | buildSettings = { 577 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/GestureKeyboard.app/GestureKeyboard"; 578 | FRAMEWORK_SEARCH_PATHS = ( 579 | "$(SDKROOT)/Developer/Library/Frameworks", 580 | "$(inherited)", 581 | ); 582 | INFOPLIST_FILE = GestureKeyboardTests/Info.plist; 583 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 584 | METAL_ENABLE_DEBUG_INFO = NO; 585 | PRODUCT_NAME = "$(TARGET_NAME)"; 586 | TEST_HOST = "$(BUNDLE_LOADER)"; 587 | }; 588 | name = Release; 589 | }; 590 | D49E329619454F84001B9BB1 /* Debug */ = { 591 | isa = XCBuildConfiguration; 592 | buildSettings = { 593 | CLANG_ENABLE_MODULES = NO; 594 | GCC_C_LANGUAGE_STANDARD = gnu99; 595 | GCC_PREPROCESSOR_DEFINITIONS = ( 596 | "DEBUG=1", 597 | "$(inherited)", 598 | ); 599 | INFOPLIST_FILE = GestureKeyboardExtension/Info.plist; 600 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 601 | METAL_ENABLE_DEBUG_INFO = YES; 602 | PRODUCT_NAME = "com.touchcentric.GestureKeyboard.$(TARGET_NAME:rfc1034identifier)"; 603 | SKIP_INSTALL = YES; 604 | }; 605 | name = Debug; 606 | }; 607 | D49E329719454F84001B9BB1 /* Release */ = { 608 | isa = XCBuildConfiguration; 609 | buildSettings = { 610 | CLANG_ENABLE_MODULES = NO; 611 | GCC_C_LANGUAGE_STANDARD = gnu99; 612 | INFOPLIST_FILE = GestureKeyboardExtension/Info.plist; 613 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 614 | METAL_ENABLE_DEBUG_INFO = NO; 615 | PRODUCT_NAME = "com.touchcentric.GestureKeyboard.$(TARGET_NAME:rfc1034identifier)"; 616 | SKIP_INSTALL = YES; 617 | }; 618 | name = Release; 619 | }; 620 | /* End XCBuildConfiguration section */ 621 | 622 | /* Begin XCConfigurationList section */ 623 | D460B2AD1943CF7600A5D298 /* Build configuration list for PBXProject "GestureKeyboard" */ = { 624 | isa = XCConfigurationList; 625 | buildConfigurations = ( 626 | D460B2CC1943CF7700A5D298 /* Debug */, 627 | D460B2CD1943CF7700A5D298 /* Release */, 628 | ); 629 | defaultConfigurationIsVisible = 0; 630 | defaultConfigurationName = Release; 631 | }; 632 | D460B2CE1943CF7700A5D298 /* Build configuration list for PBXNativeTarget "GestureKeyboard" */ = { 633 | isa = XCConfigurationList; 634 | buildConfigurations = ( 635 | D460B2CF1943CF7700A5D298 /* Debug */, 636 | D460B2D01943CF7700A5D298 /* Release */, 637 | ); 638 | defaultConfigurationIsVisible = 0; 639 | defaultConfigurationName = Release; 640 | }; 641 | D460B2D11943CF7700A5D298 /* Build configuration list for PBXNativeTarget "GestureKeyboardTests" */ = { 642 | isa = XCConfigurationList; 643 | buildConfigurations = ( 644 | D460B2D21943CF7700A5D298 /* Debug */, 645 | D460B2D31943CF7700A5D298 /* Release */, 646 | ); 647 | defaultConfigurationIsVisible = 0; 648 | defaultConfigurationName = Release; 649 | }; 650 | D49E329519454F84001B9BB1 /* Build configuration list for PBXNativeTarget "GestureKeyboardExtension" */ = { 651 | isa = XCConfigurationList; 652 | buildConfigurations = ( 653 | D49E329619454F84001B9BB1 /* Debug */, 654 | D49E329719454F84001B9BB1 /* Release */, 655 | ); 656 | defaultConfigurationIsVisible = 0; 657 | defaultConfigurationName = Release; 658 | }; 659 | /* End XCConfigurationList section */ 660 | }; 661 | rootObject = D460B2AA1943CF7600A5D298 /* Project object */; 662 | } 663 | -------------------------------------------------------------------------------- /GestureKeyboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GestureKeyboard/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GestureKeyboard 4 | // 5 | // Created by Rob Terrell on 6/7/14. 6 | // Copyright (c) 2014 TouchCentric. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /GestureKeyboard/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 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /GestureKeyboard/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /GestureKeyboard/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /GestureKeyboard/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.touchcentric.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | 35 | NSExtension 36 | 37 | NSExtensionAttributes 38 | 39 | IsASCIICapable 40 | 41 | PrefersRightToLeft 42 | 43 | PrimaryLanguage 44 | en-US 45 | RequestsOpenAccess 46 | 47 | 48 | NSExtensionPointIdentifier 49 | com.apple.keyboard-service 50 | NSExtensionPrincipalClass 51 | KeyboardViewController 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /GestureKeyboard/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // GestureKeyboard 4 | // 5 | // Created by Rob Terrell on 6/7/14. 6 | // Copyright (c) 2014 TouchCentric. All rights reserved. 7 | // 8 | 9 | // Note: this is the main view controller for the app -- NOT for the keyboard. 10 | 11 | import UIKit 12 | 13 | class ViewController: UIViewController { 14 | 15 | @IBOutlet var imageView : UIImageView 16 | 17 | @IBOutlet var label : UILabel 18 | 19 | var plist:String = NSBundle.mainBundle().pathForResource("graffiti", ofType: "plist") 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | var a = CMUnistrokeGestureRecognizer(target: self, action: "gotGesture:" ) 25 | var dict = NSDictionary(contentsOfFile: plist) // COMPILER BUG: as Dictionary 26 | var keys:String[] = dict.allKeys as String[] 27 | 28 | for letter:String in keys { 29 | var path = UIBezierPath() 30 | var first = true 31 | var points = dict[letter] as String[] 32 | for p in points { 33 | var cgp = CGPointFromString(p) 34 | if (first) { 35 | path.moveToPoint(cgp) 36 | first = false 37 | } else { 38 | path.addLineToPoint(cgp) 39 | } 40 | } 41 | path.closePath(); 42 | a.registerUnistrokeWithName( letter, bezierPath: path) 43 | } 44 | 45 | 46 | var drawView = UIView() as UIView 47 | drawView.frame = CGRectMake(0, 0, 120, 120) 48 | drawView.center = self.view.center 49 | drawView.center.y = drawView.center.y + 180 50 | drawView.backgroundColor = UIColor.darkGrayColor() 51 | // var drawViewBottomConstraint = NSLayoutConstraint(item: self.imageView, attribute: .Top, relatedBy: .Equal, toItem: drawView, attribute: .Bottom, multiplier: 1.0, constant: 0.0) 52 | // self.view.addConstraint( drawViewBottomConstraint ) 53 | 54 | self.view.addSubview(drawView) 55 | self.view.addGestureRecognizer(a) 56 | } 57 | 58 | func gotGesture( r : CMUnistrokeGestureRecognizer ) { 59 | var path = r.strokePath 60 | var name = r.result.recognizedStrokeName 61 | 62 | println("gotGesture: \(name)") 63 | 64 | label.text = label.text + name; 65 | 66 | 67 | } 68 | 69 | override func didReceiveMemoryWarning() { 70 | super.didReceiveMemoryWarning() 71 | // Dispose of any resources that can be recreated. 72 | } 73 | 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /GestureKeyboard/graffiti_strokes.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robterrell/graffiti-keyboard/5b01d016070b9a892a993e9a6a1b81d290384799/GestureKeyboard/graffiti_strokes.jpeg -------------------------------------------------------------------------------- /GestureKeyboardExtension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Graffiti 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | ${PRODUCT_NAME} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | IsASCIICapable 30 | 31 | PrefersRightToLeft 32 | 33 | PrimaryLanguage 34 | en-US 35 | RequestsOpenAccess 36 | 37 | 38 | NSExtensionPointIdentifier 39 | com.apple.keyboard-service 40 | NSExtensionPrincipalClass 41 | ${PRODUCT_MODULE_NAME}.KeyboardViewController 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /GestureKeyboardExtension/KeyboardViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardViewController.swift 3 | // GestureKeyboardExtension 4 | // 5 | // Created by Rob Terrell on 6/8/14. 6 | // Copyright (c) 2014 TouchCentric. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class KeyboardViewController: UIInputViewController { 12 | 13 | @IBOutlet var nextKeyboardButton: UIButton 14 | var drawView : UIView = UIView() 15 | 16 | init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { 17 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 18 | NSLog("Keyboard View Controller init") 19 | } 20 | 21 | override func updateViewConstraints() { 22 | super.updateViewConstraints() 23 | 24 | // Add custom view sizing constraints here 25 | } 26 | override func viewDidAppear(animated: Bool) { 27 | NSLog("viewDidAppear \(self.drawView.frame), \(self.drawView.superview.frame)") 28 | self.drawView.center = self.view.center 29 | } 30 | 31 | override func viewDidLoad() { 32 | NSLog("Keyboard View Conroller viewDidLoad") 33 | 34 | super.viewDidLoad() 35 | 36 | NSLog("Keyboard View Conroller view \(self.view)") 37 | 38 | // Perform custom UI setup here 39 | self.nextKeyboardButton = UIButton.buttonWithType(.System) as UIButton 40 | self.nextKeyboardButton.setTitle(NSLocalizedString("Next Keyboard!", comment: "Title for 'Next Keyboard' button"), forState: .Normal) 41 | self.nextKeyboardButton.sizeToFit() 42 | self.nextKeyboardButton.setTranslatesAutoresizingMaskIntoConstraints(false) 43 | self.nextKeyboardButton.addTarget(self, action: "advanceToNextInputMode", forControlEvents: .TouchUpInside) 44 | 45 | self.view.addSubview(self.nextKeyboardButton) 46 | 47 | var nextKeyboardButtonLeftSideConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 0.0) 48 | var nextKeyboardButtonBottomConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 7.0) 49 | self.view.addConstraints([nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint]) 50 | 51 | drawView.frame = CGRectMake(0, 0, 180, 180) 52 | drawView.backgroundColor = UIColor.darkGrayColor() 53 | drawView.center = self.view.center 54 | drawView.layer.cornerRadius = 5.0 55 | self.view.addSubview(drawView) 56 | 57 | var drawViewVertConstraint = NSLayoutConstraint(item: self.drawView, attribute: .CenterY, relatedBy: .Equal, toItem: self.drawView.superview, attribute: .CenterY, multiplier: 1.0, constant: 0.0) 58 | 59 | var drawViewHorizConstraint = NSLayoutConstraint(item: self.drawView, attribute: .CenterX, relatedBy: .Equal, toItem: self.nextKeyboardButton.superview, attribute: .CenterX, multiplier: 1.0, constant: 0.0) 60 | 61 | self.view.addConstraints( [drawViewVertConstraint, drawViewHorizConstraint] ) 62 | 63 | var a = CMUnistrokeGestureRecognizer(target: self, action: "gotGesture:" ) 64 | 65 | var plist = NSBundle.mainBundle().pathForResource("graffiti", ofType: "plist") 66 | var dict = NSDictionary(contentsOfFile: plist) // COMPILER BUG: as Dictionary 67 | var keys:String[] = dict.allKeys as String[] 68 | 69 | for letter:String in keys { 70 | var path = UIBezierPath() 71 | var first = true 72 | var points = dict[letter] as String[] 73 | for p in points { 74 | var cgp = CGPointFromString(p) 75 | if (first) { 76 | path.moveToPoint(cgp) 77 | first = false 78 | } else { 79 | path.addLineToPoint(cgp) 80 | } 81 | } 82 | path.closePath(); 83 | a.registerUnistrokeWithName( letter, bezierPath: path) 84 | } 85 | 86 | drawView.addGestureRecognizer(a) 87 | 88 | } 89 | 90 | func gotGesture( r : CMUnistrokeGestureRecognizer ) { 91 | var path = r.strokePath 92 | var name = r.result.recognizedStrokeName 93 | 94 | println("gotGesture \(name)") 95 | 96 | var doc : UIKeyInput = self.textDocumentProxy as UIKeyInput 97 | 98 | if name == "delete" { 99 | doc.deleteBackward() 100 | } else { 101 | doc.insertText(name) 102 | } 103 | } 104 | 105 | override func didReceiveMemoryWarning() { 106 | super.didReceiveMemoryWarning() 107 | // Dispose of any resources that can be recreated 108 | } 109 | 110 | override func textWillChange(textInput: UITextInput) { 111 | // The app is about to change the document's contents. Perform any preparation here. 112 | } 113 | 114 | override func textDidChange(textInput: UITextInput) { 115 | // The app has just changed the document's contents, the document context has been updated. 116 | 117 | var textColor: UIColor 118 | var proxy = self.textDocumentProxy as UITextDocumentProxy 119 | if proxy.keyboardAppearance == UIKeyboardAppearance.Dark { 120 | textColor = UIColor.whiteColor() 121 | } else { 122 | textColor = UIColor.blackColor() 123 | } 124 | self.nextKeyboardButton.setTitleColor(textColor, forState: .Normal) 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /GestureKeyboardTests/GestureKeyboardTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GestureKeyboardTests.swift 3 | // GestureKeyboardTests 4 | // 5 | // Created by Rob Terrell on 6/7/14. 6 | // Copyright (c) 2014 TouchCentric. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class GestureKeyboardTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | XCTAssert(true, "Pass") 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /GestureKeyboardTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.touchcentric.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /graffiti.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | a 6 | 7 | {0.5, 4.5} 8 | {2.5, 0.5} 9 | {4.5, 4.5} 10 | 11 | b 12 | 13 | {1.0, 0.5} 14 | {1.0, 4.5} 15 | {1.5, 0.75} 16 | {3.0, 0.5} 17 | {3.5, 1.5} 18 | {2.5, 2} 19 | {1.5, 2.5} 20 | {3.0, 3.0} 21 | {3.5, 4} 22 | {3.0, 4.5} 23 | {2.0, 4.5) 24 | 25 | c 26 | 27 | {4.0, 1.0} 28 | {1.0, 1.0} 29 | {1.0, 4.0} 30 | {4.0, 4.0} 31 | 32 | d 33 | 34 | {1.0, 0.75} 35 | {1.0, 4.5} 36 | {1.25, 1.0} 37 | {3.0, 0.5} 38 | {4.0, 1.0} 39 | {4.5, 2.5} 40 | {4.0, 3.5} 41 | {2.5, 4.5} 42 | {1.0, 4.5) 43 | 44 | e 45 | 46 | {4.0, 1.0} 47 | {3.0, 0.5} 48 | {2.0, 0.5} 49 | {1.0, 1.5} 50 | {2.5, 2.5} 51 | {1.0, 3.5} 52 | {2.0, 4.5} 53 | {3.0, 4.5} 54 | {4.0, 4.0} 55 | 56 | f 57 | 58 | {1.0, 4.0} 59 | {1.0, 1.0} 60 | {4.0, 1.0} 61 | 62 | g 63 | 64 | {4.0, 1.5} 65 | {3.0, 0.5} 66 | {2.0, 0.5} 67 | {1.0, 1.5} 68 | {1.0, 3.5} 69 | {2.0, 4.5} 70 | {4.0, 4.5} 71 | {4.0, 3.0} 72 | {3.0, 3.0} 73 | 74 | h 75 | 76 | {1.0, 0.5} 77 | {1.0, 4.0} 78 | {1.1, 2.0} 79 | {3.0, 2.0} 80 | {4.0, 3.0} 81 | 82 | i 83 | 84 | {3.0, 0.5} 85 | {3.2, 1.0} 86 | {3.0, 1.5} 87 | {3.2, 2.0} 88 | {3.0, 2.5} 89 | {3.2, 3.0} 90 | {3.0, 3.5} 91 | {3.2, 4.0} 92 | {3.0, 4.5} 93 | 94 | j 95 | 96 | {4.0, 0.5} 97 | {4.0, 4.5} 98 | {1.0, 4.5} 99 | 100 | k 101 | 102 | {4.0, 1.0} 103 | {1.0, 4.0} 104 | {1.0, 1.0} 105 | {4.0, 4.0} 106 | 107 | l 108 | 109 | {1.0, 1.0} 110 | {1.1, 4.0} 111 | {4.0, 4.0} 112 | 113 | m 114 | 115 | {1.0, 4.0} 116 | {1.0, 1.0} 117 | {2.5, 2.0} 118 | {4.0, 1.0} 119 | {4.0, 4.0} 120 | 121 | n 122 | 123 | {1.0, 4.0} 124 | {1.1, 1.0} 125 | {4.0, 4.0} 126 | {4.0, 1.0} 127 | 128 | o 129 | 130 | {2.0, 1.0} 131 | {1.0, 2.0} 132 | {1.0, 3.0} 133 | {2.0, 4.0} 134 | {3.0, 4.0} 135 | {4.0, 3.0} 136 | {4.0, 2.0} 137 | {3.0, 1.0} 138 | 139 | p 140 | 141 | {0.8, 1.0} 142 | {1.0, 4.5} 143 | {1.0, 1.0} 144 | {2.0, 1.0} 145 | {3.0, 2.0} 146 | {2.0, 3.0} 147 | {1.0, 3.0} 148 | 149 | 150 | 151 | {1.5, 3.0} 152 | {2.0, 3.1} 153 | {2.5, 3.1} 154 | {3.0, 3.0} 155 | {3.5, 3.0} 156 | {4.0, 3.1} 157 | 158 | delete 159 | 160 | {3.5, 3.2} 161 | {3.0, 3.1} 162 | {2.5, 3.0} 163 | {2.0, 3.1} 164 | {1.5, 3.0} 165 | 166 | 167 | 168 | --------------------------------------------------------------------------------