├── .gitignore ├── LICENSE ├── README.md ├── TouchVisualizer ├── ALG3DTouchThreshholdGestureRecognizer.swift ├── ALGSqueezeGestureRecognizer.swift ├── ForcePropertiesGestureRecognizer │ ├── ALGInitialTouchGestureRecognizer.swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── TouchVisualizer.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── alexis.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ ├── xcshareddata │ │ └── xcschemes │ │ │ ├── ForceDataCollector.xcscheme │ │ │ └── TouchVisualizer.xcscheme │ └── xcuserdata │ │ └── alexis.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist └── TouchVisualizer │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── TouchDisplayingWindow.swift │ └── ViewController.swift └── data and analysis ├── TouchDataAnalysis.playground ├── Contents.swift ├── Resources │ └── 04317CA4-3AD0-4776-861E-78040D8BC3E2.json ├── Sources │ ├── Analysis.swift │ └── ParsedData.swift ├── contents.xcplayground ├── playground.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── alexis.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── timeline.xctimeline ├── data-60secondsVariedForce ├── .ipynb_checkpoints │ └── Untitled-checkpoint.ipynb ├── 04317CA4-3AD0-4776-861E-78040D8BC3E2.json └── DataAndCharts.nb └── data-weightMeasurements ├── weight.xlsx └── weightCSV.csv /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2017 Alexis Gallagher, http://alexisgallagher.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 3D Touch visualizer and gesture recognizers 2 | 3 | Hello, world. What is this? 4 | 5 | This repo contains a few components related to the wonderful world of 3D Touch on iPhones. This is the code and data behind [my talk on 3D Touch](https://realm.io/news/alexis-gallagher-3d-touch-swift/). 6 | 7 | Useful bits are, perhaps, the force-visualization overlay window, and the gesture recognizers for detecting force. 8 | 9 | This repo also has the data I gathered studying the force properties API, and the test app I wrote for gathering that data. 10 | 11 | To summarize, it looks like 3D Touch reports 400 distinct force values, which are highly accurate and map linearly to actual physical force. The maximum force value reported by the API is about 0.5 kg. (However, this is less than the actual maximum force value the device recognizes, since you can see Apple is using a higher degree of force to trigger the pop behavior.) 12 | 13 | Here are items in the repo: 14 | 15 | ## TouchDisplayingWindow 16 | 17 | This is like `UIWindow`, but it displays an overlay view displaying active touches with force annotations. To run it, run the `TouchVisualizer` target in the Xcode project. 18 | 19 | You can add this class to an existing app for debugging purposes, or merely to dazzle and frighten. 20 | 21 | There are two steps to using this: (1) configure your app to use this instead of UIWindow and (2) ensure to deactive it on devices that do not offer force properties API 22 | 23 | To use this instead of UIWindow, override your app delegate's `window:UIWindow?` property with a computed property, where the setter's a no-op, and the getter returns a constant reference to an instance of this class. 24 | 25 | To de-activate on non-force devices, set `forceActive=false`. Accessing force information on devices without force capability is "undefined" so this precuation is pedantically needed for defined behavior. 26 | 27 | This class should not affect normal touch delivery at all. 28 | 29 | This class tries to keep its own overlay subview in front but it does not take heroic measures to do so. So I'm not sure if this works in complex cases. It might fail if user code or system code does not anticipate another component modifying the existence or order of the key window's subviews. 30 | 31 | ## ALGSqueezeGestureRecognizer 32 | 33 | This is a simple discrete gesture recognizer that detects any `UITouch.force` beyond a certain threshhold 34 | 35 | ## ALG3DTouchThreshholdGestureRecognizer 36 | 37 | This is a more elaborate continuous gesture recognizer, which I am hoping is the last force gesture recognizer I will need. 38 | 39 | It can be configured just to report all force changes. Or you can configure it with a set of "force threshholds" and then it will report every time the observed force reaches or crosses a threshhold. As long as one is only interested in single-touch gestures, this should meet most purposes. 40 | 41 | This is probably a bit overengineered at the moment. I may refactor it later. 42 | 43 | ## ForceDataCollector 44 | 45 | This is the app I built just to collect force data and export it for analysis 46 | 47 | KNOWN GOOD: iPhone 6s (iOS 9.1), Xcode 7.1.1 48 | 49 | Alexis Gallagher 50 | 2015-11-18T1606 51 | 52 | -------------------------------------------------------------------------------- /TouchVisualizer/ALG3DTouchThreshholdGestureRecognizer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ALG3DTouchThreshholdGestureRecognizer.swift 3 | // TouchVisualizer 4 | // 5 | // Created by Alexis Gallagher on 2015-11-03. 6 | // Copyright © 2015 Alexis Gallagher. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import UIKit.UIGestureRecognizerSubclass 11 | 12 | /** 13 | 14 | A continuous gesture recognizer for changes in the `UITouch.force` property of the initial touch (i.e., first finger) of any multitouch sequence that begins with a single touch. 15 | 16 | This recognizer will "recognize" (i.e., call its action method) whenever the force changes. You can access the `UITouch` being tracked via the `currentTouch` property. 17 | 18 | In addition, this recognizer can track whenever the force touches or crosses one or more force threshholds. A force threshholds is a normalized force value (in the range of `0...1`, instead of `0...maximumPossibleForce`). To define a set of force threshhold, just set the property `normalizedForceThreshholds` with a sorted array of `CGFloat` values within `0...1`. Whenever the touch's normalized force reaches or crosses one of these values, then in addition to calling its action method the class will: 19 | 20 | 1. set `indexOfLastThreshholdReachedOrCrossed` 21 | 2. set `lastThreshholdWasdReachedByIncrease` 22 | 3. call `threshholdWasReachedOrCrossed` if it is non-nil 23 | 24 | - note: Overengineered? Afraid so. Some refactoring could simplify this. 25 | 26 | */ 27 | class ALG3DTouchThreshholdGestureRecognizer: UIGestureRecognizer 28 | { 29 | // 30 | // configuration properties 31 | // 32 | 33 | /** 34 | Normalized force threshholds. 35 | 36 | This array must either be empty, or else it must contain a sorted array of distinct `CGFloat` values in the closed-closed interval `0...1`. These represent normalized force values. Whenever the `UITouch`'s normalized force value changes so that it equals or crosses one of the these threshholds, the receiver will update `indexOfLastThreshholdReachedOrCrossed`, `lastThreshholdWasdReachedByIncrease`, and call `threshholdWasReachedOrCrossed` if it is non-nil. 37 | */ 38 | var normalizedForceThreshholds:[CGFloat] = [] 39 | { 40 | didSet { 41 | if normalizedForceThreshholds.isEmpty == false 42 | { 43 | let isSorted = (normalizedForceThreshholds.sorted() == normalizedForceThreshholds) 44 | assert(isSorted, "ERROR: normalizedForceThreshholds must be a sorted array of numbers") 45 | let isBounded = normalizedForceThreshholds 46 | .map({ (0 ... 1).contains($0) }) 47 | .reduce(true, { $0 && $1 }) 48 | assert(isBounded, "ERROR: all numbers in normalizedForceThreshholds must be greater than or equal to 0 and less than or equal to 1") 49 | 50 | let (zones,indexes) = zonesAndThreshholdIndexesForThreshholds(normalizedForceThreshholds) 51 | self.zones = zones 52 | self.indexesOfThreshholdZones = indexes 53 | } 54 | else { 55 | self.zones = nil 56 | self.indexesOfThreshholdZones = [] 57 | } 58 | } 59 | } 60 | 61 | // called when the tracked touch's normalized force reaches or crosses a threshhold. (This is just a convenience, you could get the same information by just checking `indexOfLastThreshholdReachedOrCrossed` every time the action method is called.) 62 | var threshholdWasReachedOrCrossed:(()->Void)? = nil 63 | 64 | // 65 | // output properties, to expose information to the class's users 66 | // 67 | 68 | /// contains the `UITouch` object being tracked by the receiver, and will be non-nil whenever the receiver recognizes and fires its action method 69 | var currentTouch:UITouch? { 70 | switch self.extendedState { 71 | case .possible,.failed: return nil 72 | case .began(let touch,_): return touch 73 | case .changed(let touch,_): return touch 74 | case .ended(let touch,_): return touch 75 | case .canceled(let touch): return touch 76 | } 77 | } 78 | 79 | fileprivate(set) var indexOfLastThreshholdReachedOrCrossed:Int? = nil 80 | fileprivate(set) var lastThreshholdWasdReachedByIncrease:Bool = false 81 | 82 | // 83 | // private state properties 84 | // 85 | 86 | /// array of `Interval`s, generated from the `normalizedForceThreshholds` 87 | fileprivate var zones:[Interval]? = nil 88 | /// indexes of threshhold zones, generated from the `normalizedForceThreshholds` 89 | fileprivate var indexesOfThreshholdZones:[Int] = [] 90 | 91 | /* Represents all the GR's internal state. 92 | 93 | The associated UITouch value is the touch being tracked for the gesture. The associated CGFloat is the normalized force from the last call to one of the `touches...` methods. 94 | 95 | */ 96 | fileprivate enum ExtendedState { 97 | case possible 98 | case failed 99 | case began(UITouch,CGFloat) 100 | case changed(UITouch,CGFloat) 101 | case ended(UITouch,CGFloat) 102 | case canceled(UITouch) 103 | } 104 | 105 | 106 | fileprivate var extendedState:ExtendedState = .possible { 107 | didSet(newValue) { 108 | switch extendedState { 109 | case .possible: self.state = .possible 110 | case .failed: self.state = .failed 111 | case .began(_,_): self.state = .began 112 | case .changed(_,_): self.state = .changed 113 | case .ended(_,_): self.state = .ended 114 | case .canceled(_): self.state = .cancelled 115 | } 116 | } 117 | } 118 | 119 | fileprivate func touchesWithAction(_ touches: Set, withEvent event: UIEvent, phase:UITouchPhase) 120 | { 121 | switch (self.extendedState,phase) 122 | { 123 | // assert: .Possible -> [.Began,.Failed] 124 | case (.possible, UITouchPhase.began): 125 | if let theTouch = touches.first, touches.count == 1 { 126 | let wasReached = self.shouldReportThreshholdCrossingForInitialForce(theTouch.normalizedForce) 127 | self.extendedState = .began(theTouch,theTouch.normalizedForce) 128 | if wasReached { self.threshholdWasReachedOrCrossed?() } 129 | } 130 | else { 131 | // ignore multitouch sequences which begin with more than two simultaneous touches 132 | self.extendedState = .failed 133 | } 134 | 135 | case (.possible, _): 136 | assertionFailure("unexpected call to non-touchesBegan when UIGestureRecognizer was in .Possible state") 137 | self.extendedState = .failed 138 | break 139 | 140 | // assert: .Began -> [.Changed] 141 | case (.began(let touch, let lastNormalizedForce),let touchPhase): 142 | let wasReached = shouldReportThreshholdCrossingForForceChanged(lastNormalizedForce, currentNormalizedForce: touch.normalizedForce) 143 | if touchPhase == UITouchPhase.ended { 144 | self.extendedState = .ended(touch,touch.normalizedForce) 145 | } 146 | else if touchPhase == UITouchPhase.cancelled { 147 | self.extendedState = .canceled(touch) 148 | } 149 | else { 150 | self.extendedState = .changed(touch,touch.normalizedForce) 151 | } 152 | if wasReached { self.threshholdWasReachedOrCrossed?() } 153 | 154 | // assert: .Changes -> [.Changed, .Canceled, .Ended] 155 | case (.changed(let touch, _), .began): 156 | // if a touch began, it must not be the touch we are recognizing which has already begun 157 | for irrelevantTouch in touches.filter({ $0 != touch }) { 158 | self.ignore(irrelevantTouch, for: event) 159 | } 160 | 161 | case (.changed(let touch, let lastNormalizedForce),.moved) where touches.contains(touch): 162 | // if the touch merely moved, maybe report force changes 163 | let wasReached = shouldReportThreshholdCrossingForForceChanged(lastNormalizedForce, currentNormalizedForce: touch.normalizedForce) 164 | self.extendedState = .changed(touch,touch.normalizedForce) 165 | if wasReached { self.threshholdWasReachedOrCrossed?() } 166 | 167 | case (.changed(let touch, let lastNormalizedForce),.stationary) where touches.contains(touch): 168 | // if the touch did not move, maybe report force changes 169 | let wasReached = shouldReportThreshholdCrossingForForceChanged(lastNormalizedForce, currentNormalizedForce: touch.normalizedForce) 170 | // TODO: reconsider this. Do we even want to check .Stationary events. Or does the 171 | // API report all force-only changes as touches Moved? 172 | self.extendedState = .changed(touch,touch.normalizedForce) 173 | if wasReached { self.threshholdWasReachedOrCrossed?() } 174 | 175 | case (.changed(let touch, let lastNormalizedForce),.ended) where touches.contains(touch): 176 | // if the tracked touch ended, always report its final force 177 | let wasReached = self.shouldReportThreshholdCrossingForForceChanged(lastNormalizedForce, currentNormalizedForce: touch.normalizedForce) 178 | self.extendedState = .ended(touch,touch.normalizedForce) 179 | if wasReached { self.threshholdWasReachedOrCrossed?() } 180 | 181 | 182 | case (.changed(let touch,_),.cancelled) where touches.contains(touch): 183 | // if the entire multitouch sequence was cancelled, cancel the gesture as well 184 | self.extendedState = .canceled(touch) 185 | 186 | case (.changed(let touch,_),_) where !touches.contains(touch): 187 | // we were just passed a touch that we said we were ignoring. UIKit, why??? 188 | NSLog("touches%@ called a Changed gesture recognizer with an ignored touch. Event: %@",phase.description,event) 189 | break 190 | 191 | case (.changed(_),let phase): 192 | assertionFailure("Should be unreachable") 193 | NSLog("unexpected call to touches\(phase.description) for this event \(event)") 194 | break 195 | 196 | // assert: no transition requirements from .Failed, .Ended, .Canceled. 197 | // UIKit is responsible for transitioning from these states by calling `reset()` 198 | case (.failed,_): fallthrough 199 | case (.ended(_,_),_): fallthrough 200 | case (.canceled(_),_): 201 | break 202 | } 203 | } 204 | 205 | /** 206 | 207 | Returns true if the force change represents triggers a threshhold, and also updates the output properties `indexOfLastThreshholdReachedOrCrossed` and `lastThreshholdWasdReachedByIncrease` 208 | 209 | - parameter currentNormalizedForce: the current `UITouch.force`, normalized to 0...1 210 | - parameter lastNormalizedForce: the last `UITouch.force`, normalized to 0...1 211 | - returns: returns true for any of these conditions: 212 | 213 | 1. the normalized force is now exactly equal to one of the defined threshholds, or 214 | 2. to reach the current normalized force through a continuous change must have required the value to to cross one or more of the threshholds. 215 | 216 | */ 217 | fileprivate func shouldReportThreshholdCrossingForForceChanged(_ lastNormalizedForce:CGFloat,currentNormalizedForce:CGFloat) -> Bool 218 | { 219 | if currentNormalizedForce == lastNormalizedForce { return false } 220 | 221 | guard let theZones = self.zones, !theZones.isEmpty else { 222 | // GR was configured with no threshholds, so this is not a threshhold event 223 | return false 224 | } 225 | 226 | let result = evaluateForceChangeWithZones(lastNormalizedForce, currentNormalizedForce: currentNormalizedForce, zones: theZones, indexesOfThreshholdZones: self.indexesOfThreshholdZones) 227 | 228 | switch result { 229 | case .ignore: 230 | return false 231 | 232 | case .forceChanged( 233 | indexOfLastThreshholdReachedOrCrossed: let indexOfLastThreshholdReachedOrCrossed, 234 | threshholdReachedByIncreased: let threshholdReachedByIncreased): 235 | 236 | self.indexOfLastThreshholdReachedOrCrossed = indexOfLastThreshholdReachedOrCrossed 237 | self.lastThreshholdWasdReachedByIncrease = threshholdReachedByIncreased 238 | 239 | return true 240 | } 241 | } 242 | 243 | /** 244 | Returns whether the initial foce of a touchesBegan touch should be recognized, and updates output parameters if needed. 245 | */ 246 | fileprivate func shouldReportThreshholdCrossingForInitialForce(_ normalizedForce:CGFloat) -> Bool 247 | { 248 | /* 249 | 250 | We usually compute whether to recognize a force change with `shouldReportForceChanged`. 251 | 252 | But a Began touch is a special case, since there is no prior force with respect to which we can define a change. In particular, just computing change versus an imputed previous touch of force=0 would incorrectly fail to recognize the case when there's a touch with exactly force=0 and also a threshhold of zero, as this would would show up as "no change" from 0 to 0. 253 | 254 | So we cover this special cases manually and then delegate to the method `shouldReportForceChanged` for all other cases. 255 | 256 | */ 257 | 258 | 259 | if self.normalizedForceThreshholds.isEmpty { 260 | return false 261 | } 262 | else { 263 | if normalizedForce == 0 264 | { 265 | if self.normalizedForceThreshholds.first! == CGFloat(0) { 266 | // first touch is exactly on a force==0 threshhold 267 | self.indexOfLastThreshholdReachedOrCrossed = 0 268 | self.lastThreshholdWasdReachedByIncrease = false 269 | return true 270 | } 271 | else { 272 | return false 273 | } 274 | } 275 | else { 276 | return self.shouldReportThreshholdCrossingForForceChanged(CGFloat(0), currentNormalizedForce: normalizedForce) 277 | } 278 | } 279 | } 280 | 281 | // 282 | // MARK: overrides 283 | // 284 | 285 | override func touchesBegan(_ touches: Set, with event: UIEvent) { 286 | super.touchesBegan(touches, with: event) 287 | self.touchesWithAction(touches, withEvent: event, phase: .began) 288 | } 289 | 290 | override func touchesMoved(_ touches: Set, with event: UIEvent) { 291 | super.touchesMoved(touches, with: event) 292 | self.touchesWithAction(touches, withEvent: event, phase: .moved) 293 | } 294 | 295 | override func touchesEnded(_ touches: Set, with event: UIEvent) { 296 | super.touchesEnded(touches, with: event) 297 | self.touchesWithAction(touches, withEvent: event, phase: .ended) 298 | } 299 | 300 | override func touchesCancelled(_ touches: Set, with event: UIEvent) { 301 | super.touchesCancelled(touches, with: event) 302 | self.touchesWithAction(touches, withEvent: event, phase: .cancelled) 303 | } 304 | 305 | override func reset() 306 | { 307 | super.reset() 308 | self.indexOfLastThreshholdReachedOrCrossed = nil 309 | self.lastThreshholdWasdReachedByIncrease = false 310 | self.extendedState = .possible 311 | } 312 | } 313 | 314 | // MARK: helpers 315 | 316 | private extension UITouch 317 | { 318 | var normalizedForce:CGFloat { 319 | return self.force / self.maximumPossibleForce 320 | } 321 | } 322 | 323 | 324 | private enum ForceChangeResult { 325 | case ignore 326 | case forceChanged(indexOfLastThreshholdReachedOrCrossed:Int,threshholdReachedByIncreased:Bool) 327 | } 328 | 329 | /** 330 | 331 | - parameter currentNormalizedForce: 332 | - parameter lastNormalizedForce: 333 | - parameter zones: an array of `Interval`s, covering 0...1, where `threshholdZoneIndexes` are the indexes of elements representing threshhold values 334 | - parameter indexesOfThreshholdZones: sorted indexes of elements in `zones` which represent threshhold values. 335 | - returns: a `ForeceChangeResult.ForceChanged` if the force entered a threshhold zone or crossed one or more threshhold zones; otherwise, `ForceChangeResult.Ignore` 336 | 337 | - precondition: `zones` must be a sorted array of non-overlapping `Interval`s which collectively contain all of `0...1` 338 | - note: pure. 339 | 340 | */ 341 | private func evaluateForceChangeWithZones(_ lastNormalizedForce:CGFloat, currentNormalizedForce:CGFloat, zones:[Interval], indexesOfThreshholdZones:[Int]) -> ForceChangeResult 342 | { 343 | guard currentNormalizedForce != lastNormalizedForce else { return .ignore } 344 | 345 | guard 346 | let indexOfLastForce = zones.index(where: { $0.contains(lastNormalizedForce) }), 347 | let indexOfCurrentForce = zones.index(where: { $0.contains(currentNormalizedForce) }) 348 | else { 349 | NSLog("\(#function): ERROR: zones should cover all the 0...1 interval, but I was unable to fine the index for the zone which contained either the currentNormalizedForce or the lastNormalizedForce") 350 | return .ignore 351 | } 352 | 353 | func threshholdIndexForThreshholdZoneIndex(_ zoneIndex:Int) -> Int { 354 | if let threshholdPosition = indexesOfThreshholdZones.index(of: zoneIndex) { 355 | return threshholdPosition 356 | } 357 | else { 358 | NSLog("Error: could not find zone index for this threshhold") 359 | return 0 360 | } 361 | } 362 | 363 | let currentForceIsOnThreshhold = indexesOfThreshholdZones.contains(indexOfCurrentForce) 364 | let indexDelta = indexOfCurrentForce - indexOfLastForce 365 | 366 | if indexDelta == 0 { 367 | // no movement from one zone to another, so nothing to report 368 | return .ignore 369 | } 370 | else if currentForceIsOnThreshhold { 371 | // landed directly on a threshhold zone, so report this change 372 | 373 | let didIncrease = indexDelta > 0 374 | let lastThreshholdZoneReachedIndex = indexOfCurrentForce 375 | let lastThresholdReachedIndex = threshholdIndexForThreshholdZoneIndex(lastThreshholdZoneReachedIndex) 376 | return .forceChanged(indexOfLastThreshholdReachedOrCrossed:lastThresholdReachedIndex,threshholdReachedByIncreased:didIncrease) 377 | } 378 | else 379 | { 380 | // landed in an area zone. Which threshholds if any did we cross to get here? 381 | let interveningIndexesStartItem = (min(indexOfCurrentForce, indexOfLastForce) + 1) 382 | let interveningIndexesEndItem = max(indexOfCurrentForce, indexOfLastForce) 383 | let interveningZoneIndexes = (interveningIndexesStartItem ..< interveningIndexesEndItem) 384 | let crossedThreshholdIndexes = interveningZoneIndexes.filter({ indexesOfThreshholdZones.contains($0) }).sorted() 385 | 386 | if crossedThreshholdIndexes.isEmpty { 387 | return .ignore 388 | } 389 | else { 390 | // this exit means we report ONCE even if the change in force means that we crossed two threshholds 391 | // maybe change this behavior later to generate one report event per crossing? 392 | let didIncrease = indexDelta > 0 393 | let lastThreshholdZoneCrossedIndex = didIncrease ? crossedThreshholdIndexes.last! : crossedThreshholdIndexes.first! 394 | let threshholdIndexForZoneIndex = threshholdIndexForThreshholdZoneIndex(lastThreshholdZoneCrossedIndex) 395 | 396 | return .forceChanged(indexOfLastThreshholdReachedOrCrossed:threshholdIndexForZoneIndex,threshholdReachedByIncreased:didIncrease) 397 | } 398 | } 399 | 400 | } 401 | 402 | /** 403 | Takes `threshholds`, an array of sorted distinct `CGFloat` values in the closed-closed interval 0...1, and returns an array of `Interval`s representing the threshholds and the spaces around them, as well as indexes for only the threshholds. 404 | 405 | - returns: a tuple with two components. The first components is an array of "zones", that is, `Interval` values which collectively contain all values in 0...1, representing either threshholds or the spaces around them. The second component is the indexes of the intervals that represent threshholds, as opposed to spaces. 406 | 407 | - note: Pure. This helper essentially lets us think about force changes in terms of discrete movements through a finite set of zones, rather than as float moving around a continuum. 408 | 409 | */ 410 | private func zonesAndThreshholdIndexesForThreshholds(_ threshholds:[CGFloat]) -> ([Interval],[Int]) 411 | { 412 | guard threshholds.isEmpty == false else { return ([],[]) } 413 | 414 | let threshholdZones = threshholds.map({ Interval.closedClosed(($0 ... $0)) }) 415 | let gapZones = Array(zip(threshholds, threshholds.dropFirst())).map({Interval.openOpen(OpenOpenInterval($0.0,$0.1))}) 416 | let threshholdsAndInterveningGaps:[Interval] = Array(zip(threshholdZones,gapZones)).flatMap({ [$0.0,$0.1] }) + [threshholdZones.last!] 417 | 418 | // add initial and terminal gap zones, if needed 419 | var allZones = threshholdsAndInterveningGaps 420 | if threshholds.first! != CGFloat(0) { 421 | allZones.insert(Interval.closedOpen((CGFloat(0) ..< threshholds.first!)), at: 0) 422 | } 423 | if threshholds.last! != CGFloat(1) { 424 | allZones.append(Interval.openClosed(OpenClosedInterval(threshholds.last!,CGFloat(1)))) 425 | } 426 | 427 | // collect the indexes of zones representing threshholds 428 | let indexesOfThreshholdZones = Array(allZones.enumerated()).filter({ 429 | switch $1 { case .closedClosed(_): return true 430 | default: return false 431 | }}).map({$0.0}) 432 | 433 | return (allZones,indexesOfThreshholdZones) 434 | } 435 | 436 | 437 | // MARK: Interval 438 | 439 | // poor man's subtype polymorphism 440 | private enum Interval { 441 | case openOpen(OpenOpenInterval) 442 | case closedClosed(ClosedRange) 443 | case closedOpen(Range) 444 | case openClosed(OpenClosedInterval) 445 | 446 | func contains(_ value:T) -> Bool { 447 | switch self { 448 | case .openOpen(let x): return x.contains(value) 449 | case .closedClosed(let x): return x.contains(value) 450 | case .closedOpen(let x): return x.contains(value) 451 | case .openClosed(let x): return x.contains(value) 452 | } 453 | } 454 | } 455 | 456 | // MARK: OpenOpenInterval 457 | 458 | /// Swift's missing OpenOpenInterval type 459 | private struct OpenOpenInterval { 460 | let start:T 461 | let end:T 462 | 463 | init(_ start:T,_ end:T) { 464 | self.start = start 465 | self.end = end 466 | } 467 | } 468 | 469 | extension OpenOpenInterval { 470 | typealias Bound = T 471 | var isEmpty:Bool { 472 | return !(start < end) 473 | } 474 | 475 | func contains(_ value: OpenOpenInterval.Bound) -> Bool { 476 | return start < value && value < end 477 | } 478 | 479 | func clamp(_ intervalToClamp: OpenOpenInterval) -> OpenOpenInterval { 480 | let maxStart = max(self.start,intervalToClamp.start) 481 | let minEnd = max(self.end,intervalToClamp.end) 482 | return OpenOpenInterval(maxStart, minEnd) 483 | } 484 | } 485 | 486 | private func ==(lhs:OpenOpenInterval,rhs:OpenOpenInterval) -> Bool { 487 | return lhs.start == rhs.start && lhs.end == rhs.end 488 | } 489 | 490 | extension OpenOpenInterval : Equatable { } 491 | 492 | // MARK: OpenClosedInterval 493 | 494 | /// Swift's missing OpenOpenInterval type 495 | private struct OpenClosedInterval { 496 | let start:T 497 | let end:T 498 | 499 | init(_ start:T,_ end:T) { 500 | self.start = start 501 | self.end = end 502 | } 503 | } 504 | 505 | extension OpenClosedInterval { 506 | typealias Bound = T 507 | var isEmpty:Bool { 508 | return false 509 | } 510 | 511 | func contains(_ value: OpenClosedInterval.Bound) -> Bool { 512 | return start < value && value <= end 513 | } 514 | 515 | func clamp(_ intervalToClamp: OpenClosedInterval) -> OpenClosedInterval { 516 | let maxStart = max(self.start,intervalToClamp.start) 517 | let minEnd = max(self.end,intervalToClamp.end) 518 | return OpenClosedInterval(maxStart, minEnd) 519 | } 520 | } 521 | 522 | private func ==(lhs:OpenClosedInterval,rhs:OpenClosedInterval) -> Bool { 523 | return lhs.start == rhs.start && lhs.end == rhs.end 524 | } 525 | 526 | extension OpenClosedInterval : Equatable { } 527 | 528 | -------------------------------------------------------------------------------- /TouchVisualizer/ALGSqueezeGestureRecognizer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ALGSqueezeGestureRecognizer.swift 3 | // TouchVisualizer 4 | // 5 | // Created by Alexis Gallagher on 2015-11-03. 6 | // Copyright © 2015 Alexis Gallagher. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import UIKit.UIGestureRecognizerSubclass 11 | 12 | 13 | /** 14 | A discrete gesture recognizer which recognizes a 3D Touch foce beyond `squeezeThreshhold` 15 | */ 16 | class ALGSqueezeGestureRecognizer: UIGestureRecognizer 17 | { 18 | class var mainScreenSupportsForce:Bool { 19 | return UIScreen.main.traitCollection.forceTouchCapability == .available 20 | } 21 | 22 | /// force level required to count as a squeeze. Default is 0.5 23 | var squeezeThreshhold:CGFloat = 0.5 24 | 25 | fileprivate func touchesWithAction(_ touches: Set, withEvent event: UIEvent, phase:UITouchPhase) 26 | { 27 | // switch on GR's current state, and on the type of touches... method that was called 28 | switch (self.state,phase) { 29 | // .Possible -> [.Recognized, .Failed, or no-op ] 30 | case (.possible, UITouchPhase.began): fallthrough 31 | case (.possible, UITouchPhase.moved): fallthrough 32 | case (.possible, UITouchPhase.stationary): fallthrough 33 | case (.possible, UITouchPhase.ended): 34 | if touches.count == 1 && ALGSqueezeGestureRecognizer.mainScreenSupportsForce { 35 | let normalizedForce = touches.first!.force / touches.first!.maximumPossibleForce 36 | if normalizedForce >= self.squeezeThreshhold { 37 | self.state = .recognized 38 | } 39 | } 40 | else { 41 | self.state = .failed 42 | } 43 | 44 | case (.possible, UITouchPhase.cancelled): 45 | self.state = .failed 46 | 47 | // iOS handles evolving the GR from these states 48 | case (.failed,_): fallthrough 49 | case (.ended(_),_): 50 | break 51 | 52 | case (.changed,_): fallthrough 53 | case (.began,_): fallthrough 54 | case (.cancelled,_): 55 | assertionFailure("unreachable: this is a discrete not a continuous gesture recognizer") 56 | 57 | default: 58 | assertionFailure("unreachable") 59 | break 60 | } 61 | } 62 | 63 | // 64 | // MARK: overrides 65 | // 66 | 67 | override func touchesBegan(_ touches: Set, with event: UIEvent) { 68 | super.touchesBegan(touches, with: event) 69 | self.touchesWithAction(touches, withEvent: event, phase: .began) 70 | } 71 | 72 | override func touchesMoved(_ touches: Set, with event: UIEvent) { 73 | super.touchesMoved(touches, with: event) 74 | self.touchesWithAction(touches, withEvent: event, phase: .moved) 75 | } 76 | 77 | override func touchesEnded(_ touches: Set, with event: UIEvent) { 78 | super.touchesEnded(touches, with: event) 79 | self.touchesWithAction(touches, withEvent: event, phase: .ended) 80 | } 81 | 82 | override func touchesCancelled(_ touches: Set, with event: UIEvent) { 83 | super.touchesCancelled(touches, with: event) 84 | self.touchesWithAction(touches, withEvent: event, phase: .cancelled) 85 | } 86 | 87 | override func reset() { 88 | super.reset() 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /TouchVisualizer/ForcePropertiesGestureRecognizer/ALGInitialTouchGestureRecognizer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MonotouchGestureRecognizer.swift 3 | // TouchVisualizer 4 | // 5 | // Created by Alexis Gallagher on 2015-10-30. 6 | // Copyright © 2015 Alexis Gallagher. All rights reserved. 7 | // 8 | 9 | import UIKit.UIGestureRecognizerSubclass 10 | 11 | /** 12 | 13 | This recognizes an "initial touch sequence", which is essentially the part of a multitouch sequence that concerns only the single finger which initiated it. 14 | 15 | Given a valid multitouch sequence, that multitouch sequence contains an initial touch sequence if and only if it begins with a single-finger touch. The initial touch sequence then consists only of `UITouch`s instance values representing that finger. It ends when that finger leaves the screen or when the entire multitouch sequence is cancelled. 16 | 17 | Some valid multitouch sequences do not contain initial touch sequences -- for instance, if the multitouch sequence begins with 2 or more simultaneous touches. 18 | 19 | Some multitouch sequences outlast their initial touch sequence, for instance, if the multitouch sequence begins with one finger, adds a second finger, removes the first finger, and then moves the second finger, then only the first three actions constitute an initial touch sequence. 20 | 21 | When it calls its action method, the property `currentTouch` will be populated with the `UITouch` object of the initial touch sequence 22 | */ 23 | class ALGInitialTouchSequenceGestureRecognizer: UIGestureRecognizer 24 | { 25 | fileprivate enum ExtendedState { 26 | case possible,began(UITouch),failed,changed(UITouch),ended(UITouch),canceled(UITouch) 27 | } 28 | 29 | /// contains the `UITouch` object, and will be non-nil whenever the receiver fires its action callback 30 | var currentTouch:UITouch? { 31 | switch self.extendedState { 32 | case .possible,.failed: return nil 33 | case .began(let touch): return touch 34 | case .changed(let touch): return touch 35 | case .ended(let touch): return touch 36 | case .canceled(let touch): return touch 37 | } 38 | } 39 | 40 | fileprivate var extendedState:ExtendedState = .possible { didSet { 41 | switch extendedState { 42 | case .possible: self.state = .possible 43 | case .failed: self.state = .failed 44 | case .began(_): self.state = .began 45 | case .changed(_): self.state = .changed 46 | case .ended(_): self.state = .ended 47 | case .canceled(_): self.state = .cancelled 48 | } 49 | } 50 | } 51 | 52 | fileprivate func touchesWithAction(_ touches: Set, withEvent event: UIEvent, phase:UITouchPhase) 53 | { 54 | switch (self.extendedState,phase) 55 | { 56 | // assert: .Possible -> [.Began,.Failed] 57 | case (.possible, UITouchPhase.began): 58 | if touches.count == 1 { 59 | self.extendedState = .began(touches.first!) 60 | } 61 | else { 62 | // ignore multitouch sequences which begin with more than two simultaneous touches 63 | self.extendedState = .failed 64 | } 65 | 66 | case (.possible, _): 67 | assertionFailure("unexpected call to non-touchesBegan when UIGestureRecognizer was in .Possible state") 68 | self.extendedState = .failed 69 | break 70 | 71 | // assert: .Began -> [.Changed] 72 | case (.began(let currentTouch),_): 73 | self.extendedState = .changed(currentTouch) 74 | 75 | // assert: .Changes -> [.Changed, .Canceled, .Ended] 76 | case (.changed(let touch), .began): 77 | // if a touch began, it must not be the touch we are recognizing which already began 78 | for irrelevantTouch in touches.filter({ $0 != touch }) { 79 | self.ignore(irrelevantTouch, for: event) 80 | } 81 | 82 | case (.changed(let touch),.moved) where touches.contains(touch): 83 | self.extendedState = .changed(touch) 84 | 85 | case (.changed(let touch),.stationary) where touches.contains(touch): 86 | self.extendedState = .changed(touch) 87 | 88 | case (.changed(let touch),.ended) where touches.contains(touch): 89 | self.extendedState = .ended(touch) 90 | 91 | case (.changed(let touch),.cancelled) where touches.contains(touch): 92 | self.extendedState = .canceled(touch) 93 | 94 | case (.changed(let touch),_) where !touches.contains(touch): 95 | // NSLog("touches%@ called a Changed gesture recognizer with an ignored touch. Event: %@",method.description,event) 96 | break 97 | 98 | case (.changed(_),let phase): 99 | assertionFailure("Should be unreachable") 100 | NSLog("unexpected call to touches\(phase.description) for this event \(event)") 101 | break 102 | 103 | // assert: no transition requirements from .Failed, .Ended, .Canceled 104 | case (.failed,_): fallthrough 105 | case (.ended(_),_): fallthrough 106 | case (.canceled(_),_): 107 | break 108 | } 109 | } 110 | 111 | // 112 | // MARK: overrides 113 | // 114 | 115 | override func touchesBegan(_ touches: Set, with event: UIEvent) { 116 | super.touchesBegan(touches, with: event) 117 | self.touchesWithAction(touches, withEvent: event, phase: .began) 118 | } 119 | 120 | override func touchesMoved(_ touches: Set, with event: UIEvent) { 121 | super.touchesMoved(touches, with: event) 122 | self.touchesWithAction(touches, withEvent: event, phase: .moved) 123 | } 124 | 125 | override func touchesEnded(_ touches: Set, with event: UIEvent) { 126 | super.touchesEnded(touches, with: event) 127 | self.touchesWithAction(touches, withEvent: event, phase: .ended) 128 | } 129 | 130 | override func touchesCancelled(_ touches: Set, with event: UIEvent) { 131 | super.touchesCancelled(touches, with: event) 132 | self.touchesWithAction(touches, withEvent: event, phase: .cancelled) 133 | } 134 | 135 | override func reset() { 136 | super.reset() 137 | self.extendedState = .possible 138 | } 139 | } 140 | 141 | // MARK: logging 142 | 143 | extension UITouchPhase { 144 | var description:String { 145 | switch self { 146 | case .began: return "Began" 147 | case .cancelled: return "Cancelled" 148 | case .ended: return "Ended" 149 | case .moved: return "Moved" 150 | case .stationary: return "Stationary" 151 | } 152 | } 153 | } 154 | 155 | -------------------------------------------------------------------------------- /TouchVisualizer/ForcePropertiesGestureRecognizer/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ForcePropertiesGestureRecognizer 4 | // 5 | // Created by Alexis Gallagher on 2015-10-30. 6 | // Copyright © 2015 Alexis Gallagher. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var touchWindow: TouchDisplayingWindow? 14 | 15 | var window: UIWindow? 16 | { 17 | get { 18 | if let touchWindow = self.touchWindow { 19 | return touchWindow 20 | } 21 | else { 22 | self.touchWindow = TouchDisplayingWindow(frame: UIScreen.mainScreen().bounds) 23 | // initialize presuming we are not on a force-capable device, and then 24 | // activate later after checking the trait collection property 25 | self.touchWindow?.forceActive = false 26 | return touchWindow 27 | } 28 | } 29 | set { } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /TouchVisualizer/ForcePropertiesGestureRecognizer/Assets.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" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /TouchVisualizer/ForcePropertiesGestureRecognizer/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TouchVisualizer/ForcePropertiesGestureRecognizer/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 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /TouchVisualizer/ForcePropertiesGestureRecognizer/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIFileSharingEnabled 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /TouchVisualizer/ForcePropertiesGestureRecognizer/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ForcePropertiesGestureRecognizer 4 | // 5 | // Created by Alexis Gallagher on 2015-10-30. 6 | // Copyright © 2015 Alexis Gallagher. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TouchLoggingViewController: UIViewController 12 | { 13 | @IBOutlet weak var textView: UITextView! 14 | @IBOutlet weak var touchableView: UIView! 15 | 16 | let history = NSMutableArray() 17 | let monoTouchGR = ALGInitialTouchSequenceGestureRecognizer() 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | monoTouchGR.addTarget(self, action: Selector("handleMonoTouchAction:")) 23 | self.touchableView.addGestureRecognizer(monoTouchGR) 24 | self.textView.text = "" 25 | } 26 | 27 | func handleMonoTouchAction(sender:ALGInitialTouchSequenceGestureRecognizer) { 28 | history.addObject(NSNumber(float: Float(sender.currentTouch!.force))) 29 | let forceString = "force=\(sender.currentTouch!.force)\n" 30 | self.textView.text = self.textView.text + forceString 31 | } 32 | 33 | override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) { 34 | let forceCapable = self.traitCollection.forceTouchCapability == .Available 35 | if let touchWindow = UIApplication.sharedApplication().delegate?.window as? TouchDisplayingWindow { 36 | touchWindow.forceActive = forceCapable 37 | } 38 | } 39 | 40 | @IBAction func log(sender:AnyObject) { 41 | self.saveArray() 42 | } 43 | 44 | // MARK: shake actions 45 | 46 | override func canBecomeFirstResponder() -> Bool { 47 | return true 48 | } 49 | 50 | override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?) { 51 | self.saveArray() 52 | } 53 | 54 | // MARK: logging 55 | 56 | func saveArray() { 57 | let array = NSArray(array: self.history) 58 | let data = try! NSJSONSerialization.dataWithJSONObject(array, options: NSJSONWritingOptions.PrettyPrinted) 59 | let fileURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last!.URLByAppendingPathComponent(NSUUID().UUIDString).URLByAppendingPathExtension("json") 60 | data.writeToURL(fileURL, atomically: true) 61 | 62 | NSLog("wrote file to \(fileURL.absoluteString)") 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5E50C7BA1BF6BE0300CAE344 /* ALGInitialTouchGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EC13EF11BE40592005A00CD /* ALGInitialTouchGestureRecognizer.swift */; }; 11 | 5E822F311BD4D2B400C32C09 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E822F301BD4D2B400C32C09 /* AppDelegate.swift */; }; 12 | 5E822F331BD4D2B400C32C09 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E822F321BD4D2B400C32C09 /* ViewController.swift */; }; 13 | 5E822F361BD4D2B400C32C09 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E822F341BD4D2B400C32C09 /* Main.storyboard */; }; 14 | 5E822F381BD4D2B400C32C09 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E822F371BD4D2B400C32C09 /* Assets.xcassets */; }; 15 | 5E822F3B1BD4D2B400C32C09 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E822F391BD4D2B400C32C09 /* LaunchScreen.storyboard */; }; 16 | 5E822F431BD4D2C800C32C09 /* TouchDisplayingWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E822F421BD4D2C800C32C09 /* TouchDisplayingWindow.swift */; }; 17 | 5EA238081BE94AB200D2A1AF /* TouchDisplayingWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E822F421BD4D2C800C32C09 /* TouchDisplayingWindow.swift */; }; 18 | 5EC13EE21BE4053C005A00CD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EC13EE11BE4053C005A00CD /* AppDelegate.swift */; }; 19 | 5EC13EE41BE4053C005A00CD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EC13EE31BE4053C005A00CD /* ViewController.swift */; }; 20 | 5EC13EE71BE4053C005A00CD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EC13EE51BE4053C005A00CD /* Main.storyboard */; }; 21 | 5EC13EE91BE4053C005A00CD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5EC13EE81BE4053C005A00CD /* Assets.xcassets */; }; 22 | 5EC13EEC1BE4053C005A00CD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EC13EEA1BE4053C005A00CD /* LaunchScreen.storyboard */; }; 23 | 5EC13EF21BE40592005A00CD /* ALGInitialTouchGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EC13EF11BE40592005A00CD /* ALGInitialTouchGestureRecognizer.swift */; }; 24 | 5EF6992A1BE98219006775DA /* ALGSqueezeGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EF699291BE98219006775DA /* ALGSqueezeGestureRecognizer.swift */; }; 25 | 5EF6992C1BE9D1CA006775DA /* ALG3DTouchThreshholdGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EF6992B1BE9D1CA006775DA /* ALG3DTouchThreshholdGestureRecognizer.swift */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 5E822F2D1BD4D2B400C32C09 /* TouchVisualizer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TouchVisualizer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 5E822F301BD4D2B400C32C09 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 31 | 5E822F321BD4D2B400C32C09 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 32 | 5E822F351BD4D2B400C32C09 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | 5E822F371BD4D2B400C32C09 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | 5E822F3A1BD4D2B400C32C09 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | 5E822F3C1BD4D2B400C32C09 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 5E822F421BD4D2C800C32C09 /* TouchDisplayingWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = TouchDisplayingWindow.swift; path = TouchVisualizer/TouchDisplayingWindow.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 37 | 5EC13EDF1BE4053C005A00CD /* ForceDataCollector.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ForceDataCollector.app; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 5EC13EE11BE4053C005A00CD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39 | 5EC13EE31BE4053C005A00CD /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 40 | 5EC13EE61BE4053C005A00CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 41 | 5EC13EE81BE4053C005A00CD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 42 | 5EC13EEB1BE4053C005A00CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 43 | 5EC13EED1BE4053C005A00CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 5EC13EF11BE40592005A00CD /* ALGInitialTouchGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = ALGInitialTouchGestureRecognizer.swift; path = ForcePropertiesGestureRecognizer/ALGInitialTouchGestureRecognizer.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 45 | 5EF699291BE98219006775DA /* ALGSqueezeGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALGSqueezeGestureRecognizer.swift; sourceTree = ""; }; 46 | 5EF6992B1BE9D1CA006775DA /* ALG3DTouchThreshholdGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ALG3DTouchThreshholdGestureRecognizer.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 5E822F2A1BD4D2B400C32C09 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | 5EC13EDC1BE4053C005A00CD /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 5E822F241BD4D2B400C32C09 = { 68 | isa = PBXGroup; 69 | children = ( 70 | 5EA238091BE94B5000D2A1AF /* Common */, 71 | 5E822F2F1BD4D2B400C32C09 /* TouchVisualizer */, 72 | 5EC13EE01BE4053C005A00CD /* ForceDataCollector */, 73 | 5E822F2E1BD4D2B400C32C09 /* Products */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | 5E822F2E1BD4D2B400C32C09 /* Products */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 5E822F2D1BD4D2B400C32C09 /* TouchVisualizer.app */, 81 | 5EC13EDF1BE4053C005A00CD /* ForceDataCollector.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 5E822F2F1BD4D2B400C32C09 /* TouchVisualizer */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 5E822F301BD4D2B400C32C09 /* AppDelegate.swift */, 90 | 5E822F321BD4D2B400C32C09 /* ViewController.swift */, 91 | 5E822F341BD4D2B400C32C09 /* Main.storyboard */, 92 | 5E822F371BD4D2B400C32C09 /* Assets.xcassets */, 93 | 5E822F391BD4D2B400C32C09 /* LaunchScreen.storyboard */, 94 | 5E822F3C1BD4D2B400C32C09 /* Info.plist */, 95 | ); 96 | path = TouchVisualizer; 97 | sourceTree = ""; 98 | }; 99 | 5EA238091BE94B5000D2A1AF /* Common */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 5EC13EF11BE40592005A00CD /* ALGInitialTouchGestureRecognizer.swift */, 103 | 5EF699291BE98219006775DA /* ALGSqueezeGestureRecognizer.swift */, 104 | 5EF6992B1BE9D1CA006775DA /* ALG3DTouchThreshholdGestureRecognizer.swift */, 105 | 5E822F421BD4D2C800C32C09 /* TouchDisplayingWindow.swift */, 106 | ); 107 | name = Common; 108 | sourceTree = ""; 109 | }; 110 | 5EC13EE01BE4053C005A00CD /* ForceDataCollector */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 5EC13EE11BE4053C005A00CD /* AppDelegate.swift */, 114 | 5EC13EE31BE4053C005A00CD /* ViewController.swift */, 115 | 5EC13EE51BE4053C005A00CD /* Main.storyboard */, 116 | 5EC13EE81BE4053C005A00CD /* Assets.xcassets */, 117 | 5EC13EEA1BE4053C005A00CD /* LaunchScreen.storyboard */, 118 | 5EC13EED1BE4053C005A00CD /* Info.plist */, 119 | ); 120 | name = ForceDataCollector; 121 | path = ForcePropertiesGestureRecognizer; 122 | sourceTree = ""; 123 | }; 124 | /* End PBXGroup section */ 125 | 126 | /* Begin PBXNativeTarget section */ 127 | 5E822F2C1BD4D2B400C32C09 /* TouchVisualizer */ = { 128 | isa = PBXNativeTarget; 129 | buildConfigurationList = 5E822F3F1BD4D2B400C32C09 /* Build configuration list for PBXNativeTarget "TouchVisualizer" */; 130 | buildPhases = ( 131 | 5E822F291BD4D2B400C32C09 /* Sources */, 132 | 5E822F2A1BD4D2B400C32C09 /* Frameworks */, 133 | 5E822F2B1BD4D2B400C32C09 /* Resources */, 134 | ); 135 | buildRules = ( 136 | ); 137 | dependencies = ( 138 | ); 139 | name = TouchVisualizer; 140 | productName = TouchVisualizer; 141 | productReference = 5E822F2D1BD4D2B400C32C09 /* TouchVisualizer.app */; 142 | productType = "com.apple.product-type.application"; 143 | }; 144 | 5EC13EDE1BE4053C005A00CD /* ForceDataCollector */ = { 145 | isa = PBXNativeTarget; 146 | buildConfigurationList = 5EC13EF01BE4053C005A00CD /* Build configuration list for PBXNativeTarget "ForceDataCollector" */; 147 | buildPhases = ( 148 | 5EC13EDB1BE4053C005A00CD /* Sources */, 149 | 5EC13EDC1BE4053C005A00CD /* Frameworks */, 150 | 5EC13EDD1BE4053C005A00CD /* Resources */, 151 | ); 152 | buildRules = ( 153 | ); 154 | dependencies = ( 155 | ); 156 | name = ForceDataCollector; 157 | productName = ForcePropertiesGestureRecognizer; 158 | productReference = 5EC13EDF1BE4053C005A00CD /* ForceDataCollector.app */; 159 | productType = "com.apple.product-type.application"; 160 | }; 161 | /* End PBXNativeTarget section */ 162 | 163 | /* Begin PBXProject section */ 164 | 5E822F251BD4D2B400C32C09 /* Project object */ = { 165 | isa = PBXProject; 166 | attributes = { 167 | LastSwiftUpdateCheck = 0710; 168 | LastUpgradeCheck = 0820; 169 | ORGANIZATIONNAME = "Alexis Gallagher"; 170 | TargetAttributes = { 171 | 5E822F2C1BD4D2B400C32C09 = { 172 | CreatedOnToolsVersion = 7.0.1; 173 | LastSwiftMigration = 0820; 174 | }; 175 | 5EC13EDE1BE4053C005A00CD = { 176 | CreatedOnToolsVersion = 7.1; 177 | }; 178 | }; 179 | }; 180 | buildConfigurationList = 5E822F281BD4D2B400C32C09 /* Build configuration list for PBXProject "TouchVisualizer" */; 181 | compatibilityVersion = "Xcode 3.2"; 182 | developmentRegion = English; 183 | hasScannedForEncodings = 0; 184 | knownRegions = ( 185 | en, 186 | Base, 187 | ); 188 | mainGroup = 5E822F241BD4D2B400C32C09; 189 | productRefGroup = 5E822F2E1BD4D2B400C32C09 /* Products */; 190 | projectDirPath = ""; 191 | projectRoot = ""; 192 | targets = ( 193 | 5E822F2C1BD4D2B400C32C09 /* TouchVisualizer */, 194 | 5EC13EDE1BE4053C005A00CD /* ForceDataCollector */, 195 | ); 196 | }; 197 | /* End PBXProject section */ 198 | 199 | /* Begin PBXResourcesBuildPhase section */ 200 | 5E822F2B1BD4D2B400C32C09 /* Resources */ = { 201 | isa = PBXResourcesBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 5E822F3B1BD4D2B400C32C09 /* LaunchScreen.storyboard in Resources */, 205 | 5E822F381BD4D2B400C32C09 /* Assets.xcassets in Resources */, 206 | 5E822F361BD4D2B400C32C09 /* Main.storyboard in Resources */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | 5EC13EDD1BE4053C005A00CD /* Resources */ = { 211 | isa = PBXResourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | 5EC13EEC1BE4053C005A00CD /* LaunchScreen.storyboard in Resources */, 215 | 5EC13EE91BE4053C005A00CD /* Assets.xcassets in Resources */, 216 | 5EC13EE71BE4053C005A00CD /* Main.storyboard in Resources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXResourcesBuildPhase section */ 221 | 222 | /* Begin PBXSourcesBuildPhase section */ 223 | 5E822F291BD4D2B400C32C09 /* Sources */ = { 224 | isa = PBXSourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 5E822F331BD4D2B400C32C09 /* ViewController.swift in Sources */, 228 | 5E822F311BD4D2B400C32C09 /* AppDelegate.swift in Sources */, 229 | 5E822F431BD4D2C800C32C09 /* TouchDisplayingWindow.swift in Sources */, 230 | 5EF6992A1BE98219006775DA /* ALGSqueezeGestureRecognizer.swift in Sources */, 231 | 5EF6992C1BE9D1CA006775DA /* ALG3DTouchThreshholdGestureRecognizer.swift in Sources */, 232 | 5E50C7BA1BF6BE0300CAE344 /* ALGInitialTouchGestureRecognizer.swift in Sources */, 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | 5EC13EDB1BE4053C005A00CD /* Sources */ = { 237 | isa = PBXSourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | 5EC13EF21BE40592005A00CD /* ALGInitialTouchGestureRecognizer.swift in Sources */, 241 | 5EA238081BE94AB200D2A1AF /* TouchDisplayingWindow.swift in Sources */, 242 | 5EC13EE41BE4053C005A00CD /* ViewController.swift in Sources */, 243 | 5EC13EE21BE4053C005A00CD /* AppDelegate.swift in Sources */, 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | }; 247 | /* End PBXSourcesBuildPhase section */ 248 | 249 | /* Begin PBXVariantGroup section */ 250 | 5E822F341BD4D2B400C32C09 /* Main.storyboard */ = { 251 | isa = PBXVariantGroup; 252 | children = ( 253 | 5E822F351BD4D2B400C32C09 /* Base */, 254 | ); 255 | name = Main.storyboard; 256 | sourceTree = ""; 257 | }; 258 | 5E822F391BD4D2B400C32C09 /* LaunchScreen.storyboard */ = { 259 | isa = PBXVariantGroup; 260 | children = ( 261 | 5E822F3A1BD4D2B400C32C09 /* Base */, 262 | ); 263 | name = LaunchScreen.storyboard; 264 | sourceTree = ""; 265 | }; 266 | 5EC13EE51BE4053C005A00CD /* Main.storyboard */ = { 267 | isa = PBXVariantGroup; 268 | children = ( 269 | 5EC13EE61BE4053C005A00CD /* Base */, 270 | ); 271 | name = Main.storyboard; 272 | sourceTree = ""; 273 | }; 274 | 5EC13EEA1BE4053C005A00CD /* LaunchScreen.storyboard */ = { 275 | isa = PBXVariantGroup; 276 | children = ( 277 | 5EC13EEB1BE4053C005A00CD /* Base */, 278 | ); 279 | name = LaunchScreen.storyboard; 280 | sourceTree = ""; 281 | }; 282 | /* End PBXVariantGroup section */ 283 | 284 | /* Begin XCBuildConfiguration section */ 285 | 5E822F3D1BD4D2B400C32C09 /* Debug */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ALWAYS_SEARCH_USER_PATHS = NO; 289 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 290 | CLANG_CXX_LIBRARY = "libc++"; 291 | CLANG_ENABLE_MODULES = YES; 292 | CLANG_ENABLE_OBJC_ARC = YES; 293 | CLANG_WARN_BOOL_CONVERSION = YES; 294 | CLANG_WARN_CONSTANT_CONVERSION = YES; 295 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 296 | CLANG_WARN_EMPTY_BODY = YES; 297 | CLANG_WARN_ENUM_CONVERSION = YES; 298 | CLANG_WARN_INFINITE_RECURSION = YES; 299 | CLANG_WARN_INT_CONVERSION = YES; 300 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 301 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 302 | CLANG_WARN_UNREACHABLE_CODE = YES; 303 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 304 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 305 | COPY_PHASE_STRIP = NO; 306 | DEBUG_INFORMATION_FORMAT = dwarf; 307 | ENABLE_STRICT_OBJC_MSGSEND = YES; 308 | ENABLE_TESTABILITY = YES; 309 | GCC_C_LANGUAGE_STANDARD = gnu99; 310 | GCC_DYNAMIC_NO_PIC = NO; 311 | GCC_NO_COMMON_BLOCKS = YES; 312 | GCC_OPTIMIZATION_LEVEL = 0; 313 | GCC_PREPROCESSOR_DEFINITIONS = ( 314 | "DEBUG=1", 315 | "$(inherited)", 316 | ); 317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 318 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 319 | GCC_WARN_UNDECLARED_SELECTOR = YES; 320 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 321 | GCC_WARN_UNUSED_FUNCTION = YES; 322 | GCC_WARN_UNUSED_VARIABLE = YES; 323 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 324 | MTL_ENABLE_DEBUG_INFO = YES; 325 | ONLY_ACTIVE_ARCH = YES; 326 | SDKROOT = iphoneos; 327 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 328 | }; 329 | name = Debug; 330 | }; 331 | 5E822F3E1BD4D2B400C32C09 /* Release */ = { 332 | isa = XCBuildConfiguration; 333 | buildSettings = { 334 | ALWAYS_SEARCH_USER_PATHS = NO; 335 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 336 | CLANG_CXX_LIBRARY = "libc++"; 337 | CLANG_ENABLE_MODULES = YES; 338 | CLANG_ENABLE_OBJC_ARC = YES; 339 | CLANG_WARN_BOOL_CONVERSION = YES; 340 | CLANG_WARN_CONSTANT_CONVERSION = YES; 341 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 342 | CLANG_WARN_EMPTY_BODY = YES; 343 | CLANG_WARN_ENUM_CONVERSION = YES; 344 | CLANG_WARN_INFINITE_RECURSION = YES; 345 | CLANG_WARN_INT_CONVERSION = YES; 346 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 347 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 348 | CLANG_WARN_UNREACHABLE_CODE = YES; 349 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 350 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 351 | COPY_PHASE_STRIP = NO; 352 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 353 | ENABLE_NS_ASSERTIONS = NO; 354 | ENABLE_STRICT_OBJC_MSGSEND = YES; 355 | GCC_C_LANGUAGE_STANDARD = gnu99; 356 | GCC_NO_COMMON_BLOCKS = YES; 357 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 358 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 359 | GCC_WARN_UNDECLARED_SELECTOR = YES; 360 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 361 | GCC_WARN_UNUSED_FUNCTION = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 364 | MTL_ENABLE_DEBUG_INFO = NO; 365 | SDKROOT = iphoneos; 366 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 367 | VALIDATE_PRODUCT = YES; 368 | }; 369 | name = Release; 370 | }; 371 | 5E822F401BD4D2B400C32C09 /* Debug */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 375 | DEVELOPMENT_TEAM = ""; 376 | INFOPLIST_FILE = TouchVisualizer/Info.plist; 377 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 378 | PRODUCT_BUNDLE_IDENTIFIER = com.alexisgallagher.TouchVisualizer; 379 | PRODUCT_NAME = "$(TARGET_NAME)"; 380 | SWIFT_VERSION = 3.0; 381 | }; 382 | name = Debug; 383 | }; 384 | 5E822F411BD4D2B400C32C09 /* Release */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 388 | DEVELOPMENT_TEAM = ""; 389 | INFOPLIST_FILE = TouchVisualizer/Info.plist; 390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 391 | PRODUCT_BUNDLE_IDENTIFIER = com.alexisgallagher.TouchVisualizer; 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | SWIFT_VERSION = 3.0; 394 | }; 395 | name = Release; 396 | }; 397 | 5EC13EEE1BE4053C005A00CD /* Debug */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 401 | INFOPLIST_FILE = ForcePropertiesGestureRecognizer/Info.plist; 402 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 403 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 404 | PRODUCT_BUNDLE_IDENTIFIER = com.alexisgallagher.ForcePropertiesGestureRecognizer; 405 | PRODUCT_NAME = "$(TARGET_NAME)"; 406 | }; 407 | name = Debug; 408 | }; 409 | 5EC13EEF1BE4053C005A00CD /* Release */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 413 | INFOPLIST_FILE = ForcePropertiesGestureRecognizer/Info.plist; 414 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 415 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 416 | PRODUCT_BUNDLE_IDENTIFIER = com.alexisgallagher.ForcePropertiesGestureRecognizer; 417 | PRODUCT_NAME = "$(TARGET_NAME)"; 418 | }; 419 | name = Release; 420 | }; 421 | /* End XCBuildConfiguration section */ 422 | 423 | /* Begin XCConfigurationList section */ 424 | 5E822F281BD4D2B400C32C09 /* Build configuration list for PBXProject "TouchVisualizer" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | 5E822F3D1BD4D2B400C32C09 /* Debug */, 428 | 5E822F3E1BD4D2B400C32C09 /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | 5E822F3F1BD4D2B400C32C09 /* Build configuration list for PBXNativeTarget "TouchVisualizer" */ = { 434 | isa = XCConfigurationList; 435 | buildConfigurations = ( 436 | 5E822F401BD4D2B400C32C09 /* Debug */, 437 | 5E822F411BD4D2B400C32C09 /* Release */, 438 | ); 439 | defaultConfigurationIsVisible = 0; 440 | defaultConfigurationName = Release; 441 | }; 442 | 5EC13EF01BE4053C005A00CD /* Build configuration list for PBXNativeTarget "ForceDataCollector" */ = { 443 | isa = XCConfigurationList; 444 | buildConfigurations = ( 445 | 5EC13EEE1BE4053C005A00CD /* Debug */, 446 | 5EC13EEF1BE4053C005A00CD /* Release */, 447 | ); 448 | defaultConfigurationIsVisible = 0; 449 | defaultConfigurationName = Release; 450 | }; 451 | /* End XCConfigurationList section */ 452 | }; 453 | rootObject = 5E822F251BD4D2B400C32C09 /* Project object */; 454 | } 455 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer.xcodeproj/project.xcworkspace/xcuserdata/alexis.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algal/TouchVisualizer/ee2203c2e134119f8da4d23f1e4431d7807dc8cf/TouchVisualizer/TouchVisualizer.xcodeproj/project.xcworkspace/xcuserdata/alexis.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer.xcodeproj/xcshareddata/xcschemes/ForceDataCollector.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer.xcodeproj/xcshareddata/xcschemes/TouchVisualizer.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer.xcodeproj/xcuserdata/alexis.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 51 | 52 | 66 | 67 | 68 | 69 | 70 | 72 | 84 | 85 | 86 | 88 | 100 | 101 | 102 | 104 | 116 | 117 | 118 | 120 | 132 | 133 | 134 | 136 | 148 | 149 | 150 | 152 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer.xcodeproj/xcuserdata/alexis.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ForceDataCollector.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 2 11 | 12 | TouchVisualizer.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 5E822F2C1BD4D2B400C32C09 21 | 22 | primary 23 | 24 | 25 | 5EC13EDE1BE4053C005A00CD 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TouchVisualizer 4 | // 5 | // Created by Alexis Gallagher on 2015-10-19. 6 | // Copyright © 2015 Alexis Gallagher. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var touchWindow: TouchDisplayingWindow? 14 | 15 | var window: UIWindow? 16 | { 17 | get { 18 | if let touchWindow = self.touchWindow { 19 | return touchWindow 20 | } 21 | else { 22 | self.touchWindow = TouchDisplayingWindow(frame: UIScreen.main.bounds) 23 | // initialize presuming we are not on a force-capable device, and then 24 | // activate later after checking the trait collection property 25 | self.touchWindow?.forceActive = true 26 | 27 | return touchWindow 28 | } 29 | } 30 | set { } 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer/Assets.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" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer/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 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer/TouchDisplayingWindow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TouchVisualizer.swift 3 | // TouchVisualizer 4 | // 5 | // Created by Alexis Gallagher on 2015-10-19. 6 | // Copyright © 2015 Alexis Gallagher. All rights reserved. 7 | // 8 | 9 | // 10 | // TouchDisplayingWindow.swift 11 | // TouchRecorderSpike 12 | // 13 | // Created by Alexis Gallagher on 2015-10-09. 14 | // Copyright © 2015 Alexis Gallagher. All rights reserved. 15 | // 16 | 17 | import UIKit 18 | 19 | /** 20 | Like UIWindow, but displays an overlay view displaying active touches with force annotations. 21 | 22 | @discussion 23 | 24 | You can add this to an existing app for debugging purposes, or merely to dazzle and frighten. 25 | 26 | There are two steps to using this: (1) configure your app to use this instead of UIWindow and (2) ensure to deactive it on devices that do not offer force properties API 27 | 28 | To use this instead of UIWindow, override your app delegate's `window:UIWindow?` property with a computed property, where the setter's a no-op, and the getter returns a constant reference to an instance of this class. 29 | 30 | To de-activate on non-force devices, set `forceActive=false`. Accessing force information on devices without force capability is "undefined" so this precuation is pedantically needed for defined behavior. 31 | 32 | This class should not affect normal touch delivery at all. 33 | 34 | This class tries to keep its own overlay subview in front but it does not take heroic measures to do so. So I'm not sure if this works in complex cases. It might fail if user code or system code does not anticipate another component modifying the existence or order of the key window's subviews. 35 | 36 | KNOWN GOOD: iPhone 5 (iOS 9.0.2), iPhone 6 (iOS 9.0), Xcode 7.0.1 37 | 38 | */ 39 | class TouchDisplayingWindow: UIWindow 40 | { 41 | // if the view should do anything (rather than behave like UIWindow) 42 | var active:Bool = true { didSet { overlayView.isHidden = !active } } 43 | 44 | // if the view should display force information 45 | var forceActive:Bool { 46 | get { return overlayView.shouldDisplayForce } 47 | set { overlayView.shouldDisplayForce = newValue } 48 | } 49 | 50 | fileprivate let overlayView = OverlayGraphicView(frame: CGRect.zero) 51 | 52 | override init(frame: CGRect) { 53 | super.init(frame: frame) 54 | setup() 55 | } 56 | required init?(coder aDecoder: NSCoder) { 57 | super.init(coder: aDecoder) 58 | setup() 59 | } 60 | 61 | override func didAddSubview(_ subview: UIView) { 62 | super.didAddSubview(subview) 63 | self.bringSubview(toFront: overlayView) 64 | } 65 | 66 | fileprivate func setup() { 67 | overlayView.autoresizingMask = [.flexibleWidth,.flexibleHeight] 68 | overlayView.frame = self.bounds 69 | self.addSubview(overlayView) 70 | } 71 | 72 | override func sendEvent(_ event: UIEvent) 73 | { 74 | if self.active && event.type == .touches { 75 | if let touches = event.touches(for: self) { 76 | let activeTouches = touches.filter({[UITouchPhase.began,.stationary,.moved].contains($0.phase)}) 77 | overlayView.activeTouches = Set(activeTouches) 78 | } 79 | else { 80 | overlayView.activeTouches = Set() 81 | } 82 | } 83 | 84 | // forward events for processing as usual 85 | super.sendEvent(event) 86 | } 87 | 88 | override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { 89 | let forceCapable = self.traitCollection.forceTouchCapability == .available 90 | NSLog("window.forceCapable=\(forceCapable)") 91 | } 92 | 93 | } 94 | 95 | // view displaying info on active touches 96 | private class OverlayGraphicView : UIView 97 | { 98 | /// displays 3D Touch force as an orange circle 99 | var shouldDisplayForce:Bool = true 100 | 101 | /// displays radius of the touch as a white circle 102 | var shouldDisplayRadius:Bool = true 103 | 104 | /// displays annulus marking erro range around the radius 105 | var shouldDisplayRadiusError:Bool = true 106 | 107 | /// displays small legends near the touch 108 | var shouldDisplayLegends:Bool = true 109 | 110 | /// displays large force and/or radius label on the left side, of the topleft-most touch 111 | var shouldDisplayBigLegends:Bool = true 112 | 113 | var activeTouches:Set = Set() { 114 | didSet { 115 | setNeedsDisplay() 116 | } 117 | } 118 | 119 | let forceOverlayColor = UIColor.orange 120 | let radiusOverlayColor = UIColor.white 121 | let radiusErrorColor = UIColor.lightGray 122 | 123 | override init(frame: CGRect) { 124 | super.init(frame: frame) 125 | setup() 126 | } 127 | required init?(coder aDecoder: NSCoder) { 128 | super.init(coder: aDecoder) 129 | setup() 130 | } 131 | 132 | fileprivate func setup() { 133 | backgroundColor = .clear 134 | isUserInteractionEnabled = false 135 | } 136 | 137 | override func draw(_ rect: CGRect) 138 | { 139 | // draw force bubble 140 | let minimumRadius:CGFloat = 50 141 | let maximumRadius:CGFloat = 175 142 | 143 | for touch:UITouch in activeTouches { 144 | let centerPoint = touch.location(in: self) 145 | let rawForce:Float = shouldDisplayForce ? Float(touch.force) : 0 146 | let fractionalForce:CGFloat = shouldDisplayForce ? (touch.force / touch.maximumPossibleForce) : 0 147 | 148 | let majorRadius = touch.majorRadius 149 | let majorRadiusTolerance = touch.majorRadiusTolerance 150 | 151 | forceOverlayColor.setStroke() 152 | forceOverlayColor.withAlphaComponent(0.2).setFill() 153 | let radius:CGFloat = fractionalForce * (maximumRadius - minimumRadius) + minimumRadius 154 | let circlePath = UIBezierPath(arcCenter: centerPoint, radius: radius, startAngle: CGFloat(0), endAngle: CGFloat(M_PI * 2.0), clockwise: true) 155 | circlePath.lineWidth = CGFloat(2) 156 | circlePath.stroke() 157 | circlePath.fill() 158 | 159 | if shouldDisplayRadius { 160 | radiusOverlayColor.setStroke() 161 | radiusOverlayColor.withAlphaComponent(0.2).setFill() 162 | let circlePath = UIBezierPath(arcCenter: centerPoint, radius: majorRadius, startAngle: CGFloat(0), endAngle: CGFloat(M_PI * 2.0), clockwise: true) 163 | circlePath.lineWidth = CGFloat(2) 164 | circlePath.stroke() 165 | circlePath.fill() 166 | } 167 | 168 | if shouldDisplayRadiusError { 169 | let fingerRadiusPlusError = majorRadius + majorRadiusTolerance 170 | let fingerRadiusMinusError = majorRadius - majorRadiusTolerance 171 | radiusErrorColor.withAlphaComponent(0.2).setFill() 172 | let outerFingerCirclePath = UIBezierPath(arcCenter: centerPoint, radius: fingerRadiusPlusError, startAngle: CGFloat(0), endAngle: CGFloat(M_PI * 2.0), clockwise: true) 173 | let innerFingerCirclePath = UIBezierPath(arcCenter: centerPoint, radius: fingerRadiusMinusError, startAngle: CGFloat(0), endAngle: CGFloat(M_PI * 2.0), clockwise: false) 174 | outerFingerCirclePath.append(innerFingerCirclePath) 175 | outerFingerCirclePath.usesEvenOddFillRule = false 176 | outerFingerCirclePath.fill() 177 | } 178 | 179 | // labels 180 | 181 | if shouldDisplayForce && shouldDisplayLegends { 182 | // draw force string 183 | let percentString = String(format:"f: %4.3f\u{2007}%2.0f%%",rawForce,Float(fractionalForce * 100)) as NSString 184 | let textAttributes = numericalTextAttributesWithSize(16, color: forceOverlayColor) 185 | let textOrigin = CGPoint(x: centerPoint.x + 40, y: centerPoint.y - 70) 186 | percentString.draw(at: textOrigin, withAttributes: textAttributes) 187 | } 188 | 189 | if shouldDisplayRadius && shouldDisplayLegends { 190 | // draw force string 191 | let percentString = String(format:"r: %2.1f",Float(majorRadius)) as NSString 192 | let textAttributes = numericalTextAttributesWithSize(16, color: radiusOverlayColor) 193 | let textOrigin = CGPoint(x: centerPoint.x + 40, y: centerPoint.y + 70) 194 | percentString.draw(at: textOrigin, withAttributes: textAttributes) 195 | } 196 | 197 | // show only one touch's info in the big legend 198 | 199 | func dist(_ a:UITouch) -> CGFloat { 200 | return ((a.location(in: self).x * a.location(in: self).x) + 201 | (a.location(in: self).y * a.location(in: self).y)) 202 | } 203 | 204 | let isTopLeftMostTouch:Bool = touch == Array(activeTouches).sorted(by: { dist($0) <= dist($1) }).first! 205 | 206 | if shouldDisplayForce && shouldDisplayBigLegends && isTopLeftMostTouch { 207 | // draw force string 208 | let percentString = String(format:"f: %5.4f\u{2007}%2.0f%%",rawForce,Float(fractionalForce * 100)) as NSString 209 | let textAttributes = numericalTextAttributesWithSize(46, color: forceOverlayColor) 210 | let textSize = percentString.size(attributes: textAttributes) 211 | let textOrigin = CGPoint(x: rect.maxX - textSize.width, y: rect.minY) 212 | percentString.draw(at: textOrigin, withAttributes: textAttributes) 213 | } 214 | 215 | if shouldDisplayRadius && shouldDisplayBigLegends && isTopLeftMostTouch { 216 | // draw force string 217 | let percentString = String(format:"r: %3.2f",Float(majorRadius)) as NSString 218 | let textAttributes = numericalTextAttributesWithSize(46, color: radiusOverlayColor) 219 | let textSize = percentString.size(attributes: textAttributes) 220 | let textOrigin = CGPoint(x: rect.maxX - textSize.width, y: rect.maxY - textSize.height) 221 | percentString.draw(at: textOrigin, withAttributes: textAttributes) 222 | } 223 | } 224 | } 225 | } 226 | 227 | private func numericalTextAttributesWithSize(_ size:CGFloat,color:UIColor) -> [String:AnyObject] { 228 | let attributes = [ 229 | NSFontAttributeName:UIFont.monospacedDigitSystemFont(ofSize: size, weight: UIFontWeightMedium), 230 | NSForegroundColorAttributeName:color, 231 | ] 232 | return attributes 233 | } 234 | 235 | -------------------------------------------------------------------------------- /TouchVisualizer/TouchVisualizer/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // TouchVisualizer 4 | // 5 | // Created by Alexis Gallagher on 2015-10-19. 6 | // Copyright © 2015 Alexis Gallagher. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | let squeezeGR = ALGSqueezeGestureRecognizer() 14 | let threshholdGR = ALG3DTouchThreshholdGestureRecognizer() 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | // Do any additional setup after loading the view, typically from a nib. 19 | 20 | // self.threshholdGR.normalizedForceThreshholds = [CGFloat(0.25),CGFloat(0.75)] 21 | // self.threshholdGR.addTarget(self, action: Selector("handleThreshhold:")) 22 | // self.view.addGestureRecognizer(self.threshholdGR) 23 | 24 | // self.squeezeGR.addTarget(self, action: Selector("handleSqueeze:")) 25 | // self.view.addGestureRecognizer(self.squeezeGR) 26 | } 27 | 28 | func handleThreshhold(_ sender:ALG3DTouchThreshholdGestureRecognizer) { 29 | NSLog("==== callback: raw force=\(sender.currentTouch!.force) ") 30 | if let indexCrossed = sender.indexOfLastThreshholdReachedOrCrossed { 31 | NSLog("==== callback: crossingIndex = \(indexCrossed) crossedIncreasing=\(sender.lastThreshholdWasdReachedByIncrease)") 32 | } 33 | 34 | func handleSqueeze(_ sender:AnyObject) { 35 | NSLog("squeeze detected") 36 | } 37 | 38 | } 39 | 40 | override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { 41 | let forceCapable = self.traitCollection.forceTouchCapability == .available 42 | /* 43 | We let the view controller tell the custom UIWindow that it's running on a force-capable 44 | device, because `traitCollectionDidChange(previousTraitCollection:)` is not called on 45 | the custom UIWindow subclass or its overlay view on iOS 9.0. 46 | */ 47 | NSLog("activating force display: \(forceCapable)") 48 | 49 | if let win = UIApplication.shared.delegate?.window as? TouchDisplayingWindow { 50 | win.forceActive = forceCapable 51 | } 52 | else { 53 | NSLog("Did not find touch-displaying window") 54 | } 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /data and analysis/TouchDataAnalysis.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import UIKit 4 | import XCPlayground 5 | 6 | //: Data is loaded and parsed in `ParsedData.swift`, so that it will not be reloaded every time this page of the playground is updated. It is exported from that file as `exportedItems`, an array of CGFloats representing the raw source data 7 | 8 | let items = exportedItems 9 | 10 | //: Basic univariate descriptive statics 11 | 12 | //: Over 60 seconds, we have gathered _2140 data points_, ranging from `0` to `6.66`. This sampling presumably under-represenents the total range of possible values, but it is diagonstic for general conclusions 13 | 14 | let n = items.count 15 | let minValue = items.reduce(items[0], combine: min) 16 | let maxValue = items.reduce(items[0], combine: max) 17 | 18 | //: Representing _397 distinct force values_, if we assume all differences are meaningful rather than due to floating point error 19 | 20 | let distinctItems = Set(items) 21 | let distinctItemCount = distinctItems.count 22 | 23 | let sortedItems = items.sort() 24 | 25 | 26 | //: Let us normalize values to 0...1000 27 | 28 | let normalized = distinctItems.asArray.map( { 1000 * ($0 / maxValue) } ) 29 | 30 | //: How much does the total number of distinct vlaues change if we look only at variation which is greater than 1/1000th of the total allowed value? 31 | 32 | let distinct1000 = Set(normalized.map(trunc)) 33 | let distinct1000Count = distinct1000.count 34 | 35 | //: we still see 397 distinct values, so none of the previously observed variation was due to variation of less than 1/1000th (or 0.1%). Therefore, it was probably not due to floating-point error. 36 | 37 | //: Question: what is the smallest gap between normalized force values? 38 | 39 | let sortedDistinct1000 = distinct1000.asArray.sort() 40 | let gaps = (1..<(sortedDistinct1000.endIndex)).map({ sortedDistinct1000[$0] - sortedDistinct1000[$0.predecessor()] }) 41 | 42 | 43 | 44 | let minimumGap = gaps.reduce(gaps.first!, combine: min) 45 | let maximumGap = gaps.reduce(gaps.first!, combine: max) 46 | let meanGap = gaps.reduce(0.0,combine:+) / CGFloat(gaps.count) 47 | 48 | //: Answer: the smallest gap between force values, normalized to 0...1000, is 2. The average gap is 2.5. So in this sample of data, _the force sensor is reporting values with a maximum resolution of about 0.2% (2 parts in 1000)_. 49 | 50 | //: That tells us the precision of the reported values. To determine their accuracy, we will need to calibrate these values against controlled application of force to the device. 51 | 52 | 53 | 54 | 55 | 56 | 57 | //: Minimum gap between non-normalized items 58 | 59 | let rawDistinctItems = distinctItems.sort() 60 | let gaps2 = (1..<(rawDistinctItems.endIndex)).map({ rawDistinctItems[$0] - rawDistinctItems[$0.predecessor()]}).sort() 61 | let minGap2 = gaps2.reduce(gaps2.first!, combine: min) 62 | 63 | //: Assume: the non-normalized minimum gap is probably 1/60. 64 | let minRawgap = CGFloat(1) / CGFloat(60) 65 | 66 | //: Assume: Maximum value is 20/3 67 | let maxRawValue = CGFloat(20)/CGFloat(3) 68 | 69 | //: Infer: *exact number of distinct raw force values = 400* 70 | let distinctRawValues = maxRawValue / minRawgap 71 | 72 | let resolution = 1.0 / 400.0 73 | 74 | -------------------------------------------------------------------------------- /data and analysis/TouchDataAnalysis.playground/Resources/04317CA4-3AD0-4776-861E-78040D8BC3E2.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0, 3 | 0.05, 4 | 0.1333333, 5 | 0.25, 6 | 0.3666667, 7 | 0.4833333, 8 | 0.6166667, 9 | 0.75, 10 | 0.9, 11 | 1.033333, 12 | 1.183333, 13 | 1.333333, 14 | 1.533333, 15 | 1.633333, 16 | 1.783333, 17 | 1.983333, 18 | 2.183333, 19 | 2.383333, 20 | 2.566667, 21 | 2.733333, 22 | 2.883333, 23 | 3.033333, 24 | 3.216667, 25 | 3.366667, 26 | 3.5, 27 | 3.633333, 28 | 3.766667, 29 | 3.9, 30 | 4.033333, 31 | 4.166667, 32 | 4.316667, 33 | 4.483333, 34 | 4.683333, 35 | 4.883333, 36 | 5.033333, 37 | 5.1, 38 | 5.1, 39 | 4.883333, 40 | 4.55, 41 | 4.133333, 42 | 3.783333, 43 | 3.466667, 44 | 3.166667, 45 | 2.866667, 46 | 2.566667, 47 | 2.25, 48 | 1.983333, 49 | 1.766667, 50 | 1.616667, 51 | 1.516667, 52 | 1.466667, 53 | 1.416667, 54 | 1.383333, 55 | 1.416667, 56 | 1.5, 57 | 1.566667, 58 | 1.616667, 59 | 1.666667, 60 | 1.716667, 61 | 1.75, 62 | 1.8, 63 | 1.883333, 64 | 1.95, 65 | 2.016667, 66 | 2.066667, 67 | 2.083333, 68 | 2.116667, 69 | 2.2, 70 | 2.333333, 71 | 2.466667, 72 | 2.583333, 73 | 2.7, 74 | 2.783333, 75 | 2.85, 76 | 2.983333, 77 | 3.133333, 78 | 3.3, 79 | 3.45, 80 | 3.583333, 81 | 3.7, 82 | 3.816667, 83 | 3.933333, 84 | 4.033333, 85 | 4.133333, 86 | 4.233333, 87 | 4.35, 88 | 4.466667, 89 | 4.566667, 90 | 4.666667, 91 | 4.766667, 92 | 4.883333, 93 | 5.016667, 94 | 5.15, 95 | 5.3, 96 | 5.466667, 97 | 5.666667, 98 | 5.883333, 99 | 6.083333, 100 | 6.283333, 101 | 6.5, 102 | 6.666667, 103 | 6.666667, 104 | 6.666667, 105 | 6.666667, 106 | 6.666667, 107 | 6.666667, 108 | 6.666667, 109 | 6.666667, 110 | 6.666667, 111 | 6.666667, 112 | 6.666667, 113 | 6.666667, 114 | 6.616667, 115 | 6.183333, 116 | 5.55, 117 | 4.85, 118 | 4.2, 119 | 3.7, 120 | 3.3, 121 | 2.983333, 122 | 2.733333, 123 | 2.583333, 124 | 2.466667, 125 | 2.35, 126 | 2.233333, 127 | 2.083333, 128 | 1.883333, 129 | 1.633333, 130 | 1.383333, 131 | 1.15, 132 | 0.9166667, 133 | 0.7166666, 134 | 0.55, 135 | 0, 136 | 0.06666667, 137 | 0.15, 138 | 0.25, 139 | 0.3166667, 140 | 0.4, 141 | 0.5, 142 | 0.6333333, 143 | 0.7666667, 144 | 0.8833333, 145 | 0.95, 146 | 1.033333, 147 | 1.133333, 148 | 1.25, 149 | 1.366667, 150 | 1.483333, 151 | 1.566667, 152 | 1.65, 153 | 1.733333, 154 | 1.8, 155 | 1.9, 156 | 2, 157 | 2.133333, 158 | 2.216667, 159 | 2.316667, 160 | 2.433333, 161 | 2.55, 162 | 2.633333, 163 | 2.733333, 164 | 2.8, 165 | 2.866667, 166 | 2.95, 167 | 3.016667, 168 | 3.083333, 169 | 3.183333, 170 | 3.266667, 171 | 3.366667, 172 | 3.45, 173 | 3.533333, 174 | 3.616667, 175 | 3.683333, 176 | 3.716667, 177 | 3.75, 178 | 3.766667, 179 | 3.766667, 180 | 3.766667, 181 | 3.766667, 182 | 3.766667, 183 | 3.733333, 184 | 3.6, 185 | 3.483333, 186 | 3.383333, 187 | 3.3, 188 | 3.25, 189 | 3.233333, 190 | 3.233333, 191 | 3.233333, 192 | 3.233333, 193 | 3.233333, 194 | 3.233333, 195 | 3.233333, 196 | 3.233333, 197 | 3.233333, 198 | 3.25, 199 | 3.25, 200 | 3.25, 201 | 3.25, 202 | 3.25, 203 | 3.25, 204 | 3.25, 205 | 3.25, 206 | 3.25, 207 | 3.25, 208 | 3.233333, 209 | 3.183333, 210 | 3.2, 211 | 3.366667, 212 | 3.5, 213 | 3.65, 214 | 3.816667, 215 | 3.966667, 216 | 4.116667, 217 | 4.3, 218 | 4.466667, 219 | 4.633333, 220 | 4.75, 221 | 4.866667, 222 | 4.966667, 223 | 5.1, 224 | 5.25, 225 | 5.4, 226 | 5.55, 227 | 5.666667, 228 | 5.733333, 229 | 5.766667, 230 | 5.833333, 231 | 5.916667, 232 | 6.016667, 233 | 6.116667, 234 | 6.2, 235 | 6.283333, 236 | 6.366667, 237 | 6.433333, 238 | 6.5, 239 | 6.55, 240 | 6.65, 241 | 6.666667, 242 | 6.666667, 243 | 6.666667, 244 | 6.666667, 245 | 6.666667, 246 | 6.666667, 247 | 6.666667, 248 | 6.666667, 249 | 6.666667, 250 | 6.666667, 251 | 6.666667, 252 | 6.666667, 253 | 6.666667, 254 | 6.666667, 255 | 6.666667, 256 | 6.666667, 257 | 6.666667, 258 | 6.666667, 259 | 6.65, 260 | 6.033333, 261 | 5.333333, 262 | 4.6, 263 | 3.883333, 264 | 3.25, 265 | 2.7, 266 | 2.25, 267 | 1.95, 268 | 1.7, 269 | 1.533333, 270 | 1.4, 271 | 1.316667, 272 | 1.25, 273 | 1.183333, 274 | 1.133333, 275 | 1.05, 276 | 1, 277 | 0.95, 278 | 0.9, 279 | 0.8666667, 280 | 0.8333333, 281 | 0.7833334, 282 | 0.7333333, 283 | 0.7, 284 | 0.6666667, 285 | 0.65, 286 | 0.65, 287 | 0.65, 288 | 0.6666667, 289 | 0.75, 290 | 0.9, 291 | 1.066667, 292 | 1.266667, 293 | 1.433333, 294 | 1.583333, 295 | 1.75, 296 | 1.916667, 297 | 2.05, 298 | 2.166667, 299 | 2.25, 300 | 2.333333, 301 | 2.433333, 302 | 2.516667, 303 | 2.6, 304 | 2.683333, 305 | 2.766667, 306 | 2.833333, 307 | 2.916667, 308 | 2.983333, 309 | 3.05, 310 | 3.133333, 311 | 3.2, 312 | 3.266667, 313 | 3.316667, 314 | 3.383333, 315 | 3.433333, 316 | 3.5, 317 | 3.583333, 318 | 3.683333, 319 | 3.766667, 320 | 3.833333, 321 | 3.883333, 322 | 3.966667, 323 | 4.033333, 324 | 4.116667, 325 | 4.216667, 326 | 4.316667, 327 | 4.4, 328 | 4.483333, 329 | 4.55, 330 | 4.65, 331 | 4.733333, 332 | 4.816667, 333 | 4.9, 334 | 4.983333, 335 | 5.066667, 336 | 5.166667, 337 | 5.233333, 338 | 5.3, 339 | 5.35, 340 | 5.4, 341 | 5.45, 342 | 5.466667, 343 | 5.483333, 344 | 5.5, 345 | 5.533333, 346 | 5.583333, 347 | 5.65, 348 | 5.716667, 349 | 5.766667, 350 | 5.8, 351 | 5.816667, 352 | 5.883333, 353 | 5.966667, 354 | 6.066667, 355 | 6.183333, 356 | 6.25, 357 | 6.283333, 358 | 6.316667, 359 | 6.4, 360 | 6.533333, 361 | 6.666667, 362 | 6.666667, 363 | 6.6, 364 | 6.45, 365 | 6.383333, 366 | 6.35, 367 | 6.333333, 368 | 6.283333, 369 | 6.15, 370 | 5.916667, 371 | 5.633333, 372 | 5.316667, 373 | 5.016667, 374 | 4.733333, 375 | 4.466667, 376 | 4.2, 377 | 3.95, 378 | 3.7, 379 | 3.416667, 380 | 3.2, 381 | 3.016667, 382 | 2.883333, 383 | 2.783333, 384 | 2.733333, 385 | 2.666667, 386 | 2.6, 387 | 2.516667, 388 | 2.416667, 389 | 2.283333, 390 | 2.183333, 391 | 2.116667, 392 | 2.083333, 393 | 2.066667, 394 | 2.033333, 395 | 2, 396 | 1.933333, 397 | 1.85, 398 | 1.766667, 399 | 1.65, 400 | 1.566667, 401 | 1.466667, 402 | 1.383333, 403 | 1.3, 404 | 1.216667, 405 | 1.133333, 406 | 1.033333, 407 | 0.9666666, 408 | 0.9, 409 | 0.8333333, 410 | 0.7666667, 411 | 0.7, 412 | 0.6333333, 413 | 0.55, 414 | 0.4833333, 415 | 0.4333333, 416 | 0.3833333, 417 | 0.35, 418 | 0.3333333, 419 | 0.3166667, 420 | 0.3, 421 | 0.2833333, 422 | 0.2666667, 423 | 0.25, 424 | 0.2166667, 425 | 0.2, 426 | 0.1833333, 427 | 0.1666667, 428 | 0.15, 429 | 0.1333333, 430 | 0.1333333, 431 | 0.1166667, 432 | 0.1, 433 | 0.08333334, 434 | 0.06666667, 435 | 0.06666667, 436 | 0.06666667, 437 | 0.06666667, 438 | 0.06666667, 439 | 0.06666667, 440 | 0.06666667, 441 | 0.06666667, 442 | 0.06666667, 443 | 0.06666667, 444 | 0.06666667, 445 | 0.06666667, 446 | 0.06666667, 447 | 0.06666667, 448 | 0.06666667, 449 | 0, 450 | 0.01666667, 451 | 0.03333334, 452 | 0.05, 453 | 0.06666667, 454 | 0.08333334, 455 | 0.1, 456 | 0.1166667, 457 | 0.1333333, 458 | 0.15, 459 | 0.1666667, 460 | 0.1833333, 461 | 0.2, 462 | 0.2166667, 463 | 0.2333333, 464 | 0.25, 465 | 0.2833333, 466 | 0.3166667, 467 | 0.3333333, 468 | 0.35, 469 | 0.3833333, 470 | 0.4, 471 | 0.4333333, 472 | 0.4666667, 473 | 0.5, 474 | 0.55, 475 | 0.5833333, 476 | 0.6166667, 477 | 0.65, 478 | 0.6833333, 479 | 0.7166666, 480 | 0.75, 481 | 0.8, 482 | 0.85, 483 | 0.8833333, 484 | 0.9166667, 485 | 0.95, 486 | 0.9666666, 487 | 0.9833333, 488 | 1.016667, 489 | 1.05, 490 | 1.083333, 491 | 1.1, 492 | 1.116667, 493 | 1.133333, 494 | 1.15, 495 | 1.166667, 496 | 1.183333, 497 | 1.2, 498 | 1.216667, 499 | 1.25, 500 | 1.283333, 501 | 1.316667, 502 | 1.333333, 503 | 1.35, 504 | 1.366667, 505 | 1.383333, 506 | 1.416667, 507 | 1.433333, 508 | 1.466667, 509 | 1.483333, 510 | 1.5, 511 | 1.516667, 512 | 1.533333, 513 | 1.55, 514 | 1.566667, 515 | 1.583333, 516 | 1.6, 517 | 1.616667, 518 | 1.633333, 519 | 1.65, 520 | 1.683333, 521 | 1.7, 522 | 1.716667, 523 | 1.733333, 524 | 1.766667, 525 | 1.8, 526 | 1.833333, 527 | 1.883333, 528 | 1.933333, 529 | 1.95, 530 | 1.966667, 531 | 1.983333, 532 | 2, 533 | 2.016667, 534 | 2.033333, 535 | 2.05, 536 | 2.066667, 537 | 2.083333, 538 | 2.1, 539 | 2.116667, 540 | 2.133333, 541 | 2.15, 542 | 2.166667, 543 | 2.183333, 544 | 2.2, 545 | 2.216667, 546 | 2.233333, 547 | 2.233333, 548 | 2.233333, 549 | 2.25, 550 | 2.266667, 551 | 2.283333, 552 | 2.3, 553 | 2.316667, 554 | 2.35, 555 | 2.366667, 556 | 2.383333, 557 | 2.416667, 558 | 2.45, 559 | 2.466667, 560 | 2.5, 561 | 2.55, 562 | 2.6, 563 | 2.65, 564 | 2.683333, 565 | 2.7, 566 | 2.716667, 567 | 2.733333, 568 | 2.75, 569 | 2.783333, 570 | 2.8, 571 | 2.833333, 572 | 2.866667, 573 | 2.9, 574 | 2.933333, 575 | 2.95, 576 | 2.983333, 577 | 3.033333, 578 | 3.05, 579 | 3.083333, 580 | 3.1, 581 | 3.116667, 582 | 3.133333, 583 | 3.15, 584 | 3.166667, 585 | 3.2, 586 | 3.25, 587 | 3.283333, 588 | 3.316667, 589 | 3.35, 590 | 3.366667, 591 | 3.383333, 592 | 3.383333, 593 | 3.383333, 594 | 3.383333, 595 | 3.383333, 596 | 3.4, 597 | 3.416667, 598 | 3.466667, 599 | 3.516667, 600 | 3.583333, 601 | 3.633333, 602 | 3.683333, 603 | 3.733333, 604 | 3.783333, 605 | 3.816667, 606 | 3.85, 607 | 3.883333, 608 | 3.9, 609 | 3.916667, 610 | 3.933333, 611 | 3.966667, 612 | 4, 613 | 4.016667, 614 | 4.033333, 615 | 4.05, 616 | 4.083333, 617 | 4.1, 618 | 4.116667, 619 | 4.133333, 620 | 4.15, 621 | 4.183333, 622 | 4.216667, 623 | 4.25, 624 | 4.283333, 625 | 4.3, 626 | 4.333333, 627 | 4.366667, 628 | 4.383333, 629 | 4.416667, 630 | 4.45, 631 | 4.466667, 632 | 4.483333, 633 | 4.5, 634 | 4.516667, 635 | 4.533333, 636 | 4.55, 637 | 4.566667, 638 | 4.583333, 639 | 4.616667, 640 | 4.7, 641 | 4.783333, 642 | 4.816667, 643 | 4.866667, 644 | 4.9, 645 | 4.933333, 646 | 4.983333, 647 | 5.05, 648 | 5.116667, 649 | 5.183333, 650 | 5.233333, 651 | 5.283333, 652 | 5.333333, 653 | 5.366667, 654 | 5.4, 655 | 5.433333, 656 | 5.466667, 657 | 5.5, 658 | 5.533333, 659 | 5.566667, 660 | 5.6, 661 | 5.633333, 662 | 5.683333, 663 | 5.716667, 664 | 5.75, 665 | 5.783333, 666 | 5.8, 667 | 5.816667, 668 | 5.816667, 669 | 5.833333, 670 | 5.85, 671 | 5.866667, 672 | 5.9, 673 | 5.916667, 674 | 5.933333, 675 | 5.95, 676 | 5.966667, 677 | 5.983333, 678 | 6, 679 | 6.033333, 680 | 6.066667, 681 | 6.083333, 682 | 6.1, 683 | 6.116667, 684 | 6.15, 685 | 6.183333, 686 | 6.25, 687 | 6.316667, 688 | 6.383333, 689 | 6.45, 690 | 6.466667, 691 | 6.483333, 692 | 6.533333, 693 | 6.6, 694 | 6.666667, 695 | 6.666667, 696 | 6.666667, 697 | 6.666667, 698 | 6.666667, 699 | 6.666667, 700 | 6.666667, 701 | 6.666667, 702 | 6.666667, 703 | 6.666667, 704 | 6.583333, 705 | 6.4, 706 | 6.233333, 707 | 6.083333, 708 | 5.916667, 709 | 5.75, 710 | 5.6, 711 | 5.483333, 712 | 5.366667, 713 | 5.283333, 714 | 5.216667, 715 | 5.166667, 716 | 5.133333, 717 | 5.083333, 718 | 5.05, 719 | 5.016667, 720 | 5, 721 | 4.966667, 722 | 4.95, 723 | 4.916667, 724 | 4.883333, 725 | 4.866667, 726 | 4.85, 727 | 4.816667, 728 | 4.783333, 729 | 4.733333, 730 | 4.7, 731 | 4.65, 732 | 4.616667, 733 | 4.566667, 734 | 4.516667, 735 | 4.466667, 736 | 4.433333, 737 | 4.4, 738 | 4.35, 739 | 4.266667, 740 | 4.2, 741 | 4.116667, 742 | 4.066667, 743 | 4.033333, 744 | 4, 745 | 3.983333, 746 | 3.966667, 747 | 3.95, 748 | 3.933333, 749 | 3.916667, 750 | 3.883333, 751 | 3.833333, 752 | 3.8, 753 | 3.783333, 754 | 3.766667, 755 | 3.75, 756 | 3.733333, 757 | 3.716667, 758 | 3.7, 759 | 3.666667, 760 | 3.65, 761 | 3.633333, 762 | 3.583333, 763 | 3.55, 764 | 3.5, 765 | 3.483333, 766 | 3.466667, 767 | 3.45, 768 | 3.433333, 769 | 3.4, 770 | 3.366667, 771 | 3.333333, 772 | 3.3, 773 | 3.283333, 774 | 3.266667, 775 | 3.233333, 776 | 3.183333, 777 | 3.133333, 778 | 3.1, 779 | 3.083333, 780 | 3.016667, 781 | 2.95, 782 | 2.9, 783 | 2.866667, 784 | 2.85, 785 | 2.85, 786 | 2.816667, 787 | 2.8, 788 | 2.766667, 789 | 2.75, 790 | 2.716667, 791 | 2.7, 792 | 2.666667, 793 | 2.633333, 794 | 2.6, 795 | 2.55, 796 | 2.516667, 797 | 2.5, 798 | 2.466667, 799 | 2.433333, 800 | 2.366667, 801 | 2.3, 802 | 2.233333, 803 | 2.166667, 804 | 2.116667, 805 | 2.05, 806 | 1.983333, 807 | 1.916667, 808 | 1.85, 809 | 1.783333, 810 | 1.683333, 811 | 1.6, 812 | 1.533333, 813 | 1.483333, 814 | 1.45, 815 | 1.416667, 816 | 1.383333, 817 | 1.366667, 818 | 1.35, 819 | 1.333333, 820 | 1.316667, 821 | 1.283333, 822 | 1.266667, 823 | 1.233333, 824 | 1.216667, 825 | 1.2, 826 | 1.183333, 827 | 1.15, 828 | 1.133333, 829 | 1.083333, 830 | 1.033333, 831 | 1, 832 | 0.9666666, 833 | 0.95, 834 | 0.9166667, 835 | 0.9, 836 | 0.8666667, 837 | 0.8333333, 838 | 0.8, 839 | 0.7666667, 840 | 0.7333333, 841 | 0.7166666, 842 | 0.7, 843 | 0.6833333, 844 | 0.6666667, 845 | 0.65, 846 | 0.6166667, 847 | 0.5833333, 848 | 0.55, 849 | 0.5166667, 850 | 0.4833333, 851 | 0.4666667, 852 | 0.45, 853 | 0.4333333, 854 | 0.4166667, 855 | 0.4, 856 | 0.3833333, 857 | 0.3666667, 858 | 0.35, 859 | 0.3333333, 860 | 0.3166667, 861 | 0.3, 862 | 0.2833333, 863 | 0.2666667, 864 | 0.25, 865 | 0.2333333, 866 | 0.2166667, 867 | 0.2, 868 | 0.1833333, 869 | 0.1666667, 870 | 0.15, 871 | 0.1333333, 872 | 0.1166667, 873 | 0.1166667, 874 | 0.1, 875 | 0.1, 876 | 0.1, 877 | 0.1, 878 | 0.1, 879 | 0.1, 880 | 0.1, 881 | 0, 882 | 0.01666667, 883 | 0.03333334, 884 | 0.05, 885 | 0.06666667, 886 | 0.08333334, 887 | 0.1, 888 | 0.1166667, 889 | 0.1333333, 890 | 0.15, 891 | 0.1666667, 892 | 0.1833333, 893 | 0.2, 894 | 0.2166667, 895 | 0.2333333, 896 | 0.25, 897 | 0.2666667, 898 | 0.2833333, 899 | 0.3, 900 | 0.3166667, 901 | 0.3333333, 902 | 0.35, 903 | 0.3666667, 904 | 0.3833333, 905 | 0.4, 906 | 0.4166667, 907 | 0.4333333, 908 | 0.45, 909 | 0.4666667, 910 | 0.5, 911 | 0.5166667, 912 | 0.55, 913 | 0.5666667, 914 | 0.5833333, 915 | 0.6, 916 | 0.6166667, 917 | 0.6333333, 918 | 0.65, 919 | 0.6666667, 920 | 0.6833333, 921 | 0.7, 922 | 0.7166666, 923 | 0.7333333, 924 | 0.7666667, 925 | 0.7833334, 926 | 0.8166667, 927 | 0.8333333, 928 | 0.8666667, 929 | 0.9, 930 | 0.9333333, 931 | 0.9666666, 932 | 1, 933 | 1.033333, 934 | 1.05, 935 | 1.083333, 936 | 1.1, 937 | 1.133333, 938 | 1.166667, 939 | 1.2, 940 | 1.25, 941 | 1.283333, 942 | 1.316667, 943 | 1.35, 944 | 1.366667, 945 | 1.4, 946 | 1.416667, 947 | 1.466667, 948 | 1.5, 949 | 1.55, 950 | 1.566667, 951 | 1.583333, 952 | 1.6, 953 | 1.616667, 954 | 1.633333, 955 | 1.65, 956 | 1.666667, 957 | 1.683333, 958 | 1.716667, 959 | 1.75, 960 | 1.783333, 961 | 1.8, 962 | 1.816667, 963 | 1.833333, 964 | 1.85, 965 | 1.866667, 966 | 1.916667, 967 | 1.933333, 968 | 1.966667, 969 | 1.983333, 970 | 2.016667, 971 | 2.05, 972 | 2.066667, 973 | 2.1, 974 | 2.133333, 975 | 2.166667, 976 | 2.183333, 977 | 2.2, 978 | 2.216667, 979 | 2.233333, 980 | 2.266667, 981 | 2.283333, 982 | 2.3, 983 | 2.316667, 984 | 2.333333, 985 | 2.35, 986 | 2.366667, 987 | 2.383333, 988 | 2.4, 989 | 2.416667, 990 | 2.45, 991 | 2.483333, 992 | 2.516667, 993 | 2.55, 994 | 2.566667, 995 | 2.583333, 996 | 2.6, 997 | 2.616667, 998 | 2.633333, 999 | 2.666667, 1000 | 2.7, 1001 | 2.716667, 1002 | 2.733333, 1003 | 2.75, 1004 | 2.766667, 1005 | 2.783333, 1006 | 2.8, 1007 | 2.833333, 1008 | 2.85, 1009 | 2.866667, 1010 | 2.883333, 1011 | 2.9, 1012 | 2.916667, 1013 | 2.933333, 1014 | 2.95, 1015 | 2.966667, 1016 | 2.983333, 1017 | 3, 1018 | 3.016667, 1019 | 3.033333, 1020 | 3.05, 1021 | 3.066667, 1022 | 3.083333, 1023 | 3.1, 1024 | 3.116667, 1025 | 3.133333, 1026 | 3.15, 1027 | 3.166667, 1028 | 3.183333, 1029 | 3.216667, 1030 | 3.233333, 1031 | 3.25, 1032 | 3.266667, 1033 | 3.283333, 1034 | 3.3, 1035 | 3.316667, 1036 | 3.333333, 1037 | 3.35, 1038 | 3.366667, 1039 | 3.383333, 1040 | 3.4, 1041 | 3.433333, 1042 | 3.45, 1043 | 3.466667, 1044 | 3.483333, 1045 | 3.5, 1046 | 3.516667, 1047 | 3.533333, 1048 | 3.55, 1049 | 3.566667, 1050 | 3.583333, 1051 | 3.6, 1052 | 3.616667, 1053 | 3.633333, 1054 | 3.683333, 1055 | 3.7, 1056 | 3.716667, 1057 | 3.733333, 1058 | 3.75, 1059 | 3.766667, 1060 | 3.783333, 1061 | 3.8, 1062 | 3.816667, 1063 | 3.85, 1064 | 3.883333, 1065 | 3.9, 1066 | 3.916667, 1067 | 3.933333, 1068 | 3.95, 1069 | 3.966667, 1070 | 3.983333, 1071 | 4, 1072 | 4.016667, 1073 | 4.033333, 1074 | 4.05, 1075 | 4.066667, 1076 | 4.083333, 1077 | 4.116667, 1078 | 4.133333, 1079 | 4.15, 1080 | 4.166667, 1081 | 4.183333, 1082 | 4.2, 1083 | 4.233333, 1084 | 4.25, 1085 | 4.266667, 1086 | 4.283333, 1087 | 4.3, 1088 | 4.316667, 1089 | 4.333333, 1090 | 4.35, 1091 | 4.383333, 1092 | 4.416667, 1093 | 4.45, 1094 | 4.466667, 1095 | 4.5, 1096 | 4.516667, 1097 | 4.55, 1098 | 4.566667, 1099 | 4.583333, 1100 | 4.616667, 1101 | 4.633333, 1102 | 4.666667, 1103 | 4.7, 1104 | 4.733333, 1105 | 4.75, 1106 | 4.766667, 1107 | 4.783333, 1108 | 4.8, 1109 | 4.816667, 1110 | 4.833333, 1111 | 4.85, 1112 | 4.866667, 1113 | 4.883333, 1114 | 4.9, 1115 | 4.933333, 1116 | 4.95, 1117 | 4.983333, 1118 | 5, 1119 | 5.016667, 1120 | 5.033333, 1121 | 5.05, 1122 | 5.066667, 1123 | 5.083333, 1124 | 5.133333, 1125 | 5.15, 1126 | 5.183333, 1127 | 5.2, 1128 | 5.216667, 1129 | 5.233333, 1130 | 5.266667, 1131 | 5.3, 1132 | 5.333333, 1133 | 5.35, 1134 | 5.366667, 1135 | 5.383333, 1136 | 5.4, 1137 | 5.433333, 1138 | 5.45, 1139 | 5.466667, 1140 | 5.483333, 1141 | 5.5, 1142 | 5.516667, 1143 | 5.533333, 1144 | 5.55, 1145 | 5.566667, 1146 | 5.6, 1147 | 5.616667, 1148 | 5.633333, 1149 | 5.65, 1150 | 5.683333, 1151 | 5.716667, 1152 | 5.733333, 1153 | 5.766667, 1154 | 5.783333, 1155 | 5.8, 1156 | 5.833333, 1157 | 5.85, 1158 | 5.9, 1159 | 5.95, 1160 | 6.016667, 1161 | 6.066667, 1162 | 6.1, 1163 | 6.133333, 1164 | 6.15, 1165 | 6.183333, 1166 | 6.233333, 1167 | 6.283333, 1168 | 6.333333, 1169 | 6.383333, 1170 | 6.4, 1171 | 6.416667, 1172 | 6.433333, 1173 | 6.45, 1174 | 6.466667, 1175 | 6.5, 1176 | 6.516667, 1177 | 6.533333, 1178 | 6.616667, 1179 | 6.666667, 1180 | 6.666667, 1181 | 6.666667, 1182 | 6.666667, 1183 | 6.666667, 1184 | 6.666667, 1185 | 6.666667, 1186 | 6.666667, 1187 | 6.666667, 1188 | 6.666667, 1189 | 6.666667, 1190 | 6.666667, 1191 | 6.666667, 1192 | 6.666667, 1193 | 6.666667, 1194 | 6.666667, 1195 | 6.666667, 1196 | 6.666667, 1197 | 6.666667, 1198 | 6.666667, 1199 | 6.666667, 1200 | 6.666667, 1201 | 6.666667, 1202 | 6.666667, 1203 | 6.666667, 1204 | 6.666667, 1205 | 6.666667, 1206 | 6.666667, 1207 | 6.666667, 1208 | 6.65, 1209 | 6.533333, 1210 | 6.416667, 1211 | 6.283333, 1212 | 6.166667, 1213 | 6.033333, 1214 | 5.883333, 1215 | 5.716667, 1216 | 5.55, 1217 | 5.383333, 1218 | 5.25, 1219 | 5.133333, 1220 | 5.016667, 1221 | 4.916667, 1222 | 4.833333, 1223 | 4.75, 1224 | 4.666667, 1225 | 4.6, 1226 | 4.533333, 1227 | 4.483333, 1228 | 4.433333, 1229 | 4.4, 1230 | 4.366667, 1231 | 4.3, 1232 | 4.25, 1233 | 4.183333, 1234 | 4.133333, 1235 | 4.1, 1236 | 4.083333, 1237 | 4.05, 1238 | 4.033333, 1239 | 4.016667, 1240 | 4, 1241 | 3.983333, 1242 | 3.966667, 1243 | 3.933333, 1244 | 3.9, 1245 | 3.866667, 1246 | 3.85, 1247 | 3.833333, 1248 | 3.816667, 1249 | 3.8, 1250 | 3.783333, 1251 | 3.75, 1252 | 3.733333, 1253 | 3.716667, 1254 | 3.7, 1255 | 3.65, 1256 | 3.633333, 1257 | 3.616667, 1258 | 3.6, 1259 | 3.583333, 1260 | 3.566667, 1261 | 3.55, 1262 | 3.516667, 1263 | 3.466667, 1264 | 3.416667, 1265 | 3.383333, 1266 | 3.35, 1267 | 3.333333, 1268 | 3.316667, 1269 | 3.3, 1270 | 3.266667, 1271 | 3.216667, 1272 | 3.166667, 1273 | 3.116667, 1274 | 3.1, 1275 | 3.066667, 1276 | 3.05, 1277 | 3.016667, 1278 | 3, 1279 | 2.95, 1280 | 2.916667, 1281 | 2.883333, 1282 | 2.866667, 1283 | 2.833333, 1284 | 2.8, 1285 | 2.75, 1286 | 2.666667, 1287 | 2.6, 1288 | 2.533333, 1289 | 2.483333, 1290 | 2.433333, 1291 | 2.416667, 1292 | 2.366667, 1293 | 2.333333, 1294 | 2.3, 1295 | 2.283333, 1296 | 2.266667, 1297 | 2.233333, 1298 | 2.216667, 1299 | 2.183333, 1300 | 2.15, 1301 | 2.1, 1302 | 2.05, 1303 | 2, 1304 | 1.933333, 1305 | 1.866667, 1306 | 1.8, 1307 | 1.766667, 1308 | 1.766667, 1309 | 1.716667, 1310 | 1.666667, 1311 | 1.616667, 1312 | 1.566667, 1313 | 1.533333, 1314 | 1.516667, 1315 | 1.516667, 1316 | 1.5, 1317 | 1.483333, 1318 | 1.466667, 1319 | 1.45, 1320 | 1.433333, 1321 | 1.416667, 1322 | 1.383333, 1323 | 1.366667, 1324 | 1.316667, 1325 | 1.266667, 1326 | 1.233333, 1327 | 1.183333, 1328 | 1.133333, 1329 | 1.083333, 1330 | 1.033333, 1331 | 1.016667, 1332 | 0.9833333, 1333 | 0.9666666, 1334 | 0.95, 1335 | 0.9333333, 1336 | 0.9333333, 1337 | 0.9333333, 1338 | 0.9333333, 1339 | 0.9166667, 1340 | 0.9, 1341 | 0.8666667, 1342 | 0.8333333, 1343 | 0.8166667, 1344 | 0.7833334, 1345 | 0.7333333, 1346 | 0.6833333, 1347 | 0.6166667, 1348 | 0.5666667, 1349 | 0.5166667, 1350 | 0.4833333, 1351 | 0.4666667, 1352 | 0.45, 1353 | 0.4333333, 1354 | 0.4166667, 1355 | 0.4, 1356 | 0.3833333, 1357 | 0.3666667, 1358 | 0.35, 1359 | 0.3333333, 1360 | 0.3166667, 1361 | 0.3, 1362 | 0.2833333, 1363 | 0.2666667, 1364 | 0.25, 1365 | 0.2333333, 1366 | 0.2166667, 1367 | 0.2, 1368 | 0.2, 1369 | 0.1833333, 1370 | 0.1666667, 1371 | 0.1666667, 1372 | 0.15, 1373 | 0.1333333, 1374 | 0.1333333, 1375 | 0.1333333, 1376 | 0.1166667, 1377 | 0.1166667, 1378 | 0.1, 1379 | 0.08333334, 1380 | 0.08333334, 1381 | 0.08333334, 1382 | 0.08333334, 1383 | 0.08333334, 1384 | 0.08333334, 1385 | 0.08333334, 1386 | 0.06666667, 1387 | 0.06666667, 1388 | 0.06666667, 1389 | 0.06666667, 1390 | 0.06666667, 1391 | 0.06666667, 1392 | 0.06666667, 1393 | 0, 1394 | 0.01666667, 1395 | 0.03333334, 1396 | 0.05, 1397 | 0.06666667, 1398 | 0.08333334, 1399 | 0.1, 1400 | 0.1166667, 1401 | 0.15, 1402 | 0.1666667, 1403 | 0.1833333, 1404 | 0.2, 1405 | 0.2, 1406 | 0.2166667, 1407 | 0.2166667, 1408 | 0.2333333, 1409 | 0.25, 1410 | 0.2666667, 1411 | 0.2833333, 1412 | 0.3, 1413 | 0.3166667, 1414 | 0.3333333, 1415 | 0.35, 1416 | 0.3833333, 1417 | 0.4166667, 1418 | 0.45, 1419 | 0.4666667, 1420 | 0.5, 1421 | 0.5333334, 1422 | 0.5666667, 1423 | 0.6166667, 1424 | 0.65, 1425 | 0.6833333, 1426 | 0.7333333, 1427 | 0.7833334, 1428 | 0.8333333, 1429 | 0.9, 1430 | 0.95, 1431 | 0.9666666, 1432 | 1.016667, 1433 | 1.05, 1434 | 1.1, 1435 | 1.15, 1436 | 1.216667, 1437 | 1.3, 1438 | 1.383333, 1439 | 1.45, 1440 | 1.5, 1441 | 1.533333, 1442 | 1.566667, 1443 | 1.583333, 1444 | 1.616667, 1445 | 1.65, 1446 | 1.683333, 1447 | 1.733333, 1448 | 1.766667, 1449 | 1.816667, 1450 | 1.866667, 1451 | 1.916667, 1452 | 1.983333, 1453 | 2.033333, 1454 | 2.1, 1455 | 2.166667, 1456 | 2.216667, 1457 | 2.283333, 1458 | 2.35, 1459 | 2.416667, 1460 | 2.483333, 1461 | 2.533333, 1462 | 2.583333, 1463 | 2.65, 1464 | 2.7, 1465 | 2.733333, 1466 | 2.783333, 1467 | 2.8, 1468 | 2.833333, 1469 | 2.866667, 1470 | 2.916667, 1471 | 2.95, 1472 | 2.983333, 1473 | 3, 1474 | 3.033333, 1475 | 3.05, 1476 | 3.066667, 1477 | 3.1, 1478 | 3.15, 1479 | 3.216667, 1480 | 3.283333, 1481 | 3.35, 1482 | 3.383333, 1483 | 3.433333, 1484 | 3.5, 1485 | 3.566667, 1486 | 3.633333, 1487 | 3.7, 1488 | 3.766667, 1489 | 3.833333, 1490 | 3.9, 1491 | 3.933333, 1492 | 3.983333, 1493 | 4.016667, 1494 | 4.066667, 1495 | 4.116667, 1496 | 4.166667, 1497 | 4.2, 1498 | 4.25, 1499 | 4.266667, 1500 | 4.3, 1501 | 4.333333, 1502 | 4.383333, 1503 | 4.433333, 1504 | 4.483333, 1505 | 4.516667, 1506 | 4.55, 1507 | 4.566667, 1508 | 4.6, 1509 | 4.65, 1510 | 4.7, 1511 | 4.75, 1512 | 4.783333, 1513 | 4.8, 1514 | 4.816667, 1515 | 4.816667, 1516 | 4.833333, 1517 | 4.85, 1518 | 4.883333, 1519 | 4.916667, 1520 | 4.966667, 1521 | 5.016667, 1522 | 5.05, 1523 | 5.066667, 1524 | 5.083333, 1525 | 5.116667, 1526 | 5.133333, 1527 | 5.166667, 1528 | 5.183333, 1529 | 5.2, 1530 | 5.216667, 1531 | 5.25, 1532 | 5.266667, 1533 | 5.283333, 1534 | 5.316667, 1535 | 5.35, 1536 | 5.383333, 1537 | 5.433333, 1538 | 5.5, 1539 | 5.566667, 1540 | 5.616667, 1541 | 5.666667, 1542 | 5.716667, 1543 | 5.766667, 1544 | 5.816667, 1545 | 5.866667, 1546 | 5.916667, 1547 | 5.966667, 1548 | 6.016667, 1549 | 6.066667, 1550 | 6.116667, 1551 | 6.166667, 1552 | 6.183333, 1553 | 6.216667, 1554 | 6.25, 1555 | 6.3, 1556 | 6.35, 1557 | 6.4, 1558 | 6.433333, 1559 | 6.466667, 1560 | 6.483333, 1561 | 6.516667, 1562 | 6.55, 1563 | 6.6, 1564 | 6.666667, 1565 | 6.666667, 1566 | 6.666667, 1567 | 6.666667, 1568 | 6.666667, 1569 | 6.666667, 1570 | 6.666667, 1571 | 6.666667, 1572 | 6.666667, 1573 | 6.666667, 1574 | 6.666667, 1575 | 6.55, 1576 | 6.266667, 1577 | 5.966667, 1578 | 5.683333, 1579 | 5.416667, 1580 | 5.2, 1581 | 5, 1582 | 4.85, 1583 | 4.733333, 1584 | 4.633333, 1585 | 4.533333, 1586 | 4.416667, 1587 | 4.333333, 1588 | 4.233333, 1589 | 4.166667, 1590 | 4.133333, 1591 | 4.083333, 1592 | 4.033333, 1593 | 4, 1594 | 3.95, 1595 | 3.916667, 1596 | 3.883333, 1597 | 3.85, 1598 | 3.816667, 1599 | 3.783333, 1600 | 3.733333, 1601 | 3.65, 1602 | 3.6, 1603 | 3.566667, 1604 | 3.533333, 1605 | 3.5, 1606 | 3.45, 1607 | 3.383333, 1608 | 3.316667, 1609 | 3.233333, 1610 | 3.166667, 1611 | 3.116667, 1612 | 3.083333, 1613 | 3.016667, 1614 | 2.983333, 1615 | 2.933333, 1616 | 2.883333, 1617 | 2.816667, 1618 | 2.75, 1619 | 2.666667, 1620 | 2.583333, 1621 | 2.5, 1622 | 2.416667, 1623 | 2.35, 1624 | 2.3, 1625 | 2.266667, 1626 | 2.25, 1627 | 2.233333, 1628 | 2.216667, 1629 | 2.183333, 1630 | 2.166667, 1631 | 2.133333, 1632 | 2.116667, 1633 | 2.1, 1634 | 2.066667, 1635 | 2.033333, 1636 | 2.016667, 1637 | 1.983333, 1638 | 1.966667, 1639 | 1.95, 1640 | 1.916667, 1641 | 1.883333, 1642 | 1.85, 1643 | 1.816667, 1644 | 1.783333, 1645 | 1.733333, 1646 | 1.7, 1647 | 1.65, 1648 | 1.6, 1649 | 1.583333, 1650 | 1.566667, 1651 | 1.55, 1652 | 1.533333, 1653 | 1.516667, 1654 | 1.483333, 1655 | 1.45, 1656 | 1.416667, 1657 | 1.366667, 1658 | 1.316667, 1659 | 1.266667, 1660 | 1.216667, 1661 | 1.15, 1662 | 1.066667, 1663 | 1, 1664 | 0.95, 1665 | 0.9, 1666 | 0.85, 1667 | 0.8, 1668 | 0.7666667, 1669 | 0.7166666, 1670 | 0.6666667, 1671 | 0.6333333, 1672 | 0.6, 1673 | 0.5833333, 1674 | 0.5666667, 1675 | 0.55, 1676 | 0.5333334, 1677 | 0.5166667, 1678 | 0.5, 1679 | 0.4833333, 1680 | 0.4666667, 1681 | 0.45, 1682 | 0.4333333, 1683 | 0.4166667, 1684 | 0.4, 1685 | 0.3833333, 1686 | 0.3666667, 1687 | 0.35, 1688 | 0.3333333, 1689 | 0.3, 1690 | 0.2833333, 1691 | 0.2666667, 1692 | 0.2333333, 1693 | 0.2166667, 1694 | 0.2, 1695 | 0.1833333, 1696 | 0.1666667, 1697 | 0.15, 1698 | 0.1333333, 1699 | 0.1166667, 1700 | 0.1, 1701 | 0.08333334, 1702 | 0.06666667, 1703 | 0.06666667, 1704 | 0.06666667, 1705 | 0.06666667, 1706 | 0, 1707 | 0.01666667, 1708 | 0.1666667, 1709 | 0.1833333, 1710 | 0.2166667, 1711 | 0.25, 1712 | 0.3, 1713 | 0.35, 1714 | 0.4, 1715 | 0.4333333, 1716 | 0.4833333, 1717 | 0.5166667, 1718 | 0.55, 1719 | 0.5833333, 1720 | 0.6333333, 1721 | 0.6833333, 1722 | 0.7333333, 1723 | 0.7833334, 1724 | 0.85, 1725 | 0.9, 1726 | 0.95, 1727 | 0.9833333, 1728 | 1, 1729 | 1.016667, 1730 | 1.05, 1731 | 1.083333, 1732 | 1.116667, 1733 | 1.15, 1734 | 1.2, 1735 | 1.233333, 1736 | 1.25, 1737 | 1.266667, 1738 | 1.3, 1739 | 1.333333, 1740 | 1.383333, 1741 | 1.416667, 1742 | 1.466667, 1743 | 1.516667, 1744 | 1.55, 1745 | 1.583333, 1746 | 1.616667, 1747 | 1.65, 1748 | 1.683333, 1749 | 1.7, 1750 | 1.716667, 1751 | 1.733333, 1752 | 1.75, 1753 | 1.766667, 1754 | 1.8, 1755 | 1.833333, 1756 | 1.85, 1757 | 1.866667, 1758 | 1.883333, 1759 | 1.9, 1760 | 1.916667, 1761 | 1.966667, 1762 | 2, 1763 | 2.033333, 1764 | 2.083333, 1765 | 2.116667, 1766 | 2.15, 1767 | 2.183333, 1768 | 2.25, 1769 | 2.3, 1770 | 2.333333, 1771 | 2.366667, 1772 | 2.383333, 1773 | 2.416667, 1774 | 2.45, 1775 | 2.5, 1776 | 2.566667, 1777 | 2.616667, 1778 | 2.65, 1779 | 2.666667, 1780 | 2.683333, 1781 | 2.7, 1782 | 2.733333, 1783 | 2.75, 1784 | 2.766667, 1785 | 2.783333, 1786 | 2.8, 1787 | 2.816667, 1788 | 2.866667, 1789 | 2.9, 1790 | 2.916667, 1791 | 2.933333, 1792 | 2.95, 1793 | 2.966667, 1794 | 2.983333, 1795 | 3, 1796 | 3.033333, 1797 | 3.05, 1798 | 3.066667, 1799 | 3.083333, 1800 | 3.1, 1801 | 3.116667, 1802 | 3.133333, 1803 | 3.133333, 1804 | 3.15, 1805 | 3.166667, 1806 | 3.183333, 1807 | 3.2, 1808 | 3.216667, 1809 | 3.233333, 1810 | 3.25, 1811 | 3.266667, 1812 | 3.283333, 1813 | 3.3, 1814 | 3.316667, 1815 | 3.333333, 1816 | 3.35, 1817 | 3.366667, 1818 | 3.383333, 1819 | 3.4, 1820 | 3.4, 1821 | 3.416667, 1822 | 3.433333, 1823 | 3.45, 1824 | 3.466667, 1825 | 3.5, 1826 | 3.533333, 1827 | 3.566667, 1828 | 3.6, 1829 | 3.633333, 1830 | 3.666667, 1831 | 3.683333, 1832 | 3.7, 1833 | 3.716667, 1834 | 3.733333, 1835 | 3.766667, 1836 | 3.783333, 1837 | 3.8, 1838 | 3.833333, 1839 | 3.85, 1840 | 3.866667, 1841 | 3.883333, 1842 | 3.9, 1843 | 3.916667, 1844 | 3.933333, 1845 | 3.95, 1846 | 3.966667, 1847 | 3.983333, 1848 | 4, 1849 | 4.033333, 1850 | 4.066667, 1851 | 4.083333, 1852 | 4.1, 1853 | 4.116667, 1854 | 4.15, 1855 | 4.183333, 1856 | 4.216667, 1857 | 4.25, 1858 | 4.283333, 1859 | 4.333333, 1860 | 4.366667, 1861 | 4.4, 1862 | 4.433333, 1863 | 4.466667, 1864 | 4.516667, 1865 | 4.566667, 1866 | 4.633333, 1867 | 4.683333, 1868 | 4.716667, 1869 | 4.75, 1870 | 4.766667, 1871 | 4.8, 1872 | 4.833333, 1873 | 4.866667, 1874 | 4.916667, 1875 | 5, 1876 | 5.1, 1877 | 5.216667, 1878 | 5.333333, 1879 | 5.45, 1880 | 5.566667, 1881 | 5.683333, 1882 | 5.8, 1883 | 5.9, 1884 | 6.016667, 1885 | 6.166667, 1886 | 6.3, 1887 | 6.45, 1888 | 6.583333, 1889 | 6.666667, 1890 | 6.666667, 1891 | 6.666667, 1892 | 6.666667, 1893 | 6.666667, 1894 | 6.666667, 1895 | 6.666667, 1896 | 6.666667, 1897 | 6.666667, 1898 | 6.666667, 1899 | 6.666667, 1900 | 6.666667, 1901 | 6.666667, 1902 | 6.666667, 1903 | 6.666667, 1904 | 6.666667, 1905 | 6.666667, 1906 | 6.666667, 1907 | 6.666667, 1908 | 6.666667, 1909 | 6.666667, 1910 | 6.5, 1911 | 6.333333, 1912 | 6.15, 1913 | 5.966667, 1914 | 5.8, 1915 | 5.683333, 1916 | 5.6, 1917 | 5.55, 1918 | 5.516667, 1919 | 5.5, 1920 | 5.466667, 1921 | 5.416667, 1922 | 5.383333, 1923 | 5.366667, 1924 | 5.35, 1925 | 5.316667, 1926 | 5.283333, 1927 | 5.233333, 1928 | 5.2, 1929 | 5.15, 1930 | 5.116667, 1931 | 5.066667, 1932 | 5.016667, 1933 | 4.983333, 1934 | 4.933333, 1935 | 4.9, 1936 | 4.833333, 1937 | 4.783333, 1938 | 4.733333, 1939 | 4.7, 1940 | 4.683333, 1941 | 4.666667, 1942 | 4.633333, 1943 | 4.6, 1944 | 4.583333, 1945 | 4.566667, 1946 | 4.55, 1947 | 4.533333, 1948 | 4.516667, 1949 | 4.5, 1950 | 4.483333, 1951 | 4.466667, 1952 | 4.45, 1953 | 4.433333, 1954 | 4.416667, 1955 | 4.4, 1956 | 4.383333, 1957 | 4.366667, 1958 | 4.333333, 1959 | 4.3, 1960 | 4.283333, 1961 | 4.266667, 1962 | 4.233333, 1963 | 4.183333, 1964 | 4.133333, 1965 | 4.066667, 1966 | 4.016667, 1967 | 3.983333, 1968 | 3.966667, 1969 | 3.95, 1970 | 3.933333, 1971 | 3.916667, 1972 | 3.816667, 1973 | 3.8, 1974 | 3.766667, 1975 | 3.733333, 1976 | 3.666667, 1977 | 3.583333, 1978 | 3.5, 1979 | 3.45, 1980 | 3.433333, 1981 | 3.4, 1982 | 3.366667, 1983 | 3.333333, 1984 | 3.316667, 1985 | 3.3, 1986 | 3.283333, 1987 | 3.266667, 1988 | 3.233333, 1989 | 3.3, 1990 | 3.383333, 1991 | 3.433333, 1992 | 3.5, 1993 | 3.566667, 1994 | 3.616667, 1995 | 3.683333, 1996 | 3.75, 1997 | 3.816667, 1998 | 3.866667, 1999 | 3.933333, 2000 | 4, 2001 | 4.066667, 2002 | 4.166667, 2003 | 4.25, 2004 | 4.316667, 2005 | 4.35, 2006 | 4.366667, 2007 | 4.416667, 2008 | 4.466667, 2009 | 4.516667, 2010 | 4.566667, 2011 | 4.6, 2012 | 4.616667, 2013 | 4.633333, 2014 | 4.516667, 2015 | 4.383333, 2016 | 4.25, 2017 | 4.15, 2018 | 4.066667, 2019 | 3.983333, 2020 | 3.916667, 2021 | 3.85, 2022 | 3.8, 2023 | 3.766667, 2024 | 3.733333, 2025 | 3.7, 2026 | 3.666667, 2027 | 3.616667, 2028 | 3.55, 2029 | 3.483333, 2030 | 3.433333, 2031 | 3.383333, 2032 | 3.35, 2033 | 3.283333, 2034 | 3.216667, 2035 | 3.133333, 2036 | 3.05, 2037 | 2.7, 2038 | 2.633333, 2039 | 2.583333, 2040 | 2.533333, 2041 | 2.516667, 2042 | 2.5, 2043 | 2.483333, 2044 | 2.45, 2045 | 2.416667, 2046 | 2.366667, 2047 | 2.333333, 2048 | 2.283333, 2049 | 2.233333, 2050 | 2.183333, 2051 | 2.15, 2052 | 2.1, 2053 | 2.05, 2054 | 2, 2055 | 1.95, 2056 | 1.9, 2057 | 1.866667, 2058 | 1.833333, 2059 | 1.783333, 2060 | 1.733333, 2061 | 1.716667, 2062 | 1.683333, 2063 | 1.666667, 2064 | 1.633333, 2065 | 1.6, 2066 | 1.566667, 2067 | 1.55, 2068 | 1.516667, 2069 | 1.483333, 2070 | 1.45, 2071 | 1.433333, 2072 | 1.4, 2073 | 1.366667, 2074 | 1.333333, 2075 | 1.316667, 2076 | 1.283333, 2077 | 1.25, 2078 | 1.2, 2079 | 1.15, 2080 | 1.1, 2081 | 1.05, 2082 | 1, 2083 | 0.95, 2084 | 0.9166667, 2085 | 0.8666667, 2086 | 0.8166667, 2087 | 0.7666667, 2088 | 0.7333333, 2089 | 0.7166666, 2090 | 0.7, 2091 | 0.6833333, 2092 | 0.6666667, 2093 | 0.65, 2094 | 0.6333333, 2095 | 0.6166667, 2096 | 0.6, 2097 | 0.5833333, 2098 | 0.5666667, 2099 | 0.55, 2100 | 0.5333334, 2101 | 0.5166667, 2102 | 0.5, 2103 | 0.4833333, 2104 | 0.45, 2105 | 0.4333333, 2106 | 0.4, 2107 | 0.3833333, 2108 | 0.3833333, 2109 | 0.3833333, 2110 | 0.3833333, 2111 | 0.3666667, 2112 | 0.3666667, 2113 | 0.3666667, 2114 | 0.35, 2115 | 0.3333333, 2116 | 0.3, 2117 | 0.2833333, 2118 | 0.2833333, 2119 | 0.2666667, 2120 | 0.25, 2121 | 0.2333333, 2122 | 0.2166667, 2123 | 0.2166667, 2124 | 0.2166667, 2125 | 0.2166667, 2126 | 0.2, 2127 | 0.1833333, 2128 | 0.15, 2129 | 0.1333333, 2130 | 0.1333333, 2131 | 0.1333333, 2132 | 0.1333333, 2133 | 0.1166667, 2134 | 0.1166667, 2135 | 0.1166667, 2136 | 0.1166667, 2137 | 0.1166667, 2138 | 0.1166667, 2139 | 0, 2140 | 0, 2141 | 0 2142 | ] -------------------------------------------------------------------------------- /data and analysis/TouchDataAnalysis.playground/Sources/Analysis.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | 4 | extension SequenceType { 5 | public var asArray:[Self.Generator.Element] { 6 | return Array(self) 7 | } 8 | } 9 | 10 | //extension SequenceType { 11 | // public var asSet:Set { 12 | // return Set(self) 13 | // } 14 | //} 15 | 16 | func descriptiveStats(items:[Float]) -> Void 17 | { 18 | guard items.isEmpty == false else { return } 19 | let n = items.count 20 | let minimum = items.reduce(items.first!, combine: min) 21 | let maximum = items.reduce(items.first!, combine: max) 22 | let distinctItemsCount = Set(items).count 23 | } 24 | 25 | /// Compute frequenceis of values in an arrray 26 | public func frequencies(items:[T]) -> [T:Int] { 27 | return items.reduce([T:Int](), combine: { (var d,item) in 28 | d[item] = 1 + (d[item] ?? 0) 29 | return d 30 | }) 31 | } 32 | 33 | extension Dictionary { 34 | public func mapKeys(transform:(Key -> T)) -> Dictionary { 35 | var result:[T:Value] = [:] 36 | for (k,v) in self { 37 | result[transform(k)] = v 38 | } 39 | return result 40 | } 41 | } 42 | 43 | /// Super primitive column chart histogram 44 | public class Histogram : UIView 45 | { 46 | public var data:[String:Int] = [:] { didSet { self.setNeedsDisplay() } } 47 | var maxCount:Int = 500 48 | 49 | public override func drawRect(rect: CGRect) 50 | { 51 | let keys = data.keys.asArray.sort() 52 | let columnsCount = keys.count 53 | let columnGap = rect.size.width / CGFloat(columnsCount + 1) 54 | let columnXs = Array(columnGap.stride(to: CGRectGetMaxX(rect), by: columnGap)) 55 | let columnYs = keys.map({ (CGFloat(self.data[$0]!) / CGFloat(maxCount)) * rect.size.height }) 56 | 57 | self.tintColor.setStroke() 58 | 59 | for (x,y) in zip(columnXs, columnYs) { 60 | let columnPath = UIBezierPath() 61 | columnPath.lineWidth = 4 62 | columnPath.moveToPoint(CGPoint(x: x, y: 0)) 63 | columnPath.addLineToPoint(CGPoint(x: x, y: y)) 64 | columnPath.stroke() 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /data and analysis/TouchDataAnalysis.playground/Sources/ParsedData.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | // 60 seconds of data produced by gradually varying force applied by my thumb. 4 | let datafilename = "04317CA4-3AD0-4776-861E-78040D8BC3E2.json" 5 | 6 | let filename = datafilename 7 | 8 | let path = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)! 9 | let data = NSData(contentsOfURL: path)! 10 | let array = try! NSJSONSerialization.JSONObjectWithData(data, options: []) as! NSArray 11 | 12 | public let exportedItems:[CGFloat] = array.map({ ($0 as! NSNumber).floatValue }).map({ CGFloat($0) } ) 13 | -------------------------------------------------------------------------------- /data and analysis/TouchDataAnalysis.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /data and analysis/TouchDataAnalysis.playground/playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data and analysis/TouchDataAnalysis.playground/playground.xcworkspace/xcuserdata/alexis.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algal/TouchVisualizer/ee2203c2e134119f8da4d23f1e4431d7807dc8cf/data and analysis/TouchDataAnalysis.playground/playground.xcworkspace/xcuserdata/alexis.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /data and analysis/TouchDataAnalysis.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data and analysis/data-60secondsVariedForce/.ipynb_checkpoints/Untitled-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 0 6 | } 7 | -------------------------------------------------------------------------------- /data and analysis/data-60secondsVariedForce/04317CA4-3AD0-4776-861E-78040D8BC3E2.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0, 3 | 0.05, 4 | 0.1333333, 5 | 0.25, 6 | 0.3666667, 7 | 0.4833333, 8 | 0.6166667, 9 | 0.75, 10 | 0.9, 11 | 1.033333, 12 | 1.183333, 13 | 1.333333, 14 | 1.533333, 15 | 1.633333, 16 | 1.783333, 17 | 1.983333, 18 | 2.183333, 19 | 2.383333, 20 | 2.566667, 21 | 2.733333, 22 | 2.883333, 23 | 3.033333, 24 | 3.216667, 25 | 3.366667, 26 | 3.5, 27 | 3.633333, 28 | 3.766667, 29 | 3.9, 30 | 4.033333, 31 | 4.166667, 32 | 4.316667, 33 | 4.483333, 34 | 4.683333, 35 | 4.883333, 36 | 5.033333, 37 | 5.1, 38 | 5.1, 39 | 4.883333, 40 | 4.55, 41 | 4.133333, 42 | 3.783333, 43 | 3.466667, 44 | 3.166667, 45 | 2.866667, 46 | 2.566667, 47 | 2.25, 48 | 1.983333, 49 | 1.766667, 50 | 1.616667, 51 | 1.516667, 52 | 1.466667, 53 | 1.416667, 54 | 1.383333, 55 | 1.416667, 56 | 1.5, 57 | 1.566667, 58 | 1.616667, 59 | 1.666667, 60 | 1.716667, 61 | 1.75, 62 | 1.8, 63 | 1.883333, 64 | 1.95, 65 | 2.016667, 66 | 2.066667, 67 | 2.083333, 68 | 2.116667, 69 | 2.2, 70 | 2.333333, 71 | 2.466667, 72 | 2.583333, 73 | 2.7, 74 | 2.783333, 75 | 2.85, 76 | 2.983333, 77 | 3.133333, 78 | 3.3, 79 | 3.45, 80 | 3.583333, 81 | 3.7, 82 | 3.816667, 83 | 3.933333, 84 | 4.033333, 85 | 4.133333, 86 | 4.233333, 87 | 4.35, 88 | 4.466667, 89 | 4.566667, 90 | 4.666667, 91 | 4.766667, 92 | 4.883333, 93 | 5.016667, 94 | 5.15, 95 | 5.3, 96 | 5.466667, 97 | 5.666667, 98 | 5.883333, 99 | 6.083333, 100 | 6.283333, 101 | 6.5, 102 | 6.666667, 103 | 6.666667, 104 | 6.666667, 105 | 6.666667, 106 | 6.666667, 107 | 6.666667, 108 | 6.666667, 109 | 6.666667, 110 | 6.666667, 111 | 6.666667, 112 | 6.666667, 113 | 6.666667, 114 | 6.616667, 115 | 6.183333, 116 | 5.55, 117 | 4.85, 118 | 4.2, 119 | 3.7, 120 | 3.3, 121 | 2.983333, 122 | 2.733333, 123 | 2.583333, 124 | 2.466667, 125 | 2.35, 126 | 2.233333, 127 | 2.083333, 128 | 1.883333, 129 | 1.633333, 130 | 1.383333, 131 | 1.15, 132 | 0.9166667, 133 | 0.7166666, 134 | 0.55, 135 | 0, 136 | 0.06666667, 137 | 0.15, 138 | 0.25, 139 | 0.3166667, 140 | 0.4, 141 | 0.5, 142 | 0.6333333, 143 | 0.7666667, 144 | 0.8833333, 145 | 0.95, 146 | 1.033333, 147 | 1.133333, 148 | 1.25, 149 | 1.366667, 150 | 1.483333, 151 | 1.566667, 152 | 1.65, 153 | 1.733333, 154 | 1.8, 155 | 1.9, 156 | 2, 157 | 2.133333, 158 | 2.216667, 159 | 2.316667, 160 | 2.433333, 161 | 2.55, 162 | 2.633333, 163 | 2.733333, 164 | 2.8, 165 | 2.866667, 166 | 2.95, 167 | 3.016667, 168 | 3.083333, 169 | 3.183333, 170 | 3.266667, 171 | 3.366667, 172 | 3.45, 173 | 3.533333, 174 | 3.616667, 175 | 3.683333, 176 | 3.716667, 177 | 3.75, 178 | 3.766667, 179 | 3.766667, 180 | 3.766667, 181 | 3.766667, 182 | 3.766667, 183 | 3.733333, 184 | 3.6, 185 | 3.483333, 186 | 3.383333, 187 | 3.3, 188 | 3.25, 189 | 3.233333, 190 | 3.233333, 191 | 3.233333, 192 | 3.233333, 193 | 3.233333, 194 | 3.233333, 195 | 3.233333, 196 | 3.233333, 197 | 3.233333, 198 | 3.25, 199 | 3.25, 200 | 3.25, 201 | 3.25, 202 | 3.25, 203 | 3.25, 204 | 3.25, 205 | 3.25, 206 | 3.25, 207 | 3.25, 208 | 3.233333, 209 | 3.183333, 210 | 3.2, 211 | 3.366667, 212 | 3.5, 213 | 3.65, 214 | 3.816667, 215 | 3.966667, 216 | 4.116667, 217 | 4.3, 218 | 4.466667, 219 | 4.633333, 220 | 4.75, 221 | 4.866667, 222 | 4.966667, 223 | 5.1, 224 | 5.25, 225 | 5.4, 226 | 5.55, 227 | 5.666667, 228 | 5.733333, 229 | 5.766667, 230 | 5.833333, 231 | 5.916667, 232 | 6.016667, 233 | 6.116667, 234 | 6.2, 235 | 6.283333, 236 | 6.366667, 237 | 6.433333, 238 | 6.5, 239 | 6.55, 240 | 6.65, 241 | 6.666667, 242 | 6.666667, 243 | 6.666667, 244 | 6.666667, 245 | 6.666667, 246 | 6.666667, 247 | 6.666667, 248 | 6.666667, 249 | 6.666667, 250 | 6.666667, 251 | 6.666667, 252 | 6.666667, 253 | 6.666667, 254 | 6.666667, 255 | 6.666667, 256 | 6.666667, 257 | 6.666667, 258 | 6.666667, 259 | 6.65, 260 | 6.033333, 261 | 5.333333, 262 | 4.6, 263 | 3.883333, 264 | 3.25, 265 | 2.7, 266 | 2.25, 267 | 1.95, 268 | 1.7, 269 | 1.533333, 270 | 1.4, 271 | 1.316667, 272 | 1.25, 273 | 1.183333, 274 | 1.133333, 275 | 1.05, 276 | 1, 277 | 0.95, 278 | 0.9, 279 | 0.8666667, 280 | 0.8333333, 281 | 0.7833334, 282 | 0.7333333, 283 | 0.7, 284 | 0.6666667, 285 | 0.65, 286 | 0.65, 287 | 0.65, 288 | 0.6666667, 289 | 0.75, 290 | 0.9, 291 | 1.066667, 292 | 1.266667, 293 | 1.433333, 294 | 1.583333, 295 | 1.75, 296 | 1.916667, 297 | 2.05, 298 | 2.166667, 299 | 2.25, 300 | 2.333333, 301 | 2.433333, 302 | 2.516667, 303 | 2.6, 304 | 2.683333, 305 | 2.766667, 306 | 2.833333, 307 | 2.916667, 308 | 2.983333, 309 | 3.05, 310 | 3.133333, 311 | 3.2, 312 | 3.266667, 313 | 3.316667, 314 | 3.383333, 315 | 3.433333, 316 | 3.5, 317 | 3.583333, 318 | 3.683333, 319 | 3.766667, 320 | 3.833333, 321 | 3.883333, 322 | 3.966667, 323 | 4.033333, 324 | 4.116667, 325 | 4.216667, 326 | 4.316667, 327 | 4.4, 328 | 4.483333, 329 | 4.55, 330 | 4.65, 331 | 4.733333, 332 | 4.816667, 333 | 4.9, 334 | 4.983333, 335 | 5.066667, 336 | 5.166667, 337 | 5.233333, 338 | 5.3, 339 | 5.35, 340 | 5.4, 341 | 5.45, 342 | 5.466667, 343 | 5.483333, 344 | 5.5, 345 | 5.533333, 346 | 5.583333, 347 | 5.65, 348 | 5.716667, 349 | 5.766667, 350 | 5.8, 351 | 5.816667, 352 | 5.883333, 353 | 5.966667, 354 | 6.066667, 355 | 6.183333, 356 | 6.25, 357 | 6.283333, 358 | 6.316667, 359 | 6.4, 360 | 6.533333, 361 | 6.666667, 362 | 6.666667, 363 | 6.6, 364 | 6.45, 365 | 6.383333, 366 | 6.35, 367 | 6.333333, 368 | 6.283333, 369 | 6.15, 370 | 5.916667, 371 | 5.633333, 372 | 5.316667, 373 | 5.016667, 374 | 4.733333, 375 | 4.466667, 376 | 4.2, 377 | 3.95, 378 | 3.7, 379 | 3.416667, 380 | 3.2, 381 | 3.016667, 382 | 2.883333, 383 | 2.783333, 384 | 2.733333, 385 | 2.666667, 386 | 2.6, 387 | 2.516667, 388 | 2.416667, 389 | 2.283333, 390 | 2.183333, 391 | 2.116667, 392 | 2.083333, 393 | 2.066667, 394 | 2.033333, 395 | 2, 396 | 1.933333, 397 | 1.85, 398 | 1.766667, 399 | 1.65, 400 | 1.566667, 401 | 1.466667, 402 | 1.383333, 403 | 1.3, 404 | 1.216667, 405 | 1.133333, 406 | 1.033333, 407 | 0.9666666, 408 | 0.9, 409 | 0.8333333, 410 | 0.7666667, 411 | 0.7, 412 | 0.6333333, 413 | 0.55, 414 | 0.4833333, 415 | 0.4333333, 416 | 0.3833333, 417 | 0.35, 418 | 0.3333333, 419 | 0.3166667, 420 | 0.3, 421 | 0.2833333, 422 | 0.2666667, 423 | 0.25, 424 | 0.2166667, 425 | 0.2, 426 | 0.1833333, 427 | 0.1666667, 428 | 0.15, 429 | 0.1333333, 430 | 0.1333333, 431 | 0.1166667, 432 | 0.1, 433 | 0.08333334, 434 | 0.06666667, 435 | 0.06666667, 436 | 0.06666667, 437 | 0.06666667, 438 | 0.06666667, 439 | 0.06666667, 440 | 0.06666667, 441 | 0.06666667, 442 | 0.06666667, 443 | 0.06666667, 444 | 0.06666667, 445 | 0.06666667, 446 | 0.06666667, 447 | 0.06666667, 448 | 0.06666667, 449 | 0, 450 | 0.01666667, 451 | 0.03333334, 452 | 0.05, 453 | 0.06666667, 454 | 0.08333334, 455 | 0.1, 456 | 0.1166667, 457 | 0.1333333, 458 | 0.15, 459 | 0.1666667, 460 | 0.1833333, 461 | 0.2, 462 | 0.2166667, 463 | 0.2333333, 464 | 0.25, 465 | 0.2833333, 466 | 0.3166667, 467 | 0.3333333, 468 | 0.35, 469 | 0.3833333, 470 | 0.4, 471 | 0.4333333, 472 | 0.4666667, 473 | 0.5, 474 | 0.55, 475 | 0.5833333, 476 | 0.6166667, 477 | 0.65, 478 | 0.6833333, 479 | 0.7166666, 480 | 0.75, 481 | 0.8, 482 | 0.85, 483 | 0.8833333, 484 | 0.9166667, 485 | 0.95, 486 | 0.9666666, 487 | 0.9833333, 488 | 1.016667, 489 | 1.05, 490 | 1.083333, 491 | 1.1, 492 | 1.116667, 493 | 1.133333, 494 | 1.15, 495 | 1.166667, 496 | 1.183333, 497 | 1.2, 498 | 1.216667, 499 | 1.25, 500 | 1.283333, 501 | 1.316667, 502 | 1.333333, 503 | 1.35, 504 | 1.366667, 505 | 1.383333, 506 | 1.416667, 507 | 1.433333, 508 | 1.466667, 509 | 1.483333, 510 | 1.5, 511 | 1.516667, 512 | 1.533333, 513 | 1.55, 514 | 1.566667, 515 | 1.583333, 516 | 1.6, 517 | 1.616667, 518 | 1.633333, 519 | 1.65, 520 | 1.683333, 521 | 1.7, 522 | 1.716667, 523 | 1.733333, 524 | 1.766667, 525 | 1.8, 526 | 1.833333, 527 | 1.883333, 528 | 1.933333, 529 | 1.95, 530 | 1.966667, 531 | 1.983333, 532 | 2, 533 | 2.016667, 534 | 2.033333, 535 | 2.05, 536 | 2.066667, 537 | 2.083333, 538 | 2.1, 539 | 2.116667, 540 | 2.133333, 541 | 2.15, 542 | 2.166667, 543 | 2.183333, 544 | 2.2, 545 | 2.216667, 546 | 2.233333, 547 | 2.233333, 548 | 2.233333, 549 | 2.25, 550 | 2.266667, 551 | 2.283333, 552 | 2.3, 553 | 2.316667, 554 | 2.35, 555 | 2.366667, 556 | 2.383333, 557 | 2.416667, 558 | 2.45, 559 | 2.466667, 560 | 2.5, 561 | 2.55, 562 | 2.6, 563 | 2.65, 564 | 2.683333, 565 | 2.7, 566 | 2.716667, 567 | 2.733333, 568 | 2.75, 569 | 2.783333, 570 | 2.8, 571 | 2.833333, 572 | 2.866667, 573 | 2.9, 574 | 2.933333, 575 | 2.95, 576 | 2.983333, 577 | 3.033333, 578 | 3.05, 579 | 3.083333, 580 | 3.1, 581 | 3.116667, 582 | 3.133333, 583 | 3.15, 584 | 3.166667, 585 | 3.2, 586 | 3.25, 587 | 3.283333, 588 | 3.316667, 589 | 3.35, 590 | 3.366667, 591 | 3.383333, 592 | 3.383333, 593 | 3.383333, 594 | 3.383333, 595 | 3.383333, 596 | 3.4, 597 | 3.416667, 598 | 3.466667, 599 | 3.516667, 600 | 3.583333, 601 | 3.633333, 602 | 3.683333, 603 | 3.733333, 604 | 3.783333, 605 | 3.816667, 606 | 3.85, 607 | 3.883333, 608 | 3.9, 609 | 3.916667, 610 | 3.933333, 611 | 3.966667, 612 | 4, 613 | 4.016667, 614 | 4.033333, 615 | 4.05, 616 | 4.083333, 617 | 4.1, 618 | 4.116667, 619 | 4.133333, 620 | 4.15, 621 | 4.183333, 622 | 4.216667, 623 | 4.25, 624 | 4.283333, 625 | 4.3, 626 | 4.333333, 627 | 4.366667, 628 | 4.383333, 629 | 4.416667, 630 | 4.45, 631 | 4.466667, 632 | 4.483333, 633 | 4.5, 634 | 4.516667, 635 | 4.533333, 636 | 4.55, 637 | 4.566667, 638 | 4.583333, 639 | 4.616667, 640 | 4.7, 641 | 4.783333, 642 | 4.816667, 643 | 4.866667, 644 | 4.9, 645 | 4.933333, 646 | 4.983333, 647 | 5.05, 648 | 5.116667, 649 | 5.183333, 650 | 5.233333, 651 | 5.283333, 652 | 5.333333, 653 | 5.366667, 654 | 5.4, 655 | 5.433333, 656 | 5.466667, 657 | 5.5, 658 | 5.533333, 659 | 5.566667, 660 | 5.6, 661 | 5.633333, 662 | 5.683333, 663 | 5.716667, 664 | 5.75, 665 | 5.783333, 666 | 5.8, 667 | 5.816667, 668 | 5.816667, 669 | 5.833333, 670 | 5.85, 671 | 5.866667, 672 | 5.9, 673 | 5.916667, 674 | 5.933333, 675 | 5.95, 676 | 5.966667, 677 | 5.983333, 678 | 6, 679 | 6.033333, 680 | 6.066667, 681 | 6.083333, 682 | 6.1, 683 | 6.116667, 684 | 6.15, 685 | 6.183333, 686 | 6.25, 687 | 6.316667, 688 | 6.383333, 689 | 6.45, 690 | 6.466667, 691 | 6.483333, 692 | 6.533333, 693 | 6.6, 694 | 6.666667, 695 | 6.666667, 696 | 6.666667, 697 | 6.666667, 698 | 6.666667, 699 | 6.666667, 700 | 6.666667, 701 | 6.666667, 702 | 6.666667, 703 | 6.666667, 704 | 6.583333, 705 | 6.4, 706 | 6.233333, 707 | 6.083333, 708 | 5.916667, 709 | 5.75, 710 | 5.6, 711 | 5.483333, 712 | 5.366667, 713 | 5.283333, 714 | 5.216667, 715 | 5.166667, 716 | 5.133333, 717 | 5.083333, 718 | 5.05, 719 | 5.016667, 720 | 5, 721 | 4.966667, 722 | 4.95, 723 | 4.916667, 724 | 4.883333, 725 | 4.866667, 726 | 4.85, 727 | 4.816667, 728 | 4.783333, 729 | 4.733333, 730 | 4.7, 731 | 4.65, 732 | 4.616667, 733 | 4.566667, 734 | 4.516667, 735 | 4.466667, 736 | 4.433333, 737 | 4.4, 738 | 4.35, 739 | 4.266667, 740 | 4.2, 741 | 4.116667, 742 | 4.066667, 743 | 4.033333, 744 | 4, 745 | 3.983333, 746 | 3.966667, 747 | 3.95, 748 | 3.933333, 749 | 3.916667, 750 | 3.883333, 751 | 3.833333, 752 | 3.8, 753 | 3.783333, 754 | 3.766667, 755 | 3.75, 756 | 3.733333, 757 | 3.716667, 758 | 3.7, 759 | 3.666667, 760 | 3.65, 761 | 3.633333, 762 | 3.583333, 763 | 3.55, 764 | 3.5, 765 | 3.483333, 766 | 3.466667, 767 | 3.45, 768 | 3.433333, 769 | 3.4, 770 | 3.366667, 771 | 3.333333, 772 | 3.3, 773 | 3.283333, 774 | 3.266667, 775 | 3.233333, 776 | 3.183333, 777 | 3.133333, 778 | 3.1, 779 | 3.083333, 780 | 3.016667, 781 | 2.95, 782 | 2.9, 783 | 2.866667, 784 | 2.85, 785 | 2.85, 786 | 2.816667, 787 | 2.8, 788 | 2.766667, 789 | 2.75, 790 | 2.716667, 791 | 2.7, 792 | 2.666667, 793 | 2.633333, 794 | 2.6, 795 | 2.55, 796 | 2.516667, 797 | 2.5, 798 | 2.466667, 799 | 2.433333, 800 | 2.366667, 801 | 2.3, 802 | 2.233333, 803 | 2.166667, 804 | 2.116667, 805 | 2.05, 806 | 1.983333, 807 | 1.916667, 808 | 1.85, 809 | 1.783333, 810 | 1.683333, 811 | 1.6, 812 | 1.533333, 813 | 1.483333, 814 | 1.45, 815 | 1.416667, 816 | 1.383333, 817 | 1.366667, 818 | 1.35, 819 | 1.333333, 820 | 1.316667, 821 | 1.283333, 822 | 1.266667, 823 | 1.233333, 824 | 1.216667, 825 | 1.2, 826 | 1.183333, 827 | 1.15, 828 | 1.133333, 829 | 1.083333, 830 | 1.033333, 831 | 1, 832 | 0.9666666, 833 | 0.95, 834 | 0.9166667, 835 | 0.9, 836 | 0.8666667, 837 | 0.8333333, 838 | 0.8, 839 | 0.7666667, 840 | 0.7333333, 841 | 0.7166666, 842 | 0.7, 843 | 0.6833333, 844 | 0.6666667, 845 | 0.65, 846 | 0.6166667, 847 | 0.5833333, 848 | 0.55, 849 | 0.5166667, 850 | 0.4833333, 851 | 0.4666667, 852 | 0.45, 853 | 0.4333333, 854 | 0.4166667, 855 | 0.4, 856 | 0.3833333, 857 | 0.3666667, 858 | 0.35, 859 | 0.3333333, 860 | 0.3166667, 861 | 0.3, 862 | 0.2833333, 863 | 0.2666667, 864 | 0.25, 865 | 0.2333333, 866 | 0.2166667, 867 | 0.2, 868 | 0.1833333, 869 | 0.1666667, 870 | 0.15, 871 | 0.1333333, 872 | 0.1166667, 873 | 0.1166667, 874 | 0.1, 875 | 0.1, 876 | 0.1, 877 | 0.1, 878 | 0.1, 879 | 0.1, 880 | 0.1, 881 | 0, 882 | 0.01666667, 883 | 0.03333334, 884 | 0.05, 885 | 0.06666667, 886 | 0.08333334, 887 | 0.1, 888 | 0.1166667, 889 | 0.1333333, 890 | 0.15, 891 | 0.1666667, 892 | 0.1833333, 893 | 0.2, 894 | 0.2166667, 895 | 0.2333333, 896 | 0.25, 897 | 0.2666667, 898 | 0.2833333, 899 | 0.3, 900 | 0.3166667, 901 | 0.3333333, 902 | 0.35, 903 | 0.3666667, 904 | 0.3833333, 905 | 0.4, 906 | 0.4166667, 907 | 0.4333333, 908 | 0.45, 909 | 0.4666667, 910 | 0.5, 911 | 0.5166667, 912 | 0.55, 913 | 0.5666667, 914 | 0.5833333, 915 | 0.6, 916 | 0.6166667, 917 | 0.6333333, 918 | 0.65, 919 | 0.6666667, 920 | 0.6833333, 921 | 0.7, 922 | 0.7166666, 923 | 0.7333333, 924 | 0.7666667, 925 | 0.7833334, 926 | 0.8166667, 927 | 0.8333333, 928 | 0.8666667, 929 | 0.9, 930 | 0.9333333, 931 | 0.9666666, 932 | 1, 933 | 1.033333, 934 | 1.05, 935 | 1.083333, 936 | 1.1, 937 | 1.133333, 938 | 1.166667, 939 | 1.2, 940 | 1.25, 941 | 1.283333, 942 | 1.316667, 943 | 1.35, 944 | 1.366667, 945 | 1.4, 946 | 1.416667, 947 | 1.466667, 948 | 1.5, 949 | 1.55, 950 | 1.566667, 951 | 1.583333, 952 | 1.6, 953 | 1.616667, 954 | 1.633333, 955 | 1.65, 956 | 1.666667, 957 | 1.683333, 958 | 1.716667, 959 | 1.75, 960 | 1.783333, 961 | 1.8, 962 | 1.816667, 963 | 1.833333, 964 | 1.85, 965 | 1.866667, 966 | 1.916667, 967 | 1.933333, 968 | 1.966667, 969 | 1.983333, 970 | 2.016667, 971 | 2.05, 972 | 2.066667, 973 | 2.1, 974 | 2.133333, 975 | 2.166667, 976 | 2.183333, 977 | 2.2, 978 | 2.216667, 979 | 2.233333, 980 | 2.266667, 981 | 2.283333, 982 | 2.3, 983 | 2.316667, 984 | 2.333333, 985 | 2.35, 986 | 2.366667, 987 | 2.383333, 988 | 2.4, 989 | 2.416667, 990 | 2.45, 991 | 2.483333, 992 | 2.516667, 993 | 2.55, 994 | 2.566667, 995 | 2.583333, 996 | 2.6, 997 | 2.616667, 998 | 2.633333, 999 | 2.666667, 1000 | 2.7, 1001 | 2.716667, 1002 | 2.733333, 1003 | 2.75, 1004 | 2.766667, 1005 | 2.783333, 1006 | 2.8, 1007 | 2.833333, 1008 | 2.85, 1009 | 2.866667, 1010 | 2.883333, 1011 | 2.9, 1012 | 2.916667, 1013 | 2.933333, 1014 | 2.95, 1015 | 2.966667, 1016 | 2.983333, 1017 | 3, 1018 | 3.016667, 1019 | 3.033333, 1020 | 3.05, 1021 | 3.066667, 1022 | 3.083333, 1023 | 3.1, 1024 | 3.116667, 1025 | 3.133333, 1026 | 3.15, 1027 | 3.166667, 1028 | 3.183333, 1029 | 3.216667, 1030 | 3.233333, 1031 | 3.25, 1032 | 3.266667, 1033 | 3.283333, 1034 | 3.3, 1035 | 3.316667, 1036 | 3.333333, 1037 | 3.35, 1038 | 3.366667, 1039 | 3.383333, 1040 | 3.4, 1041 | 3.433333, 1042 | 3.45, 1043 | 3.466667, 1044 | 3.483333, 1045 | 3.5, 1046 | 3.516667, 1047 | 3.533333, 1048 | 3.55, 1049 | 3.566667, 1050 | 3.583333, 1051 | 3.6, 1052 | 3.616667, 1053 | 3.633333, 1054 | 3.683333, 1055 | 3.7, 1056 | 3.716667, 1057 | 3.733333, 1058 | 3.75, 1059 | 3.766667, 1060 | 3.783333, 1061 | 3.8, 1062 | 3.816667, 1063 | 3.85, 1064 | 3.883333, 1065 | 3.9, 1066 | 3.916667, 1067 | 3.933333, 1068 | 3.95, 1069 | 3.966667, 1070 | 3.983333, 1071 | 4, 1072 | 4.016667, 1073 | 4.033333, 1074 | 4.05, 1075 | 4.066667, 1076 | 4.083333, 1077 | 4.116667, 1078 | 4.133333, 1079 | 4.15, 1080 | 4.166667, 1081 | 4.183333, 1082 | 4.2, 1083 | 4.233333, 1084 | 4.25, 1085 | 4.266667, 1086 | 4.283333, 1087 | 4.3, 1088 | 4.316667, 1089 | 4.333333, 1090 | 4.35, 1091 | 4.383333, 1092 | 4.416667, 1093 | 4.45, 1094 | 4.466667, 1095 | 4.5, 1096 | 4.516667, 1097 | 4.55, 1098 | 4.566667, 1099 | 4.583333, 1100 | 4.616667, 1101 | 4.633333, 1102 | 4.666667, 1103 | 4.7, 1104 | 4.733333, 1105 | 4.75, 1106 | 4.766667, 1107 | 4.783333, 1108 | 4.8, 1109 | 4.816667, 1110 | 4.833333, 1111 | 4.85, 1112 | 4.866667, 1113 | 4.883333, 1114 | 4.9, 1115 | 4.933333, 1116 | 4.95, 1117 | 4.983333, 1118 | 5, 1119 | 5.016667, 1120 | 5.033333, 1121 | 5.05, 1122 | 5.066667, 1123 | 5.083333, 1124 | 5.133333, 1125 | 5.15, 1126 | 5.183333, 1127 | 5.2, 1128 | 5.216667, 1129 | 5.233333, 1130 | 5.266667, 1131 | 5.3, 1132 | 5.333333, 1133 | 5.35, 1134 | 5.366667, 1135 | 5.383333, 1136 | 5.4, 1137 | 5.433333, 1138 | 5.45, 1139 | 5.466667, 1140 | 5.483333, 1141 | 5.5, 1142 | 5.516667, 1143 | 5.533333, 1144 | 5.55, 1145 | 5.566667, 1146 | 5.6, 1147 | 5.616667, 1148 | 5.633333, 1149 | 5.65, 1150 | 5.683333, 1151 | 5.716667, 1152 | 5.733333, 1153 | 5.766667, 1154 | 5.783333, 1155 | 5.8, 1156 | 5.833333, 1157 | 5.85, 1158 | 5.9, 1159 | 5.95, 1160 | 6.016667, 1161 | 6.066667, 1162 | 6.1, 1163 | 6.133333, 1164 | 6.15, 1165 | 6.183333, 1166 | 6.233333, 1167 | 6.283333, 1168 | 6.333333, 1169 | 6.383333, 1170 | 6.4, 1171 | 6.416667, 1172 | 6.433333, 1173 | 6.45, 1174 | 6.466667, 1175 | 6.5, 1176 | 6.516667, 1177 | 6.533333, 1178 | 6.616667, 1179 | 6.666667, 1180 | 6.666667, 1181 | 6.666667, 1182 | 6.666667, 1183 | 6.666667, 1184 | 6.666667, 1185 | 6.666667, 1186 | 6.666667, 1187 | 6.666667, 1188 | 6.666667, 1189 | 6.666667, 1190 | 6.666667, 1191 | 6.666667, 1192 | 6.666667, 1193 | 6.666667, 1194 | 6.666667, 1195 | 6.666667, 1196 | 6.666667, 1197 | 6.666667, 1198 | 6.666667, 1199 | 6.666667, 1200 | 6.666667, 1201 | 6.666667, 1202 | 6.666667, 1203 | 6.666667, 1204 | 6.666667, 1205 | 6.666667, 1206 | 6.666667, 1207 | 6.666667, 1208 | 6.65, 1209 | 6.533333, 1210 | 6.416667, 1211 | 6.283333, 1212 | 6.166667, 1213 | 6.033333, 1214 | 5.883333, 1215 | 5.716667, 1216 | 5.55, 1217 | 5.383333, 1218 | 5.25, 1219 | 5.133333, 1220 | 5.016667, 1221 | 4.916667, 1222 | 4.833333, 1223 | 4.75, 1224 | 4.666667, 1225 | 4.6, 1226 | 4.533333, 1227 | 4.483333, 1228 | 4.433333, 1229 | 4.4, 1230 | 4.366667, 1231 | 4.3, 1232 | 4.25, 1233 | 4.183333, 1234 | 4.133333, 1235 | 4.1, 1236 | 4.083333, 1237 | 4.05, 1238 | 4.033333, 1239 | 4.016667, 1240 | 4, 1241 | 3.983333, 1242 | 3.966667, 1243 | 3.933333, 1244 | 3.9, 1245 | 3.866667, 1246 | 3.85, 1247 | 3.833333, 1248 | 3.816667, 1249 | 3.8, 1250 | 3.783333, 1251 | 3.75, 1252 | 3.733333, 1253 | 3.716667, 1254 | 3.7, 1255 | 3.65, 1256 | 3.633333, 1257 | 3.616667, 1258 | 3.6, 1259 | 3.583333, 1260 | 3.566667, 1261 | 3.55, 1262 | 3.516667, 1263 | 3.466667, 1264 | 3.416667, 1265 | 3.383333, 1266 | 3.35, 1267 | 3.333333, 1268 | 3.316667, 1269 | 3.3, 1270 | 3.266667, 1271 | 3.216667, 1272 | 3.166667, 1273 | 3.116667, 1274 | 3.1, 1275 | 3.066667, 1276 | 3.05, 1277 | 3.016667, 1278 | 3, 1279 | 2.95, 1280 | 2.916667, 1281 | 2.883333, 1282 | 2.866667, 1283 | 2.833333, 1284 | 2.8, 1285 | 2.75, 1286 | 2.666667, 1287 | 2.6, 1288 | 2.533333, 1289 | 2.483333, 1290 | 2.433333, 1291 | 2.416667, 1292 | 2.366667, 1293 | 2.333333, 1294 | 2.3, 1295 | 2.283333, 1296 | 2.266667, 1297 | 2.233333, 1298 | 2.216667, 1299 | 2.183333, 1300 | 2.15, 1301 | 2.1, 1302 | 2.05, 1303 | 2, 1304 | 1.933333, 1305 | 1.866667, 1306 | 1.8, 1307 | 1.766667, 1308 | 1.766667, 1309 | 1.716667, 1310 | 1.666667, 1311 | 1.616667, 1312 | 1.566667, 1313 | 1.533333, 1314 | 1.516667, 1315 | 1.516667, 1316 | 1.5, 1317 | 1.483333, 1318 | 1.466667, 1319 | 1.45, 1320 | 1.433333, 1321 | 1.416667, 1322 | 1.383333, 1323 | 1.366667, 1324 | 1.316667, 1325 | 1.266667, 1326 | 1.233333, 1327 | 1.183333, 1328 | 1.133333, 1329 | 1.083333, 1330 | 1.033333, 1331 | 1.016667, 1332 | 0.9833333, 1333 | 0.9666666, 1334 | 0.95, 1335 | 0.9333333, 1336 | 0.9333333, 1337 | 0.9333333, 1338 | 0.9333333, 1339 | 0.9166667, 1340 | 0.9, 1341 | 0.8666667, 1342 | 0.8333333, 1343 | 0.8166667, 1344 | 0.7833334, 1345 | 0.7333333, 1346 | 0.6833333, 1347 | 0.6166667, 1348 | 0.5666667, 1349 | 0.5166667, 1350 | 0.4833333, 1351 | 0.4666667, 1352 | 0.45, 1353 | 0.4333333, 1354 | 0.4166667, 1355 | 0.4, 1356 | 0.3833333, 1357 | 0.3666667, 1358 | 0.35, 1359 | 0.3333333, 1360 | 0.3166667, 1361 | 0.3, 1362 | 0.2833333, 1363 | 0.2666667, 1364 | 0.25, 1365 | 0.2333333, 1366 | 0.2166667, 1367 | 0.2, 1368 | 0.2, 1369 | 0.1833333, 1370 | 0.1666667, 1371 | 0.1666667, 1372 | 0.15, 1373 | 0.1333333, 1374 | 0.1333333, 1375 | 0.1333333, 1376 | 0.1166667, 1377 | 0.1166667, 1378 | 0.1, 1379 | 0.08333334, 1380 | 0.08333334, 1381 | 0.08333334, 1382 | 0.08333334, 1383 | 0.08333334, 1384 | 0.08333334, 1385 | 0.08333334, 1386 | 0.06666667, 1387 | 0.06666667, 1388 | 0.06666667, 1389 | 0.06666667, 1390 | 0.06666667, 1391 | 0.06666667, 1392 | 0.06666667, 1393 | 0, 1394 | 0.01666667, 1395 | 0.03333334, 1396 | 0.05, 1397 | 0.06666667, 1398 | 0.08333334, 1399 | 0.1, 1400 | 0.1166667, 1401 | 0.15, 1402 | 0.1666667, 1403 | 0.1833333, 1404 | 0.2, 1405 | 0.2, 1406 | 0.2166667, 1407 | 0.2166667, 1408 | 0.2333333, 1409 | 0.25, 1410 | 0.2666667, 1411 | 0.2833333, 1412 | 0.3, 1413 | 0.3166667, 1414 | 0.3333333, 1415 | 0.35, 1416 | 0.3833333, 1417 | 0.4166667, 1418 | 0.45, 1419 | 0.4666667, 1420 | 0.5, 1421 | 0.5333334, 1422 | 0.5666667, 1423 | 0.6166667, 1424 | 0.65, 1425 | 0.6833333, 1426 | 0.7333333, 1427 | 0.7833334, 1428 | 0.8333333, 1429 | 0.9, 1430 | 0.95, 1431 | 0.9666666, 1432 | 1.016667, 1433 | 1.05, 1434 | 1.1, 1435 | 1.15, 1436 | 1.216667, 1437 | 1.3, 1438 | 1.383333, 1439 | 1.45, 1440 | 1.5, 1441 | 1.533333, 1442 | 1.566667, 1443 | 1.583333, 1444 | 1.616667, 1445 | 1.65, 1446 | 1.683333, 1447 | 1.733333, 1448 | 1.766667, 1449 | 1.816667, 1450 | 1.866667, 1451 | 1.916667, 1452 | 1.983333, 1453 | 2.033333, 1454 | 2.1, 1455 | 2.166667, 1456 | 2.216667, 1457 | 2.283333, 1458 | 2.35, 1459 | 2.416667, 1460 | 2.483333, 1461 | 2.533333, 1462 | 2.583333, 1463 | 2.65, 1464 | 2.7, 1465 | 2.733333, 1466 | 2.783333, 1467 | 2.8, 1468 | 2.833333, 1469 | 2.866667, 1470 | 2.916667, 1471 | 2.95, 1472 | 2.983333, 1473 | 3, 1474 | 3.033333, 1475 | 3.05, 1476 | 3.066667, 1477 | 3.1, 1478 | 3.15, 1479 | 3.216667, 1480 | 3.283333, 1481 | 3.35, 1482 | 3.383333, 1483 | 3.433333, 1484 | 3.5, 1485 | 3.566667, 1486 | 3.633333, 1487 | 3.7, 1488 | 3.766667, 1489 | 3.833333, 1490 | 3.9, 1491 | 3.933333, 1492 | 3.983333, 1493 | 4.016667, 1494 | 4.066667, 1495 | 4.116667, 1496 | 4.166667, 1497 | 4.2, 1498 | 4.25, 1499 | 4.266667, 1500 | 4.3, 1501 | 4.333333, 1502 | 4.383333, 1503 | 4.433333, 1504 | 4.483333, 1505 | 4.516667, 1506 | 4.55, 1507 | 4.566667, 1508 | 4.6, 1509 | 4.65, 1510 | 4.7, 1511 | 4.75, 1512 | 4.783333, 1513 | 4.8, 1514 | 4.816667, 1515 | 4.816667, 1516 | 4.833333, 1517 | 4.85, 1518 | 4.883333, 1519 | 4.916667, 1520 | 4.966667, 1521 | 5.016667, 1522 | 5.05, 1523 | 5.066667, 1524 | 5.083333, 1525 | 5.116667, 1526 | 5.133333, 1527 | 5.166667, 1528 | 5.183333, 1529 | 5.2, 1530 | 5.216667, 1531 | 5.25, 1532 | 5.266667, 1533 | 5.283333, 1534 | 5.316667, 1535 | 5.35, 1536 | 5.383333, 1537 | 5.433333, 1538 | 5.5, 1539 | 5.566667, 1540 | 5.616667, 1541 | 5.666667, 1542 | 5.716667, 1543 | 5.766667, 1544 | 5.816667, 1545 | 5.866667, 1546 | 5.916667, 1547 | 5.966667, 1548 | 6.016667, 1549 | 6.066667, 1550 | 6.116667, 1551 | 6.166667, 1552 | 6.183333, 1553 | 6.216667, 1554 | 6.25, 1555 | 6.3, 1556 | 6.35, 1557 | 6.4, 1558 | 6.433333, 1559 | 6.466667, 1560 | 6.483333, 1561 | 6.516667, 1562 | 6.55, 1563 | 6.6, 1564 | 6.666667, 1565 | 6.666667, 1566 | 6.666667, 1567 | 6.666667, 1568 | 6.666667, 1569 | 6.666667, 1570 | 6.666667, 1571 | 6.666667, 1572 | 6.666667, 1573 | 6.666667, 1574 | 6.666667, 1575 | 6.55, 1576 | 6.266667, 1577 | 5.966667, 1578 | 5.683333, 1579 | 5.416667, 1580 | 5.2, 1581 | 5, 1582 | 4.85, 1583 | 4.733333, 1584 | 4.633333, 1585 | 4.533333, 1586 | 4.416667, 1587 | 4.333333, 1588 | 4.233333, 1589 | 4.166667, 1590 | 4.133333, 1591 | 4.083333, 1592 | 4.033333, 1593 | 4, 1594 | 3.95, 1595 | 3.916667, 1596 | 3.883333, 1597 | 3.85, 1598 | 3.816667, 1599 | 3.783333, 1600 | 3.733333, 1601 | 3.65, 1602 | 3.6, 1603 | 3.566667, 1604 | 3.533333, 1605 | 3.5, 1606 | 3.45, 1607 | 3.383333, 1608 | 3.316667, 1609 | 3.233333, 1610 | 3.166667, 1611 | 3.116667, 1612 | 3.083333, 1613 | 3.016667, 1614 | 2.983333, 1615 | 2.933333, 1616 | 2.883333, 1617 | 2.816667, 1618 | 2.75, 1619 | 2.666667, 1620 | 2.583333, 1621 | 2.5, 1622 | 2.416667, 1623 | 2.35, 1624 | 2.3, 1625 | 2.266667, 1626 | 2.25, 1627 | 2.233333, 1628 | 2.216667, 1629 | 2.183333, 1630 | 2.166667, 1631 | 2.133333, 1632 | 2.116667, 1633 | 2.1, 1634 | 2.066667, 1635 | 2.033333, 1636 | 2.016667, 1637 | 1.983333, 1638 | 1.966667, 1639 | 1.95, 1640 | 1.916667, 1641 | 1.883333, 1642 | 1.85, 1643 | 1.816667, 1644 | 1.783333, 1645 | 1.733333, 1646 | 1.7, 1647 | 1.65, 1648 | 1.6, 1649 | 1.583333, 1650 | 1.566667, 1651 | 1.55, 1652 | 1.533333, 1653 | 1.516667, 1654 | 1.483333, 1655 | 1.45, 1656 | 1.416667, 1657 | 1.366667, 1658 | 1.316667, 1659 | 1.266667, 1660 | 1.216667, 1661 | 1.15, 1662 | 1.066667, 1663 | 1, 1664 | 0.95, 1665 | 0.9, 1666 | 0.85, 1667 | 0.8, 1668 | 0.7666667, 1669 | 0.7166666, 1670 | 0.6666667, 1671 | 0.6333333, 1672 | 0.6, 1673 | 0.5833333, 1674 | 0.5666667, 1675 | 0.55, 1676 | 0.5333334, 1677 | 0.5166667, 1678 | 0.5, 1679 | 0.4833333, 1680 | 0.4666667, 1681 | 0.45, 1682 | 0.4333333, 1683 | 0.4166667, 1684 | 0.4, 1685 | 0.3833333, 1686 | 0.3666667, 1687 | 0.35, 1688 | 0.3333333, 1689 | 0.3, 1690 | 0.2833333, 1691 | 0.2666667, 1692 | 0.2333333, 1693 | 0.2166667, 1694 | 0.2, 1695 | 0.1833333, 1696 | 0.1666667, 1697 | 0.15, 1698 | 0.1333333, 1699 | 0.1166667, 1700 | 0.1, 1701 | 0.08333334, 1702 | 0.06666667, 1703 | 0.06666667, 1704 | 0.06666667, 1705 | 0.06666667, 1706 | 0, 1707 | 0.01666667, 1708 | 0.1666667, 1709 | 0.1833333, 1710 | 0.2166667, 1711 | 0.25, 1712 | 0.3, 1713 | 0.35, 1714 | 0.4, 1715 | 0.4333333, 1716 | 0.4833333, 1717 | 0.5166667, 1718 | 0.55, 1719 | 0.5833333, 1720 | 0.6333333, 1721 | 0.6833333, 1722 | 0.7333333, 1723 | 0.7833334, 1724 | 0.85, 1725 | 0.9, 1726 | 0.95, 1727 | 0.9833333, 1728 | 1, 1729 | 1.016667, 1730 | 1.05, 1731 | 1.083333, 1732 | 1.116667, 1733 | 1.15, 1734 | 1.2, 1735 | 1.233333, 1736 | 1.25, 1737 | 1.266667, 1738 | 1.3, 1739 | 1.333333, 1740 | 1.383333, 1741 | 1.416667, 1742 | 1.466667, 1743 | 1.516667, 1744 | 1.55, 1745 | 1.583333, 1746 | 1.616667, 1747 | 1.65, 1748 | 1.683333, 1749 | 1.7, 1750 | 1.716667, 1751 | 1.733333, 1752 | 1.75, 1753 | 1.766667, 1754 | 1.8, 1755 | 1.833333, 1756 | 1.85, 1757 | 1.866667, 1758 | 1.883333, 1759 | 1.9, 1760 | 1.916667, 1761 | 1.966667, 1762 | 2, 1763 | 2.033333, 1764 | 2.083333, 1765 | 2.116667, 1766 | 2.15, 1767 | 2.183333, 1768 | 2.25, 1769 | 2.3, 1770 | 2.333333, 1771 | 2.366667, 1772 | 2.383333, 1773 | 2.416667, 1774 | 2.45, 1775 | 2.5, 1776 | 2.566667, 1777 | 2.616667, 1778 | 2.65, 1779 | 2.666667, 1780 | 2.683333, 1781 | 2.7, 1782 | 2.733333, 1783 | 2.75, 1784 | 2.766667, 1785 | 2.783333, 1786 | 2.8, 1787 | 2.816667, 1788 | 2.866667, 1789 | 2.9, 1790 | 2.916667, 1791 | 2.933333, 1792 | 2.95, 1793 | 2.966667, 1794 | 2.983333, 1795 | 3, 1796 | 3.033333, 1797 | 3.05, 1798 | 3.066667, 1799 | 3.083333, 1800 | 3.1, 1801 | 3.116667, 1802 | 3.133333, 1803 | 3.133333, 1804 | 3.15, 1805 | 3.166667, 1806 | 3.183333, 1807 | 3.2, 1808 | 3.216667, 1809 | 3.233333, 1810 | 3.25, 1811 | 3.266667, 1812 | 3.283333, 1813 | 3.3, 1814 | 3.316667, 1815 | 3.333333, 1816 | 3.35, 1817 | 3.366667, 1818 | 3.383333, 1819 | 3.4, 1820 | 3.4, 1821 | 3.416667, 1822 | 3.433333, 1823 | 3.45, 1824 | 3.466667, 1825 | 3.5, 1826 | 3.533333, 1827 | 3.566667, 1828 | 3.6, 1829 | 3.633333, 1830 | 3.666667, 1831 | 3.683333, 1832 | 3.7, 1833 | 3.716667, 1834 | 3.733333, 1835 | 3.766667, 1836 | 3.783333, 1837 | 3.8, 1838 | 3.833333, 1839 | 3.85, 1840 | 3.866667, 1841 | 3.883333, 1842 | 3.9, 1843 | 3.916667, 1844 | 3.933333, 1845 | 3.95, 1846 | 3.966667, 1847 | 3.983333, 1848 | 4, 1849 | 4.033333, 1850 | 4.066667, 1851 | 4.083333, 1852 | 4.1, 1853 | 4.116667, 1854 | 4.15, 1855 | 4.183333, 1856 | 4.216667, 1857 | 4.25, 1858 | 4.283333, 1859 | 4.333333, 1860 | 4.366667, 1861 | 4.4, 1862 | 4.433333, 1863 | 4.466667, 1864 | 4.516667, 1865 | 4.566667, 1866 | 4.633333, 1867 | 4.683333, 1868 | 4.716667, 1869 | 4.75, 1870 | 4.766667, 1871 | 4.8, 1872 | 4.833333, 1873 | 4.866667, 1874 | 4.916667, 1875 | 5, 1876 | 5.1, 1877 | 5.216667, 1878 | 5.333333, 1879 | 5.45, 1880 | 5.566667, 1881 | 5.683333, 1882 | 5.8, 1883 | 5.9, 1884 | 6.016667, 1885 | 6.166667, 1886 | 6.3, 1887 | 6.45, 1888 | 6.583333, 1889 | 6.666667, 1890 | 6.666667, 1891 | 6.666667, 1892 | 6.666667, 1893 | 6.666667, 1894 | 6.666667, 1895 | 6.666667, 1896 | 6.666667, 1897 | 6.666667, 1898 | 6.666667, 1899 | 6.666667, 1900 | 6.666667, 1901 | 6.666667, 1902 | 6.666667, 1903 | 6.666667, 1904 | 6.666667, 1905 | 6.666667, 1906 | 6.666667, 1907 | 6.666667, 1908 | 6.666667, 1909 | 6.666667, 1910 | 6.5, 1911 | 6.333333, 1912 | 6.15, 1913 | 5.966667, 1914 | 5.8, 1915 | 5.683333, 1916 | 5.6, 1917 | 5.55, 1918 | 5.516667, 1919 | 5.5, 1920 | 5.466667, 1921 | 5.416667, 1922 | 5.383333, 1923 | 5.366667, 1924 | 5.35, 1925 | 5.316667, 1926 | 5.283333, 1927 | 5.233333, 1928 | 5.2, 1929 | 5.15, 1930 | 5.116667, 1931 | 5.066667, 1932 | 5.016667, 1933 | 4.983333, 1934 | 4.933333, 1935 | 4.9, 1936 | 4.833333, 1937 | 4.783333, 1938 | 4.733333, 1939 | 4.7, 1940 | 4.683333, 1941 | 4.666667, 1942 | 4.633333, 1943 | 4.6, 1944 | 4.583333, 1945 | 4.566667, 1946 | 4.55, 1947 | 4.533333, 1948 | 4.516667, 1949 | 4.5, 1950 | 4.483333, 1951 | 4.466667, 1952 | 4.45, 1953 | 4.433333, 1954 | 4.416667, 1955 | 4.4, 1956 | 4.383333, 1957 | 4.366667, 1958 | 4.333333, 1959 | 4.3, 1960 | 4.283333, 1961 | 4.266667, 1962 | 4.233333, 1963 | 4.183333, 1964 | 4.133333, 1965 | 4.066667, 1966 | 4.016667, 1967 | 3.983333, 1968 | 3.966667, 1969 | 3.95, 1970 | 3.933333, 1971 | 3.916667, 1972 | 3.816667, 1973 | 3.8, 1974 | 3.766667, 1975 | 3.733333, 1976 | 3.666667, 1977 | 3.583333, 1978 | 3.5, 1979 | 3.45, 1980 | 3.433333, 1981 | 3.4, 1982 | 3.366667, 1983 | 3.333333, 1984 | 3.316667, 1985 | 3.3, 1986 | 3.283333, 1987 | 3.266667, 1988 | 3.233333, 1989 | 3.3, 1990 | 3.383333, 1991 | 3.433333, 1992 | 3.5, 1993 | 3.566667, 1994 | 3.616667, 1995 | 3.683333, 1996 | 3.75, 1997 | 3.816667, 1998 | 3.866667, 1999 | 3.933333, 2000 | 4, 2001 | 4.066667, 2002 | 4.166667, 2003 | 4.25, 2004 | 4.316667, 2005 | 4.35, 2006 | 4.366667, 2007 | 4.416667, 2008 | 4.466667, 2009 | 4.516667, 2010 | 4.566667, 2011 | 4.6, 2012 | 4.616667, 2013 | 4.633333, 2014 | 4.516667, 2015 | 4.383333, 2016 | 4.25, 2017 | 4.15, 2018 | 4.066667, 2019 | 3.983333, 2020 | 3.916667, 2021 | 3.85, 2022 | 3.8, 2023 | 3.766667, 2024 | 3.733333, 2025 | 3.7, 2026 | 3.666667, 2027 | 3.616667, 2028 | 3.55, 2029 | 3.483333, 2030 | 3.433333, 2031 | 3.383333, 2032 | 3.35, 2033 | 3.283333, 2034 | 3.216667, 2035 | 3.133333, 2036 | 3.05, 2037 | 2.7, 2038 | 2.633333, 2039 | 2.583333, 2040 | 2.533333, 2041 | 2.516667, 2042 | 2.5, 2043 | 2.483333, 2044 | 2.45, 2045 | 2.416667, 2046 | 2.366667, 2047 | 2.333333, 2048 | 2.283333, 2049 | 2.233333, 2050 | 2.183333, 2051 | 2.15, 2052 | 2.1, 2053 | 2.05, 2054 | 2, 2055 | 1.95, 2056 | 1.9, 2057 | 1.866667, 2058 | 1.833333, 2059 | 1.783333, 2060 | 1.733333, 2061 | 1.716667, 2062 | 1.683333, 2063 | 1.666667, 2064 | 1.633333, 2065 | 1.6, 2066 | 1.566667, 2067 | 1.55, 2068 | 1.516667, 2069 | 1.483333, 2070 | 1.45, 2071 | 1.433333, 2072 | 1.4, 2073 | 1.366667, 2074 | 1.333333, 2075 | 1.316667, 2076 | 1.283333, 2077 | 1.25, 2078 | 1.2, 2079 | 1.15, 2080 | 1.1, 2081 | 1.05, 2082 | 1, 2083 | 0.95, 2084 | 0.9166667, 2085 | 0.8666667, 2086 | 0.8166667, 2087 | 0.7666667, 2088 | 0.7333333, 2089 | 0.7166666, 2090 | 0.7, 2091 | 0.6833333, 2092 | 0.6666667, 2093 | 0.65, 2094 | 0.6333333, 2095 | 0.6166667, 2096 | 0.6, 2097 | 0.5833333, 2098 | 0.5666667, 2099 | 0.55, 2100 | 0.5333334, 2101 | 0.5166667, 2102 | 0.5, 2103 | 0.4833333, 2104 | 0.45, 2105 | 0.4333333, 2106 | 0.4, 2107 | 0.3833333, 2108 | 0.3833333, 2109 | 0.3833333, 2110 | 0.3833333, 2111 | 0.3666667, 2112 | 0.3666667, 2113 | 0.3666667, 2114 | 0.35, 2115 | 0.3333333, 2116 | 0.3, 2117 | 0.2833333, 2118 | 0.2833333, 2119 | 0.2666667, 2120 | 0.25, 2121 | 0.2333333, 2122 | 0.2166667, 2123 | 0.2166667, 2124 | 0.2166667, 2125 | 0.2166667, 2126 | 0.2, 2127 | 0.1833333, 2128 | 0.15, 2129 | 0.1333333, 2130 | 0.1333333, 2131 | 0.1333333, 2132 | 0.1333333, 2133 | 0.1166667, 2134 | 0.1166667, 2135 | 0.1166667, 2136 | 0.1166667, 2137 | 0.1166667, 2138 | 0.1166667, 2139 | 0, 2140 | 0, 2141 | 0 2142 | ] -------------------------------------------------------------------------------- /data and analysis/data-weightMeasurements/weight.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algal/TouchVisualizer/ee2203c2e134119f8da4d23f1e4431d7807dc8cf/data and analysis/data-weightMeasurements/weight.xlsx -------------------------------------------------------------------------------- /data and analysis/data-weightMeasurements/weightCSV.csv: -------------------------------------------------------------------------------- 1 | UITouch.force,physical_force [N] 0.1500,0.2352 0.3833,0.2352 0.6333,0.4704 0.4000,0.2352 0.4333,0.3528 0.4833,0.3822 0.5667,0.4312 0.6333,0.4802 0.7000,0.5292 0.7667,0.5782 0.8833,0.6272 0.9000,0.6762 0.9667,0.7252 1.0333,0.7742 1.1000,0.8232 1.1333,0.8722 1.2833,0.9702 1.3667,1.0192 1.4833,1.1172 2.7833,1.9992 3.3167,2.2834 6.0500,4.5962 --------------------------------------------------------------------------------