Full Code of facebookarchive/pop for AI

master 87d1f8b74cda cached
99 files
569.2 KB
165.8k tokens
219 symbols
1 requests
Download .txt
Showing preview only (600K chars total). Download the full file or copy to clipboard to get everything.
Repository: facebookarchive/pop
Branch: master
Commit: 87d1f8b74cda
Files: 99
Total size: 569.2 KB

Directory structure:
gitextract_0i4r22d7/

├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── Configuration/
│   ├── Compiler.xcconfig
│   ├── Product/
│   │   ├── ApplicationTests.xcconfig
│   │   ├── Framework.xcconfig
│   │   ├── LogicTests.xcconfig
│   │   └── StaticLibrary.xcconfig
│   └── Project/
│       ├── Project-Debug.xcconfig
│       ├── Project-Profile.xcconfig
│       ├── Project-Release.xcconfig
│       └── Project.xcconfig
├── LICENSE
├── PATENTS
├── Podfile
├── README.md
├── codecov.yml
├── pop/
│   ├── POP.h
│   ├── POPAction.h
│   ├── POPAnimatableProperty.h
│   ├── POPAnimatableProperty.mm
│   ├── POPAnimatablePropertyTypes.h
│   ├── POPAnimation.h
│   ├── POPAnimation.mm
│   ├── POPAnimationEvent.h
│   ├── POPAnimationEvent.mm
│   ├── POPAnimationEventInternal.h
│   ├── POPAnimationExtras.h
│   ├── POPAnimationExtras.mm
│   ├── POPAnimationInternal.h
│   ├── POPAnimationPrivate.h
│   ├── POPAnimationRuntime.h
│   ├── POPAnimationRuntime.mm
│   ├── POPAnimationTracer.h
│   ├── POPAnimationTracer.mm
│   ├── POPAnimationTracerInternal.h
│   ├── POPAnimator.h
│   ├── POPAnimator.mm
│   ├── POPAnimatorPrivate.h
│   ├── POPBasicAnimation.h
│   ├── POPBasicAnimation.mm
│   ├── POPBasicAnimationInternal.h
│   ├── POPCGUtils.h
│   ├── POPCGUtils.mm
│   ├── POPCustomAnimation.h
│   ├── POPCustomAnimation.mm
│   ├── POPDecayAnimation.h
│   ├── POPDecayAnimation.mm
│   ├── POPDecayAnimationInternal.h
│   ├── POPDefines.h
│   ├── POPGeometry.h
│   ├── POPGeometry.mm
│   ├── POPLayerExtras.h
│   ├── POPLayerExtras.mm
│   ├── POPMath.h
│   ├── POPMath.mm
│   ├── POPPropertyAnimation.h
│   ├── POPPropertyAnimation.mm
│   ├── POPPropertyAnimationInternal.h
│   ├── POPSpringAnimation.h
│   ├── POPSpringAnimation.mm
│   ├── POPSpringAnimationInternal.h
│   ├── POPSpringSolver.h
│   ├── POPVector.h
│   ├── POPVector.mm
│   ├── WebCore/
│   │   ├── FloatConversion.h
│   │   ├── TransformationMatrix.cpp
│   │   ├── TransformationMatrix.h
│   │   └── UnitBezier.h
│   ├── module.modulemap
│   ├── pop-ios-Info.plist
│   ├── pop-osx-Info.plist
│   └── pop-tvos-Info.plist
├── pop-tests/
│   ├── POPAnimatable.h
│   ├── POPAnimatable.mm
│   ├── POPAnimatablePropertyTests.mm
│   ├── POPAnimationMRRTests.mm
│   ├── POPAnimationTests.mm
│   ├── POPAnimationTestsExtras.h
│   ├── POPAnimationTestsExtras.mm
│   ├── POPBaseAnimationTests.h
│   ├── POPBaseAnimationTests.mm
│   ├── POPBasicAnimationTests.mm
│   ├── POPCustomAnimationTests.mm
│   ├── POPDecayAnimationTests.mm
│   ├── POPEaseInEaseOutAnimationTests.mm
│   ├── POPSpringAnimationTests.mm
│   ├── pop-tests-ios-Info.plist
│   ├── pop-tests-osx-Info.plist
│   └── pop-tests-tvos-Info.plist
├── pop.podspec
├── pop.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   └── contents.xcworkspacedata
│   └── xcshareddata/
│       └── xcschemes/
│           ├── pop-ios-framework.xcscheme
│           ├── pop-ios-static.xcscheme
│           ├── pop-osx-framework.xcscheme
│           └── pop-tvos-framework.xcscheme
└── pop.xcworkspace/
    ├── contents.xcworkspacedata
    └── xcshareddata/
        └── IDEWorkspaceChecks.plist

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store

*.pbxuser
*.perspective
*.perspectivev3

*.mode1v3
*.mode2v3

*.xcodeproj/xcuserdata/*.xcuserdatad

*.xccheckout
*.xcuserdatad

Pods

DerivedData
build

.ruby-version

================================================
FILE: .travis.yml
================================================
branches:
  only:
    - master
language: objective-c
os: osx
osx_image: xcode9.2
env:
  matrix:
    - TEST_TYPE=iOS
    - TEST_TYPE=OSX
    - TEST_TYPE=tvOS
    - TEST_TYPE=CocoaPods
install:
- |
  if [ "$TEST_TYPE" = iOS ] || [ "$TEST_TYPE" = OSX ] || [ "$TEST_TYPE" = tvOS ]; then
    gem install xcpretty -N --no-ri --no-rdoc
    gem install cocoapods --quiet --no-ri --no-rdoc
    pod install
  fi
script:
- |
  if [ "$TEST_TYPE" = iOS ]; then
    set -o pipefail
    xcodebuild -workspace pop.xcworkspace -scheme pop-ios-framework -sdk iphonesimulator build test -destination "platform=iOS Simulator,name=iPhone SE" GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty -c
  elif [ "$TEST_TYPE" = OSX ]; then
    set -o pipefail
    xcodebuild -workspace pop.xcworkspace -scheme pop-osx-framework -sdk macosx build test GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty -c
  elif [ "$TEST_TYPE" = tvOS ]; then
    set -o pipefail
    xcodebuild -workspace pop.xcworkspace -scheme pop-tvos-framework -sdk appletvsimulator build test -destination "platform=tvOS Simulator,name=Apple TV" GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty -c
  elif [ "$TEST_TYPE" = CocoaPods ]; then
    pod lib lint pop.podspec
    pod lib lint --use-libraries pop.podspec
  fi
after_success:
- |
  if [ "$TEST_TYPE" = iOS ] || [ "$TEST_TYPE" = OSX ] || [ "$TEST_TYPE" = tvOS ]; then
    bash <(curl -s https://codecov.io/bash)
  fi


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
We want to make contributing to Pop as easy and transparent as
possible. If you run into problems, please open an issue. We also actively welcome pull requests.

## Pull Requests
1. Fork the repo and create your branch from `master`.
2. If you've added code that should be tested, add tests
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes.
5. Make sure your code lints.
6. If you haven't already, complete the Contributor License Agreement ("CLA").

## Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You only need
to do this once to work on any of Facebook's open source projects.

Complete your CLA here: <https://code.facebook.com/cla>

## Issues  
We use GitHub issues to track public bugs. Please ensure your description is
clear and has sufficient instructions to be able to reproduce the issue.

## Coding Style  
* 2 spaces for indentation rather than tabs

## License
By contributing to Pop you agree that your contributions will be licensed
under its BSD license.


================================================
FILE: Configuration/Compiler.xcconfig
================================================
//
// Copyright (c) 2014-present, Facebook, Inc.
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//

// Deployment targets
MACOSX_DEPLOYMENT_TARGET = 10.7
IPHONEOS_DEPLOYMENT_TARGET = 8.0
TVOS_DEPLOYMENT_TARGET = 9.0

// Warnings
GCC_TREAT_WARNINGS_AS_ERRORS = YES
CLANG_WARN_CONSTANT_CONVERSION = YES
CLANG_WARN_ENUM_CONVERSION = YES
CLANG_WARN_INT_CONVERSION = YES
CLANG_WARN_INFINITE_RECURSION = YES
CLANG_WARN_SUSPICIOUS_MOVE = YES
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
CLANG_WARN_BOOL_CONVERSION = YES
CLANG_WARN_EMPTY_BODY = YES
CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES
CLANG_WARN_CXX0X_EXTENSIONS = NO
CLANG_WARN_UNREACHABLE_CODE = YES
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
CLANG_WARN_STRICT_PROTOTYPES = YES
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
GCC_WARN_MISSING_PARENTHESES = YES
GCC_WARN_SHADOW = YES
GCC_WARN_SIGN_COMPARE = YES
GCC_WARN_UNINITIALIZED_AUTOS = YES
GCC_WARN_UNDECLARED_SELECTOR = YES
GCC_WARN_UNUSED_FUNCTION = YES
GCC_WARN_UNUSED_VARIABLE = YES
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
CLANG_WARN_COMMA = YES
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
WARNING_CFLAGS = -Wall -Wextra -Wno-unused-parameter

// Language
GCC_C_LANGUAGE_STANDARD = gnu99
CLANG_CXX_LANGUAGE_STANDARD = c++11
CLANG_CXX_LIBRARY = libc++

// Application Extension
APPLICATION_EXTENSION_API_ONLY = YES

// Preprocessing
ENABLE_STRICT_OBJC_MSGSEND = YES

// Enable ARC
CLANG_ENABLE_OBJC_ARC = YES

// Modules
CLANG_ENABLE_MODULES=YES

// Search Paths
ALWAYS_SEARCH_USER_PATHS = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) $(BUILT_PRODUCTS_DIR)
LIBRARY_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR) $(inherited)
HEADER_SEARCH_PATHS = $(inherited) $(SYMROOT)/Headers $(DERIVED_FILE_DIR)

// Code Generation
GCC_DYNAMIC_NO_PIC = NO
GCC_NO_COMMON_BLOCKS = YES
GCC_INLINES_ARE_PRIVATE_EXTERN = YES
GCC_SYMBOLS_PRIVATE_EXTERN = NO


================================================
FILE: Configuration/Product/ApplicationTests.xcconfig
================================================
// 
// Copyright (c) 2014-present, Facebook, Inc.
// All rights reserved.
// 
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// 

#include "LogicTests.xcconfig"

// Linking
BUNDLE_LOADER = $(BUILT_PRODUCTS_DIR)/$(PROJECT_NAME)TestHost.app/$(PROJECT_NAME)TestHost

// Unit Testing
TEST_HOST = "$(BUNDLE_LOADER)"


================================================
FILE: Configuration/Product/Framework.xcconfig
================================================
//
// Copyright (c) 2014-present, Facebook, Inc.
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//

#include "../Compiler.xcconfig"

// Version
DYLIB_COMPATIBILITY_VERSION = 1
DYLIB_CURRENT_VERSION = 1
CURRENT_PROJECT_VERSION = 1


================================================
FILE: Configuration/Product/LogicTests.xcconfig
================================================
// 
// Copyright (c) 2014-present, Facebook, Inc.
// All rights reserved.
// 
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// 

#include "../Compiler.xcconfig"

// Deployment
DSTROOT = /tmp/$(TARGET_NAME)
SKIP_INSTALL = YES


================================================
FILE: Configuration/Product/StaticLibrary.xcconfig
================================================
// 
// Copyright (c) 2014-present, Facebook, Inc.
// All rights reserved.
// 
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// 

#include "../Compiler.xcconfig"

// Deployment
DSTROOT = /tmp/$(TARGET_NAME)
INSTALL_PATH = $(SYMROOT)/Headers
SKIP_INSTALL = YES

// declare inlines as extern
GCC_INLINES_ARE_PRIVATE_EXTERN = YES

// Use PRODUCT_NAME instead of TARGET_NAME to support multiple targets for different platforms
PUBLIC_HEADERS_FOLDER_PATH = include/$(PRODUCT_NAME)


================================================
FILE: Configuration/Project/Project-Debug.xcconfig
================================================
// 
// Copyright (c) 2014-present, Facebook, Inc.
// All rights reserved.
// 
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// 

#include "Project.xcconfig"

// Architectures
ONLY_ACTIVE_ARCH = YES

// Code Generation
GCC_OPTIMIZATION_LEVEL = 0

// Enable assertions
ENABLE_NS_ASSERTIONS = YES

// Avoid compression overhead
COMPRESS_PNG_FILES = NO

// Deployment
COPY_PHASE_STRIP = NO

// Default to dwarf
DEBUG_INFORMATION_FORMAT = dwarf

// Testing
ENABLE_TESTABILITY = YES


================================================
FILE: Configuration/Project/Project-Profile.xcconfig
================================================
// 
// Copyright (c) 2014-present, Facebook, Inc.
// All rights reserved.
// 
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// 

#include "Project.xcconfig"

// Code Generation
GCC_OPTIMIZATION_LEVEL = s

// Disable assertions
ENABLE_NS_ASSERTIONS = NO

// Enable Xcode PNG compression
COMPRESS_PNG_FILES = YES

// Deployment
COPY_PHASE_STRIP = NO

// Preprocessing
GCC_PREPROCESSOR_DEFINITIONS = PROFILE=1 NS_BLOCK_ASSERTIONS NDEBUG

// We need debug symbols for profiling
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym

// Not all libraries have a Profile version. Allow using the Release version if they don't.
LIBRARY_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR) $(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)


================================================
FILE: Configuration/Project/Project-Release.xcconfig
================================================
// 
// Copyright (c) 2014-present, Facebook, Inc.
// All rights reserved.
// 
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// 

#include "Project.xcconfig"

// Code Generation
GCC_OPTIMIZATION_LEVEL = s

// Disable assertions
ENABLE_NS_ASSERTIONS = NO

// Enable Xcode PNG compression
COMPRESS_PNG_FILES = YES

// Build Options
VALIDATE_PRODUCT = YES

// Preprocessing
GCC_PREPROCESSOR_DEFINITIONS = PROFILE=1 NS_BLOCK_ASSERTIONS NDEBUG


================================================
FILE: Configuration/Project/Project.xcconfig
================================================
// 
// Copyright (c) 2014-present, Facebook, Inc.
// All rights reserved.
// 
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// 

#include "../Compiler.xcconfig"

// Install
SKIP_INSTALL = YES

// Code Signing
CODE_SIGN_IDENTITY =


================================================
FILE: LICENSE
================================================
BSD License

For Pop software

Copyright (c) 2014, Facebook, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

 * Neither the name Facebook nor the names of its contributors may be used to
   endorse or promote products derived from this software without specific
   prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: PATENTS
================================================
Additional Grant of Patent Rights Version 2

"Software" means the Pop software distributed by Facebook, Inc.

Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
(subject to the termination provision below) license under any Necessary
Claims, to make, have made, use, sell, offer to sell, import, and otherwise
transfer the Software. For avoidance of doubt, no license is granted under
Facebook’s rights in any patent claims that are infringed by (i) modifications
to the Software made by you or any third party or (ii) the Software in
combination with any software or other technology.

The license granted hereunder will terminate, automatically and without notice,
if you (or any of your subsidiaries, corporate affiliates or agents) initiate
directly or indirectly, or take a direct financial interest in, any Patent
Assertion: (i) against Facebook or any of its subsidiaries or corporate
affiliates, (ii) against any party if such Patent Assertion arises in whole or
in part from any software, technology, product or service of Facebook or any of
its subsidiaries or corporate affiliates, or (iii) against any party relating
to the Software. Notwithstanding the foregoing, if Facebook or any of its
subsidiaries or corporate affiliates files a lawsuit alleging patent
infringement against you in the first instance, and you respond by filing a
patent infringement counterclaim in that lawsuit against that party that is
unrelated to the Software, the license granted hereunder will not terminate
under section (i) of this paragraph due to such counterclaim.

A "Necessary Claim" is a claim of a patent owned by Facebook that is
necessarily infringed by the Software standing alone.

A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
or contributory infringement or inducement to infringe any patent, including a
cross-claim or counterclaim.


================================================
FILE: Podfile
================================================
abstract_target 'Tests' do
  pod 'OCMock', '~> 2.2'
  
  target :'pop-tests-ios' do
    platform :ios, '6.0'
  end

  target :'pop-tests-tvos' do
    platform :tvos, '9.0'
  end

  target :'pop-tests-osx' do
    platform :osx, '10.8'
  end
end


================================================
FILE: README.md
================================================
![pop](https://github.com/facebook/pop/blob/master/Images/pop.gif?raw=true)

Pop is an extensible animation engine for iOS, tvOS, and OS X. In addition to basic static animations, it supports spring and decay dynamic animations, making it useful for building realistic, physics-based interactions. The API allows quick integration with existing Objective-C or Swift codebases and enables the animation of any property on any object. It's a mature and well-tested framework that drives all the animations and transitions in [Paper](https://en.wikipedia.org/wiki/Facebook_Paper).

[![Build Status](https://travis-ci.org/facebook/pop.svg)](https://travis-ci.org/facebook/pop)

## Installation

Pop is available on [CocoaPods](http://cocoapods.org). Just add the following to your project Podfile:

```ruby
pod 'pop', '~> 1.0'
```

Bugs are first fixed in master and then made available via a designated release. If you tend to live on the bleeding edge, you can use Pop from master with the following Podfile entry:

```ruby
pod 'pop', :git => 'https://github.com/facebook/pop.git'
```

### Framework (manual)
By adding the project to your project and adding pop.embedded framework to the Embedded Binaries section on the General tab of your app's target, you can set up pop in seconds! This also enables `@import pop` syntax with header modules.

**Note**: because of some awkward limitations with Xcode, embedded binaries must share the same name as the module and must have `.framework` as an extension. This means that you'll see three pop.frameworks when adding embedded binaries (one for OS X, one for tvOS, and one for iOS). You'll need to be sure to add the right one; they appear identically in the list but note the list is populated in order of targets. You can verify the correct one was chosen by checking the path next to the framework listed, in the format `<configuration>-<platform>` (e.g. `Debug-iphoneos`).

![Embedded Binaries](Images/EmbeddedBinaries.png?raw=true)

**Note 2**: this method does not currently play nicely with workspaces. Since targets can only depend on and embed products from other targets in the same project, it only works when pop.xcodeproj is added as a subproject to the current target's project. Otherwise, you'll need to manually set the build ordering in the scheme and copy in the product.

### Static Library (manual)
Alternatively, you can add the project to your workspace and adopt the provided configuration files or manually copy the files under the pop subdirectory into your project. If installing manually, ensure the C++ standard library is also linked by including `-lc++` to your project linker flags.

## Usage

Pop adopts the Core Animation explicit animation programming model. Use by including the following import:

#### Objective-C

```objective-c
#import <pop/POP.h>
```

or if you're using the embedded framework:

```objective-c
@import pop;
```

#### Swift

```swift
import pop
```

### Start, Stop & Update

To start an animation, add it to the object you wish to animate:

#### Objective-C

```objective-c
POPSpringAnimation *anim = [POPSpringAnimation animation];
...
[layer pop_addAnimation:anim forKey:@"myKey"];
```

#### Swift

```swift
let anim = POPSpringAnimation()
...
layer.pop_add(anim, forKey: "myKey")
```

To stop an animation, remove it from the object referencing the key specified on start:

#### Objective-C

```objective-c
[layer pop_removeAnimationForKey:@"myKey"];
```

#### Swift

```swift
layer.pop_removeAnimation(forKey: "myKey")
```

The key can also be used to query for the existence of an animation. Updating the toValue of a running animation can provide the most seamless way to change course:

#### Objective-C

```objective-c
anim = [layer pop_animationForKey:@"myKey"];
if (anim) {
  /* update to value to new destination */
  anim.toValue = @(42.0);
} else {
  /* create and start a new animation */
  ....
}
```

#### Swift

```swift
if let anim = layer.pop_animation(forKey: "myKey") as? POPSpringAnimation {
    /* update to value to new destination */
    anim.toValue = 42.0
} else {
    /* create and start a new animation */
    ....
}
```

While a layer was used in the above examples, the Pop interface is implemented as a category addition on NSObject. Any NSObject or subclass can be animated.

### Types

There are four concrete animation types: spring, decay, basic and custom.

Spring animations can be used to give objects a delightful bounce. In this example, we use a spring animation to animate a layer's bounds from its current value to (0, 0, 400, 400):

#### Objective-C

```objective-c
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds];
anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 400, 400)];
[layer pop_addAnimation:anim forKey:@"size"];
```

#### Swift

```swift
if let anim = POPSpringAnimation(propertyNamed: kPOPLayerBounds) {
    anim.toValue = NSValue(cgRect: CGRect(x: 0, y: 0, width: 400, height: 400))
    layer.pop_add(anim, forKey: "size")
}
```

Decay animations can be used to gradually slow an object to a halt. In this example, we decay a layer's positionX from it's current value and velocity 1000pts per second:

#### Objective-C

```objective-c
POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPositionX];
anim.velocity = @(1000.);
[layer pop_addAnimation:anim forKey:@"slide"];
```

#### Swift

```swift
if let anim = POPDecayAnimation(propertyNamed: kPOPLayerPositionX) {
    anim.velocity = 1000.0
    layer.pop_add(anim, forKey: "slide")
}
```

Basic animations can be used to interpolate values over a specified time period. To use an ease-in ease-out animation to animate a view's alpha from 0.0 to 1.0 over the default duration:

#### Objective-C

```objective-c
POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPViewAlpha];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.fromValue = @(0.0);
anim.toValue = @(1.0);
[view pop_addAnimation:anim forKey:@"fade"];
```

#### Swift

```swift
if let anim = POPBasicAnimation(propertyNamed: kPOPViewAlpha) {
    anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    anim.fromValue = 0.0
    anim.toValue = 1.0
    view.pop_add(anim, forKey: "fade")
}
```

`POPCustomAnimation` makes creating custom animations and transitions easier by handling CADisplayLink and associated time-step management. See header for more details.


### Properties

The property animated is specified by the `POPAnimatableProperty` class. In this example we create a spring animation and explicitly set the animatable property corresponding to `-[CALayer bounds]`:

#### Objective-C

```objective-c
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
```

#### Swift

```swift
let anim = POPSpringAnimation()
if let property = POPAnimatableProperty.property(withName: kPOPLayerBounds) as? POPAnimatableProperty {
    anim.property = property
}
```

The framework provides many common layer and view animatable properties out of box. You can animate a custom property by creating a new instance of the class. In this example, we declare a custom volume property:

#### Objective-C

```objective-c
prop = [POPAnimatableProperty propertyWithName:@"com.foo.radio.volume" initializer:^(POPMutableAnimatableProperty *prop) {
  // read value
  prop.readBlock = ^(id obj, CGFloat values[]) {
    values[0] = [obj volume];
  };
  // write value
  prop.writeBlock = ^(id obj, const CGFloat values[]) {
    [obj setVolume:values[0]];
  };
  // dynamics threshold
  prop.threshold = 0.01;
}];

anim.property = prop;
```

#### Swift

```swift
if let prop = POPAnimatableProperty.property(withName: "com.foo.radio.volume", initializer: { prop in
    guard let prop = prop else {
        return
    }
    // read value
    prop.readBlock = { obj, values in
        guard let obj = obj as? Volumeable, let values = values else {
            return
        }

        values[0] = obj.volume
    }
    // write value
    prop.writeBlock = { obj, values in
        guard var obj = obj as? Volumeable, let values = values else {
            return
        }

        obj.volume = values[0]
    }
    // dynamics threshold
    prop.threshold = 0.01
}) as? POPAnimatableProperty {
    anim.property = prop
}
```

For a complete listing of provided animatable properties, as well more information on declaring custom properties see `POPAnimatableProperty.h`.


### Debugging

Here are a few tips when debugging. Pop obeys the Simulator's Toggle Slow Animations setting. Try enabling it to slow down animations and more easily observe interactions.

Consider naming your animations. This will allow you to more easily identify them when referencing them, either via logging or in the debugger:

#### Objective-C

```objective-c
anim.name = @"springOpen";
```

#### Swift

```swift
anim.name = "springOpen"
```

Each animation comes with an associated tracer. The tracer allows you to record all animation-related events, in a fast and efficient manner, allowing you to query and analyze them after animation completion. The below example starts the tracer and configures it to log all events on animation completion:

#### Objective-C

```objective-c
POPAnimationTracer *tracer = anim.tracer;
tracer.shouldLogAndResetOnCompletion = YES;
[tracer start];
```

#### Swift

```swift
if let tracer = anim.tracer {
    tracer.shouldLogAndResetOnCompletion = true
    tracer.start()
}
```

See `POPAnimationTracer.h` for more details.

## Testing

Pop has extensive unit test coverage. To install test dependencies, navigate to the root pop directory and type:

```sh
pod install
```

Assuming CocoaPods is installed, this will include the necessary OCMock dependency to the unit test targets.

## SceneKit

Due to SceneKit requiring iOS 8 and OS X 10.9, POP's SceneKit extensions aren't provided out of box. Unfortunately, [weakly linked frameworks](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html) cannot be used due to issues mentioned in the [Xcode 6.1 Release Notes](https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html).

To remedy this, you can easily opt-in to use SceneKit! Simply add this to the Preprocessor Macros section of your Xcode Project:

```
POP_USE_SCENEKIT=1
```

## Resources

A collection of links to external resources that may prove valuable:

* [AGGeometryKit+POP - Animating Quadrilaterals with Pop](https://github.com/hfossli/aggeometrykit-pop)
* [Apple – Core Animation Programming Guide](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html)
* [iOS Development Tips – UIScrollView-like deceleration with Pop](http://iosdevtips.co/post/84571595353/replicating-uiscrollviews-deceleration-with-facebook)
* [Pop Playground – Repository of Pop animation examples](https://github.com/callmeed/pop-playground)
* [Pop Playground 2 – Playing with Facebook's framework](http://victorbaro.com/2014/05/pop-playground-playing-with-facebooks-framework/)
* [POP-MCAnimate – Concise syntax for the Pop animation framework](https://github.com/matthewcheok/POP-MCAnimate)
* [Popping - Great examples in one project](https://github.com/schneiderandre/popping)
* [Rebound – Spring Animations for Android](http://facebook.github.io/rebound/)
* [Tapity Tutorial – Getting Started with Pop](http://tapity.com/tutorial-getting-started-with-pop/)
* [Tweaks – Easily adjust parameters for iOS apps in development](https://github.com/facebook/tweaks)
* [POP Tutorial in 5 steps](https://github.com/maxmyers/FacebookPop)
* [VBFPopFlatButton – Flat animatable button, using Pop to transition between states](https://github.com/victorBaro/VBFPopFlatButton)

## Contributing
See the CONTRIBUTING file for how to help out.

## License

Pop is released under a BSD License. See LICENSE file for details.


================================================
FILE: codecov.yml
================================================
coverage:
  ignore:
    - pop-tests/*
  status:
    patch: false
    changes: false
    project:
      default:
        target: 50
comment: false


================================================
FILE: pop/POP.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#ifndef POP_POP_H
#define POP_POP_H

#import <pop/POPDefines.h>

#import <pop/POPAnimatableProperty.h>
#import <pop/POPAnimatablePropertyTypes.h>
#import <pop/POPAnimation.h>
#import <pop/POPAnimationEvent.h>
#import <pop/POPAnimationExtras.h>
#import <pop/POPAnimationTracer.h>
#import <pop/POPAnimator.h>
#import <pop/POPBasicAnimation.h>
#import <pop/POPCustomAnimation.h>
#import <pop/POPDecayAnimation.h>
#import <pop/POPGeometry.h>
#import <pop/POPLayerExtras.h>
#import <pop/POPPropertyAnimation.h>
#import <pop/POPSpringAnimation.h>

#endif /* POP_POP_H */


================================================
FILE: pop/POPAction.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#ifndef POPACTION_H
#define POPACTION_H

#import <QuartzCore/CATransaction.h>

#import <pop/POPDefines.h>

#ifdef __cplusplus

namespace POP {
  
  /**
   @abstract Disables Core Animation actions using RAII.
   @discussion The disablement of actions is scoped to the current transaction.
   */
  class ActionDisabler
  {
    BOOL state;
    
  public:
    ActionDisabler() POP_NOTHROW
    {
      state = [CATransaction disableActions];
      [CATransaction setDisableActions:YES];
    }
    
    ~ActionDisabler()
    {
      [CATransaction setDisableActions:state];
    }
  };
  
  /**
   @abstract Enables Core Animation actions using RAII.
   @discussion The enablement of actions is scoped to the current transaction.
   */
  class ActionEnabler
  {
    BOOL state;
    
  public:
    ActionEnabler() POP_NOTHROW
    {
      state = [CATransaction disableActions];
      [CATransaction setDisableActions:NO];
    }
    
    ~ActionEnabler()
    {
      [CATransaction setDisableActions:state];
    }
  };

}

#endif /* __cplusplus */

#endif /* POPACTION_H */


================================================
FILE: pop/POPAnimatableProperty.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <CoreGraphics/CoreGraphics.h>

#import <Foundation/NSObject.h>

#import <pop/POPDefines.h>
#import <pop/POPAnimatablePropertyTypes.h>

@class POPMutableAnimatableProperty;

/**
 @abstract Describes an animatable property.
 */
@interface POPAnimatableProperty : NSObject <NSCopying, NSMutableCopying>

/**
 @abstract Property accessor.
 @param name The name of the property.
 @return The animatable property with that name or nil if it does not exist.
 @discussion Common animatable properties are included by default. Use the provided constants to reference.
 */
+ (id)propertyWithName:(NSString *)name;

/**
 @abstract The designated initializer.
 @param name The name of the property.
 @param block The block used to configure the property on creation.
 @return The animatable property with name if it exists, otherwise a newly created instance configured by block.
 @discussion Custom properties should use reverse-DNS naming. A newly created instance is only mutable in the scope of block. Once constructed, a property becomes immutable.
 */
+ (id)propertyWithName:(NSString *)name initializer:(void (^)(POPMutableAnimatableProperty *prop))block;

/**
 @abstract The name of the property.
 @discussion Used to uniquely identify an animatable property.
 */
@property (readonly, nonatomic, copy) NSString *name;

/**
 @abstract Block used to read values from a property into an array of floats.
 */
@property (readonly, nonatomic, copy) POPAnimatablePropertyReadBlock readBlock;

/**
 @abstract Block used to write values from an array of floats into a property.
 */
@property (readonly, nonatomic, copy) POPAnimatablePropertyWriteBlock writeBlock;

/**
 @abstract The threshold value used when determining completion of dynamics simulations.
 */
@property (readonly, nonatomic, assign) CGFloat threshold;

@end

/**
 @abstract A mutable animatable property intended for configuration.
 */
@interface POPMutableAnimatableProperty : POPAnimatableProperty

/**
 @abstract A read-write version of POPAnimatableProperty name property.
 */
@property (readwrite, nonatomic, copy) NSString *name;

/**
 @abstract A read-write version of POPAnimatableProperty readBlock property.
 */
@property (readwrite, nonatomic, copy) POPAnimatablePropertyReadBlock readBlock;

/**
 @abstract A read-write version of POPAnimatableProperty writeBlock property.
 */
@property (readwrite, nonatomic, copy) POPAnimatablePropertyWriteBlock writeBlock;

/**
 @abstract A read-write version of POPAnimatableProperty threshold property.
 */
@property (readwrite, nonatomic, assign) CGFloat threshold;

@end

POP_EXTERN_C_BEGIN

/**
 Common CALayer property names.
 */
extern NSString * const kPOPLayerBackgroundColor;
extern NSString * const kPOPLayerBounds;
extern NSString * const kPOPLayerCornerRadius;
extern NSString * const kPOPLayerBorderWidth;
extern NSString * const kPOPLayerBorderColor;
extern NSString * const kPOPLayerOpacity;
extern NSString * const kPOPLayerPosition;
extern NSString * const kPOPLayerPositionX;
extern NSString * const kPOPLayerPositionY;
extern NSString * const kPOPLayerRotation;
extern NSString * const kPOPLayerRotationX;
extern NSString * const kPOPLayerRotationY;
extern NSString * const kPOPLayerScaleX;
extern NSString * const kPOPLayerScaleXY;
extern NSString * const kPOPLayerScaleY;
extern NSString * const kPOPLayerSize;
extern NSString * const kPOPLayerSubscaleXY;
extern NSString * const kPOPLayerSubtranslationX;
extern NSString * const kPOPLayerSubtranslationXY;
extern NSString * const kPOPLayerSubtranslationY;
extern NSString * const kPOPLayerSubtranslationZ;
extern NSString * const kPOPLayerTranslationX;
extern NSString * const kPOPLayerTranslationXY;
extern NSString * const kPOPLayerTranslationY;
extern NSString * const kPOPLayerTranslationZ;
extern NSString * const kPOPLayerZPosition;
extern NSString * const kPOPLayerShadowColor;
extern NSString * const kPOPLayerShadowOffset;
extern NSString * const kPOPLayerShadowOpacity;
extern NSString * const kPOPLayerShadowRadius;

/**
 Common CAShapeLayer property names.
 */
extern NSString * const kPOPShapeLayerStrokeStart;
extern NSString * const kPOPShapeLayerStrokeEnd;
extern NSString * const kPOPShapeLayerStrokeColor;
extern NSString * const kPOPShapeLayerFillColor;
extern NSString * const kPOPShapeLayerLineWidth;
extern NSString * const kPOPShapeLayerLineDashPhase;

/**
 Common NSLayoutConstraint property names.
 */
extern NSString * const kPOPLayoutConstraintConstant;


#if TARGET_OS_IPHONE

/**
 Common UIView property names.
 */
extern NSString * const kPOPViewAlpha;
extern NSString * const kPOPViewBackgroundColor;
extern NSString * const kPOPViewBounds;
extern NSString * const kPOPViewCenter;
extern NSString * const kPOPViewFrame;
extern NSString * const kPOPViewScaleX;
extern NSString * const kPOPViewScaleXY;
extern NSString * const kPOPViewScaleY;
extern NSString * const kPOPViewSize;
extern NSString * const kPOPViewTintColor;

/**
 Common UIScrollView property names.
 */
extern NSString * const kPOPScrollViewContentOffset;
extern NSString * const kPOPScrollViewContentSize;
extern NSString * const kPOPScrollViewZoomScale;
extern NSString * const kPOPScrollViewContentInset;
extern NSString * const kPOPScrollViewScrollIndicatorInsets;

/**
 Common UITableView property names.
 */
extern NSString * const kPOPTableViewContentOffset;
extern NSString * const kPOPTableViewContentSize;

/**
 Common UICollectionView property names.
 */
extern NSString * const kPOPCollectionViewContentOffset;
extern NSString * const kPOPCollectionViewContentSize;

/**
 Common UINavigationBar property names.
 */
extern NSString * const kPOPNavigationBarBarTintColor;

/**
 Common UIToolbar property names.
 */
extern NSString * const kPOPToolbarBarTintColor;

/**
 Common UITabBar property names.
 */
extern NSString * const kPOPTabBarBarTintColor;

/**
 Common UILabel property names.
 */
extern NSString * const kPOPLabelTextColor;

#else

/**
 Common NSView property names.
 */
extern NSString * const kPOPViewFrame;
extern NSString * const kPOPViewBounds;
extern NSString * const kPOPViewAlphaValue;
extern NSString * const kPOPViewFrameRotation;
extern NSString * const kPOPViewFrameCenterRotation;
extern NSString * const kPOPViewBoundsRotation;

/**
 Common NSWindow property names.
 */
extern NSString * const kPOPWindowFrame;
extern NSString * const kPOPWindowAlphaValue;
extern NSString * const kPOPWindowBackgroundColor;

#endif

#if SCENEKIT_SDK_AVAILABLE

/**
 Common SceneKit property names.
 */
extern NSString * const kPOPSCNNodePosition;
extern NSString * const kPOPSCNNodePositionX;
extern NSString * const kPOPSCNNodePositionY;
extern NSString * const kPOPSCNNodePositionZ;
extern NSString * const kPOPSCNNodeTranslation;
extern NSString * const kPOPSCNNodeTranslationX;
extern NSString * const kPOPSCNNodeTranslationY;
extern NSString * const kPOPSCNNodeTranslationZ;
extern NSString * const kPOPSCNNodeRotation;
extern NSString * const kPOPSCNNodeRotationX;
extern NSString * const kPOPSCNNodeRotationY;
extern NSString * const kPOPSCNNodeRotationZ;
extern NSString * const kPOPSCNNodeRotationW;
extern NSString * const kPOPSCNNodeEulerAngles;
extern NSString * const kPOPSCNNodeEulerAnglesX;
extern NSString * const kPOPSCNNodeEulerAnglesY;
extern NSString * const kPOPSCNNodeEulerAnglesZ;
extern NSString * const kPOPSCNNodeOrientation;
extern NSString * const kPOPSCNNodeOrientationX;
extern NSString * const kPOPSCNNodeOrientationY;
extern NSString * const kPOPSCNNodeOrientationZ;
extern NSString * const kPOPSCNNodeOrientationW;
extern NSString * const kPOPSCNNodeScale;
extern NSString * const kPOPSCNNodeScaleX;
extern NSString * const kPOPSCNNodeScaleY;
extern NSString * const kPOPSCNNodeScaleZ;
extern NSString * const kPOPSCNNodeScaleXY;

#endif

POP_EXTERN_C_END


================================================
FILE: pop/POPAnimatableProperty.mm
================================================
/**
  Copyright (c) 2014-present, Facebook, Inc.
  All rights reserved.

  This source code is licensed under the BSD-style license found in the
  LICENSE file in the root directory of this source tree. An additional grant
  of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPAnimatableProperty.h"

#import <QuartzCore/QuartzCore.h>

#import "POPAnimationRuntime.h"
#import "POPCGUtils.h"
#import "POPDefines.h"
#import "POPLayerExtras.h"

// common threshold definitions
static CGFloat const kPOPThresholdColor = 0.01;
static CGFloat const kPOPThresholdPoint = 1.0;
static CGFloat const kPOPThresholdOpacity = 0.01;
static CGFloat const kPOPThresholdScale = 0.005;
static CGFloat const kPOPThresholdRotation = 0.01;
static CGFloat const kPOPThresholdRadius = 0.01;

#pragma mark - Static

// CALayer
NSString * const kPOPLayerBackgroundColor = @"backgroundColor";
NSString * const kPOPLayerBounds = @"bounds";
NSString * const kPOPLayerCornerRadius = @"cornerRadius";
NSString * const kPOPLayerBorderWidth = @"borderWidth";
NSString * const kPOPLayerBorderColor = @"borderColor";
NSString * const kPOPLayerOpacity = @"opacity";
NSString * const kPOPLayerPosition = @"position";
NSString * const kPOPLayerPositionX = @"positionX";
NSString * const kPOPLayerPositionY = @"positionY";
NSString * const kPOPLayerRotation = @"rotation";
NSString * const kPOPLayerRotationX = @"rotationX";
NSString * const kPOPLayerRotationY = @"rotationY";
NSString * const kPOPLayerScaleX = @"scaleX";
NSString * const kPOPLayerScaleXY = @"scaleXY";
NSString * const kPOPLayerScaleY = @"scaleY";
NSString * const kPOPLayerSize = @"size";
NSString * const kPOPLayerSubscaleXY = @"subscaleXY";
NSString * const kPOPLayerSubtranslationX = @"subtranslationX";
NSString * const kPOPLayerSubtranslationXY = @"subtranslationXY";
NSString * const kPOPLayerSubtranslationY = @"subtranslationY";
NSString * const kPOPLayerSubtranslationZ = @"subtranslationZ";
NSString * const kPOPLayerTranslationX = @"translationX";
NSString * const kPOPLayerTranslationXY = @"translationXY";
NSString * const kPOPLayerTranslationY = @"translationY";
NSString * const kPOPLayerTranslationZ = @"translationZ";
NSString * const kPOPLayerZPosition = @"zPosition";
NSString * const kPOPLayerShadowColor = @"shadowColor";
NSString * const kPOPLayerShadowOffset = @"shadowOffset";
NSString * const kPOPLayerShadowOpacity = @"shadowOpacity";
NSString * const kPOPLayerShadowRadius = @"shadowRadius";

// CAShapeLayer
NSString * const kPOPShapeLayerStrokeStart = @"shapeLayer.strokeStart";
NSString * const kPOPShapeLayerStrokeEnd = @"shapeLayer.strokeEnd";
NSString * const kPOPShapeLayerStrokeColor = @"shapeLayer.strokeColor";
NSString * const kPOPShapeLayerFillColor = @"shapeLayer.fillColor";
NSString * const kPOPShapeLayerLineWidth = @"shapeLayer.lineWidth";
NSString * const kPOPShapeLayerLineDashPhase = @"shapeLayer.lineDashPhase";

// NSLayoutConstraint
NSString * const kPOPLayoutConstraintConstant = @"layoutConstraint.constant";

#if TARGET_OS_IPHONE

// UIView
NSString * const kPOPViewAlpha = @"view.alpha";
NSString * const kPOPViewBackgroundColor = @"view.backgroundColor";
NSString * const kPOPViewBounds = kPOPLayerBounds;
NSString * const kPOPViewCenter = @"view.center";
NSString * const kPOPViewFrame = @"view.frame";
NSString * const kPOPViewScaleX = @"view.scaleX";
NSString * const kPOPViewScaleXY = @"view.scaleXY";
NSString * const kPOPViewScaleY = @"view.scaleY";
NSString * const kPOPViewSize = kPOPLayerSize;
NSString * const kPOPViewTintColor = @"view.tintColor";

// UIScrollView
NSString * const kPOPScrollViewContentOffset = @"scrollView.contentOffset";
NSString * const kPOPScrollViewContentSize = @"scrollView.contentSize";
NSString * const kPOPScrollViewZoomScale = @"scrollView.zoomScale";
NSString * const kPOPScrollViewContentInset = @"scrollView.contentInset";
NSString * const kPOPScrollViewScrollIndicatorInsets = @"scrollView.scrollIndicatorInsets";

// UITableView
NSString * const kPOPTableViewContentOffset = kPOPScrollViewContentOffset;
NSString * const kPOPTableViewContentSize = kPOPScrollViewContentSize;

// UICollectionView
NSString * const kPOPCollectionViewContentOffset = kPOPScrollViewContentOffset;
NSString * const kPOPCollectionViewContentSize = kPOPScrollViewContentSize;

// UINavigationBar
NSString * const kPOPNavigationBarBarTintColor = @"navigationBar.barTintColor";

// UIToolbar
NSString * const kPOPToolbarBarTintColor = kPOPNavigationBarBarTintColor;

// UITabBar
NSString * const kPOPTabBarBarTintColor = kPOPNavigationBarBarTintColor;

// UILabel
NSString * const kPOPLabelTextColor = @"label.textColor";

#else

// NSView
NSString * const kPOPViewFrame = @"view.frame";
NSString * const kPOPViewBounds = @"view.bounds";
NSString * const kPOPViewAlphaValue = @"view.alphaValue";
NSString * const kPOPViewFrameRotation = @"view.frameRotation";
NSString * const kPOPViewFrameCenterRotation = @"view.frameCenterRotation";
NSString * const kPOPViewBoundsRotation = @"view.boundsRotation";

// NSWindow
NSString * const kPOPWindowFrame = @"window.frame";
NSString * const kPOPWindowAlphaValue = @"window.alphaValue";
NSString * const kPOPWindowBackgroundColor = @"window.backgroundColor";

#endif

#if SCENEKIT_SDK_AVAILABLE

// SceneKit
NSString * const kPOPSCNNodePosition = @"scnode.position";
NSString * const kPOPSCNNodePositionX = @"scnnode.position.x";
NSString * const kPOPSCNNodePositionY = @"scnnode.position.y";
NSString * const kPOPSCNNodePositionZ = @"scnnode.position.z";
NSString * const kPOPSCNNodeTranslation = @"scnnode.translation";
NSString * const kPOPSCNNodeTranslationX = @"scnnode.translation.x";
NSString * const kPOPSCNNodeTranslationY = @"scnnode.translation.y";
NSString * const kPOPSCNNodeTranslationZ = @"scnnode.translation.z";
NSString * const kPOPSCNNodeRotation = @"scnnode.rotation";
NSString * const kPOPSCNNodeRotationX = @"scnnode.rotation.x";
NSString * const kPOPSCNNodeRotationY = @"scnnode.rotation.y";
NSString * const kPOPSCNNodeRotationZ = @"scnnode.rotation.z";
NSString * const kPOPSCNNodeRotationW = @"scnnode.rotation.w";
NSString * const kPOPSCNNodeEulerAngles = @"scnnode.eulerAngles";
NSString * const kPOPSCNNodeEulerAnglesX = @"scnnode.eulerAngles.x";
NSString * const kPOPSCNNodeEulerAnglesY = @"scnnode.eulerAngles.y";
NSString * const kPOPSCNNodeEulerAnglesZ = @"scnnode.eulerAngles.z";
NSString * const kPOPSCNNodeOrientation = @"scnnode.orientation";
NSString * const kPOPSCNNodeOrientationX = @"scnnode.orientation.x";
NSString * const kPOPSCNNodeOrientationY = @"scnnode.orientation.y";
NSString * const kPOPSCNNodeOrientationZ = @"scnnode.orientation.z";
NSString * const kPOPSCNNodeOrientationW = @"scnnode.orientation.w";
NSString * const kPOPSCNNodeScale = @"scnnode.scale";
NSString * const kPOPSCNNodeScaleX = @"scnnode.scale.x";
NSString * const kPOPSCNNodeScaleY = @"scnnode.scale.y";
NSString * const kPOPSCNNodeScaleZ = @"scnnode.scale.z";
NSString * const kPOPSCNNodeScaleXY = @"scnnode.scale.xy";

#endif

/**
 State structure internal to static animatable property.
 */
typedef struct
{
  NSString *name;
  POPAnimatablePropertyReadBlock readBlock;
  POPAnimatablePropertyWriteBlock writeBlock;
  CGFloat threshold;
} _POPStaticAnimatablePropertyState;
typedef _POPStaticAnimatablePropertyState POPStaticAnimatablePropertyState;

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
static POPStaticAnimatablePropertyState _staticStates[] =
{
  /* CALayer */

  {kPOPLayerBackgroundColor,
    ^(CALayer *obj, CGFloat values[]) {
      POPCGColorGetRGBAComponents(obj.backgroundColor, values);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      CGColorRef color = POPCGColorRGBACreate(values);
      [obj setBackgroundColor:color];
      CGColorRelease(color);
    },
    kPOPThresholdColor
  },

  {kPOPLayerBounds,
    ^(CALayer *obj, CGFloat values[]) {
      values_from_rect(values, [obj bounds]);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      [obj setBounds:values_to_rect(values)];
    },
    kPOPThresholdPoint
  },

  {kPOPLayerCornerRadius,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = [obj cornerRadius];
    },
    ^(CALayer *obj, const CGFloat values[]) {
      [obj setCornerRadius:values[0]];
    },
    kPOPThresholdRadius
  },

  {kPOPLayerBorderWidth,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = [obj borderWidth];
    },
    ^(CALayer *obj, const CGFloat values[]) {
      [obj setBorderWidth:values[0]];
    },
    0.01
  },

  {kPOPLayerBorderColor,
    ^(CALayer *obj, CGFloat values[]) {
      POPCGColorGetRGBAComponents(obj.borderColor, values);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      CGColorRef color = POPCGColorRGBACreate(values);
      [obj setBorderColor:color];
      CGColorRelease(color);
    },
    kPOPThresholdColor
  },

  {kPOPLayerPosition,
    ^(CALayer *obj, CGFloat values[]) {
      values_from_point(values, [(CALayer *)obj position]);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      [obj setPosition:values_to_point(values)];
    },
    kPOPThresholdPoint
  },

  {kPOPLayerPositionX,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = [(CALayer *)obj position].x;
    },
    ^(CALayer *obj, const CGFloat values[]) {
      CGPoint p = [(CALayer *)obj position];
      p.x = values[0];
      [obj setPosition:p];
    },
    kPOPThresholdPoint
  },

  {kPOPLayerPositionY,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = [(CALayer *)obj position].y;
    },
    ^(CALayer *obj, const CGFloat values[]) {
      CGPoint p = [(CALayer *)obj position];
      p.y = values[0];
      [obj setPosition:p];
    },
    kPOPThresholdPoint
  },

  {kPOPLayerOpacity,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = [obj opacity];
    },
    ^(CALayer *obj, const CGFloat values[]) {
      [obj setOpacity:((float)values[0])];
    },
    kPOPThresholdOpacity
  },

  {kPOPLayerScaleX,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetScaleX(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetScaleX(obj, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPLayerScaleY,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetScaleY(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetScaleY(obj, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPLayerScaleXY,
    ^(CALayer *obj, CGFloat values[]) {
      values_from_point(values, POPLayerGetScaleXY(obj));
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetScaleXY(obj, values_to_point(values));
    },
    kPOPThresholdScale
  },

  {kPOPLayerSubscaleXY,
    ^(CALayer *obj, CGFloat values[]) {
      values_from_point(values, POPLayerGetSubScaleXY(obj));
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetSubScaleXY(obj, values_to_point(values));
    },
    kPOPThresholdScale
  },

  {kPOPLayerTranslationX,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetTranslationX(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetTranslationX(obj, values[0]);
    },
    kPOPThresholdPoint
  },

  {kPOPLayerTranslationY,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetTranslationY(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetTranslationY(obj, values[0]);
    },
    kPOPThresholdPoint
  },

  {kPOPLayerTranslationZ,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetTranslationZ(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetTranslationZ(obj, values[0]);
    },
    kPOPThresholdPoint
  },

  {kPOPLayerTranslationXY,
    ^(CALayer *obj, CGFloat values[]) {
      values_from_point(values, POPLayerGetTranslationXY(obj));
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetTranslationXY(obj, values_to_point(values));
    },
    kPOPThresholdPoint
  },

  {kPOPLayerSubtranslationX,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetSubTranslationX(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetSubTranslationX(obj, values[0]);
    },
    kPOPThresholdPoint
  },

  {kPOPLayerSubtranslationY,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetSubTranslationY(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetSubTranslationY(obj, values[0]);
    },
    kPOPThresholdPoint
  },

  {kPOPLayerSubtranslationZ,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetSubTranslationZ(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetSubTranslationZ(obj, values[0]);
    },
    kPOPThresholdPoint
  },

  {kPOPLayerSubtranslationXY,
    ^(CALayer *obj, CGFloat values[]) {
      values_from_point(values, POPLayerGetSubTranslationXY(obj));
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetSubTranslationXY(obj, values_to_point(values));
    },
    kPOPThresholdPoint
  },

  {kPOPLayerZPosition,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = [obj zPosition];
    },
    ^(CALayer *obj, const CGFloat values[]) {
      [obj setZPosition:values[0]];
    },
    kPOPThresholdPoint
  },

  {kPOPLayerSize,
    ^(CALayer *obj, CGFloat values[]) {
      values_from_size(values, [obj bounds].size);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      CGSize size = values_to_size(values);
      if (size.width < 0. || size.height < 0.)
        return;

      CGRect b = [obj bounds];
      b.size = size;
      [obj setBounds:b];
    },
    kPOPThresholdPoint
  },

  {kPOPLayerRotation,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetRotation(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetRotation(obj, values[0]);
    },
    kPOPThresholdRotation
  },

  {kPOPLayerRotationY,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetRotationY(obj);
    },
    ^(id obj, const CGFloat values[]) {
      POPLayerSetRotationY(obj, values[0]);
    },
    kPOPThresholdRotation
  },

  {kPOPLayerRotationX,
    ^(CALayer *obj, CGFloat values[]) {
      values[0] = POPLayerGetRotationX(obj);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      POPLayerSetRotationX(obj, values[0]);
    },
    kPOPThresholdRotation
  },

  {kPOPLayerShadowColor,
    ^(CALayer *obj, CGFloat values[]) {
        POPCGColorGetRGBAComponents(obj.shadowColor, values);
    },
    ^(CALayer *obj, const CGFloat values[]) {
        CGColorRef color = POPCGColorRGBACreate(values);
        [obj setShadowColor:color];
        CGColorRelease(color);
    },
    0.01
  },

  {kPOPLayerShadowOffset,
    ^(CALayer *obj, CGFloat values[]) {
        values_from_size(values, [obj shadowOffset]);
    },
    ^(CALayer *obj, const CGFloat values[]) {
        CGSize size = values_to_size(values);
        [obj setShadowOffset:size];
    },
    0.01
  },

  {kPOPLayerShadowOpacity,
    ^(CALayer *obj, CGFloat values[]) {
        values[0] = [obj shadowOpacity];
    },
    ^(CALayer *obj, const CGFloat values[]) {
        [obj setShadowOpacity:values[0]];
    },
    kPOPThresholdOpacity
  },

  {kPOPLayerShadowRadius,
    ^(CALayer *obj, CGFloat values[]) {
        values[0] = [obj shadowRadius];
    },
    ^(CALayer *obj, const CGFloat values[]) {
        [obj setShadowRadius:values[0]];
    },
    kPOPThresholdRadius
  },

  /* CAShapeLayer */

  {kPOPShapeLayerStrokeStart,
    ^(CAShapeLayer *obj, CGFloat values[]) {
      values[0] = obj.strokeStart;
    },
    ^(CAShapeLayer *obj, const CGFloat values[]) {
      obj.strokeStart = values[0];
    },
    0.01
  },

  {kPOPShapeLayerStrokeEnd,
    ^(CAShapeLayer *obj, CGFloat values[]) {
      values[0] = obj.strokeEnd;
    },
    ^(CAShapeLayer *obj, const CGFloat values[]) {
      obj.strokeEnd = values[0];
    },
    0.01
  },

  {kPOPShapeLayerStrokeColor,
    ^(CAShapeLayer *obj, CGFloat values[]) {
        POPCGColorGetRGBAComponents(obj.strokeColor, values);
    },
    ^(CAShapeLayer *obj, const CGFloat values[]) {
        CGColorRef color = POPCGColorRGBACreate(values);
        [obj setStrokeColor:color];
        CGColorRelease(color);
    },
    kPOPThresholdColor
  },

  {kPOPShapeLayerFillColor,
    ^(CAShapeLayer *obj, CGFloat values[]) {
        POPCGColorGetRGBAComponents(obj.fillColor, values);
    },
    ^(CAShapeLayer *obj, const CGFloat values[]) {
        CGColorRef color = POPCGColorRGBACreate(values);
        [obj setFillColor:color];
        CGColorRelease(color);
    },
    kPOPThresholdColor
  },

  {kPOPShapeLayerLineWidth,
    ^(CAShapeLayer *obj, CGFloat values[]) {
        values[0] = obj.lineWidth;
    },
    ^(CAShapeLayer *obj, const CGFloat values[]) {
        obj.lineWidth = values[0];
    },
    0.01
  },
    
    {kPOPShapeLayerLineDashPhase,
        ^(CAShapeLayer *obj, CGFloat values[]) {
            values[0] = obj.lineDashPhase;
        },
        ^(CAShapeLayer *obj, const CGFloat values[]) {
            obj.lineDashPhase = values[0];
        },
        0.01
    },

  {kPOPLayoutConstraintConstant,
    ^(NSLayoutConstraint *obj, CGFloat values[]) {
      values[0] = obj.constant;
    },
    ^(NSLayoutConstraint *obj, const CGFloat values[]) {
      obj.constant = values[0];
    },
    0.01
  },

#if TARGET_OS_IPHONE

  /* UIView */

  {kPOPViewAlpha,
    ^(UIView *obj, CGFloat values[]) {
      values[0] = obj.alpha;
    },
    ^(UIView *obj, const CGFloat values[]) {
      obj.alpha = values[0];
    },
    kPOPThresholdOpacity
  },

  {kPOPViewBackgroundColor,
    ^(UIView *obj, CGFloat values[]) {
      POPUIColorGetRGBAComponents(obj.backgroundColor, values);
    },
    ^(UIView *obj, const CGFloat values[]) {
      obj.backgroundColor = POPUIColorRGBACreate(values);
    },
    kPOPThresholdColor
  },

  {kPOPViewCenter,
    ^(UIView *obj, CGFloat values[]) {
      values_from_point(values, obj.center);
    },
    ^(UIView *obj, const CGFloat values[]) {
      obj.center = values_to_point(values);
    },
    kPOPThresholdPoint
  },

  {kPOPViewFrame,
    ^(UIView *obj, CGFloat values[]) {
      values_from_rect(values, obj.frame);
    },
    ^(UIView *obj, const CGFloat values[]) {
      obj.frame = values_to_rect(values);
    },
    kPOPThresholdPoint
  },

  {kPOPViewScaleX,
    ^(UIView *obj, CGFloat values[]) {
      values[0] = POPLayerGetScaleX(obj.layer);
    },
    ^(UIView *obj, const CGFloat values[]) {
      POPLayerSetScaleX(obj.layer, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPViewScaleY,
    ^(UIView *obj, CGFloat values[]) {
      values[0] = POPLayerGetScaleY(obj.layer);
    },
    ^(UIView *obj, const CGFloat values[]) {
      POPLayerSetScaleY(obj.layer, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPViewScaleXY,
    ^(UIView *obj, CGFloat values[]) {
      values_from_point(values, POPLayerGetScaleXY(obj.layer));
    },
    ^(UIView *obj, const CGFloat values[]) {
      POPLayerSetScaleXY(obj.layer, values_to_point(values));
    },
    kPOPThresholdScale
  },

  {kPOPViewTintColor,
    ^(UIView *obj, CGFloat values[]) {
      POPUIColorGetRGBAComponents(obj.tintColor, values);
    },
    ^(UIView *obj, const CGFloat values[]) {
        obj.tintColor = POPUIColorRGBACreate(values);
    },
    kPOPThresholdColor
  },

  /* UIScrollView */

  {kPOPScrollViewContentOffset,
    ^(UIScrollView *obj, CGFloat values[]) {
      values_from_point(values, obj.contentOffset);
    },
    ^(UIScrollView *obj, const CGFloat values[]) {
      [obj setContentOffset:values_to_point(values) animated:NO];
    },
    kPOPThresholdPoint
  },

  {kPOPScrollViewContentSize,
    ^(UIScrollView *obj, CGFloat values[]) {
      values_from_size(values, obj.contentSize);
    },
    ^(UIScrollView *obj, const CGFloat values[]) {
      obj.contentSize = values_to_size(values);
    },
    kPOPThresholdPoint
  },

  {kPOPScrollViewZoomScale,
    ^(UIScrollView *obj, CGFloat values[]) {
      values[0]=obj.zoomScale;
    },
    ^(UIScrollView *obj, const CGFloat values[]) {
      obj.zoomScale=values[0];
    },
    kPOPThresholdScale
  },

  {kPOPScrollViewContentInset,
    ^(UIScrollView *obj, CGFloat values[]) {
      values[0] = obj.contentInset.top;
      values[1] = obj.contentInset.left;
      values[2] = obj.contentInset.bottom;
      values[3] = obj.contentInset.right;
    },
    ^(UIScrollView *obj, const CGFloat values[]) {
      obj.contentInset = values_to_edge_insets(values);
    },
    kPOPThresholdPoint
  },

  {kPOPScrollViewScrollIndicatorInsets,
    ^(UIScrollView *obj, CGFloat values[]) {
      values[0] = obj.scrollIndicatorInsets.top;
      values[1] = obj.scrollIndicatorInsets.left;
      values[2] = obj.scrollIndicatorInsets.bottom;
      values[3] = obj.scrollIndicatorInsets.right;
    },
    ^(UIScrollView *obj, const CGFloat values[]) {
      obj.scrollIndicatorInsets = values_to_edge_insets(values);
    },
    kPOPThresholdPoint
  },

  /* UINavigationBar */

  {kPOPNavigationBarBarTintColor,
    ^(UINavigationBar *obj, CGFloat values[]) {
      POPUIColorGetRGBAComponents(obj.barTintColor, values);
    },
    ^(UINavigationBar *obj, const CGFloat values[]) {
      obj.barTintColor = POPUIColorRGBACreate(values);
    },
    kPOPThresholdColor
  },

  /* UILabel */

  {kPOPLabelTextColor,
    ^(UILabel *obj, CGFloat values[]) {
      POPUIColorGetRGBAComponents(obj.textColor, values);
    },
    ^(UILabel *obj, const CGFloat values[]) {
      obj.textColor = POPUIColorRGBACreate(values);
    },
    kPOPThresholdColor
  },

#else

  /* NSView */

  {kPOPViewFrame,
    ^(NSView *obj, CGFloat values[]) {
      values_from_rect(values, NSRectToCGRect(obj.frame));
    },
    ^(NSView *obj, const CGFloat values[]) {
      obj.frame = NSRectFromCGRect(values_to_rect(values));
    },
    kPOPThresholdPoint
  },

  {kPOPViewBounds,
    ^(NSView *obj, CGFloat values[]) {
      values_from_rect(values, NSRectToCGRect(obj.frame));
    },
    ^(NSView *obj, const CGFloat values[]) {
      obj.bounds = NSRectFromCGRect(values_to_rect(values));
    },
    kPOPThresholdPoint
  },

  {kPOPViewAlphaValue,
    ^(NSView *obj, CGFloat values[]) {
      values[0] = obj.alphaValue;
    },
    ^(NSView *obj, const CGFloat values[]) {
      obj.alphaValue = values[0];
    },
    kPOPThresholdOpacity
  },

  {kPOPViewFrameRotation,
    ^(NSView *obj, CGFloat values[]) {
      values[0] = obj.frameRotation;
    },
    ^(NSView *obj, const CGFloat values[]) {
      obj.frameRotation = values[0];
    },
    kPOPThresholdRotation
  },

  {kPOPViewFrameCenterRotation,
    ^(NSView *obj, CGFloat values[]) {
      values[0] = obj.frameCenterRotation;
    },
    ^(NSView *obj, const CGFloat values[]) {
      obj.frameCenterRotation = values[0];
    },
    kPOPThresholdRotation
  },

  {kPOPViewBoundsRotation,
    ^(NSView *obj, CGFloat values[]) {
      values[0] = obj.boundsRotation;
    },
    ^(NSView *obj, const CGFloat values[]) {
      obj.boundsRotation = values[0];
    },
    kPOPThresholdRotation
  },

  /* NSWindow */

  {kPOPWindowFrame,
    ^(NSWindow *obj, CGFloat values[]) {
      values_from_rect(values, NSRectToCGRect(obj.frame));
    },
    ^(NSWindow *obj, const CGFloat values[]) {
      [obj setFrame:NSRectFromCGRect(values_to_rect(values)) display:YES];
    },
    kPOPThresholdPoint
  },

  {kPOPWindowAlphaValue,
    ^(NSWindow *obj, CGFloat values[]) {
      values[0] = obj.alphaValue;
    },
    ^(NSWindow *obj, const CGFloat values[]) {
      obj.alphaValue = values[0];
    },
    kPOPThresholdOpacity
  },

  {kPOPWindowBackgroundColor,
    ^(NSWindow *obj, CGFloat values[]) {
      POPNSColorGetRGBAComponents(obj.backgroundColor, values);
    },
    ^(NSWindow *obj, const CGFloat values[]) {
      obj.backgroundColor = POPNSColorRGBACreate(values);
    },
    kPOPThresholdColor
  },

#endif

#if SCENEKIT_SDK_AVAILABLE

  /* SceneKit */

  {kPOPSCNNodePosition,
    ^(SCNNode *obj, CGFloat values[]) {
      values_from_vec3(values, obj.position);
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.position = values_to_vec3(values);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodePositionX,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.position.x;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.position = SCNVector3Make(values[0], obj.position.y, obj.position.z);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodePositionY,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.position.y;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.position = SCNVector3Make(obj.position.x, values[0], obj.position.z);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodePositionZ,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.position.z;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.position = SCNVector3Make(obj.position.x, obj.position.y, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeTranslation,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.transform.m41;
      values[1] = obj.transform.m42;
      values[2] = obj.transform.m43;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.transform = SCNMatrix4MakeTranslation(values[0], values[1], values[2]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeTranslationX,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.transform.m41;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.transform = SCNMatrix4MakeTranslation(values[0], obj.transform.m42, obj.transform.m43);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeTranslationY,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.transform.m42;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.transform = SCNMatrix4MakeTranslation(obj.transform.m41, values[0], obj.transform.m43);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeTranslationY,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.transform.m43;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.transform = SCNMatrix4MakeTranslation(obj.transform.m41, obj.transform.m42, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeRotation,
    ^(SCNNode *obj, CGFloat values[]) {
      values_from_vec4(values, obj.rotation);
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.rotation = values_to_vec4(values);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeRotationX,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.rotation.x;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.rotation = SCNVector4Make(1.0, obj.rotation.y, obj.rotation.z, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeRotationY,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.rotation.y;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.rotation = SCNVector4Make(obj.rotation.x, 1.0, obj.rotation.z, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeRotationZ,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.rotation.z;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.rotation = SCNVector4Make(obj.rotation.x, obj.rotation.y, 1.0, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeRotationW,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.rotation.w;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.rotation = SCNVector4Make(obj.rotation.x, obj.rotation.y, obj.rotation.z, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeEulerAngles,
    ^(SCNNode *obj, CGFloat values[]) {
      values_from_vec3(values, obj.eulerAngles);
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.eulerAngles = values_to_vec3(values);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeEulerAnglesX,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.eulerAngles.x;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.eulerAngles = SCNVector3Make(values[0], obj.eulerAngles.y, obj.eulerAngles.z);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeEulerAnglesY,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.eulerAngles.y;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.eulerAngles = SCNVector3Make(obj.eulerAngles.x, values[0], obj.eulerAngles.z);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeEulerAnglesZ,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.eulerAngles.z;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.eulerAngles = SCNVector3Make(obj.eulerAngles.x, obj.eulerAngles.y, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeOrientation,
    ^(SCNNode *obj, CGFloat values[]) {
      values_from_vec4(values, obj.orientation);
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.orientation = values_to_vec4(values);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeOrientationX,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.orientation.x;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.orientation = SCNVector4Make(values[0], obj.orientation.y, obj.orientation.z, obj.orientation.w);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeOrientationY,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.orientation.y;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.orientation = SCNVector4Make(obj.orientation.x, values[0], obj.orientation.z, obj.orientation.w);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeOrientationZ,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.orientation.z;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.orientation = SCNVector4Make(obj.orientation.x, obj.orientation.y, values[0], obj.orientation.w);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeOrientationW,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.orientation.w;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.orientation = SCNVector4Make(obj.orientation.x, obj.orientation.y, obj.orientation.z, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeScale,
    ^(SCNNode *obj, CGFloat values[]) {
      values_from_vec3(values, obj.scale);
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.scale = values_to_vec3(values);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeScaleX,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.scale.x;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.scale = SCNVector3Make(values[0], obj.scale.y, obj.scale.z);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeScaleY,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.scale.y;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.position = SCNVector3Make(obj.scale.x, values[0], obj.scale.z);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeScaleZ,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.scale.z;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.scale = SCNVector3Make(obj.scale.x, obj.scale.y, values[0]);
    },
    kPOPThresholdScale
  },

  {kPOPSCNNodeScaleXY,
    ^(SCNNode *obj, CGFloat values[]) {
      values[0] = obj.scale.x;
      values[1] = obj.scale.y;
    },
    ^(SCNNode *obj, const CGFloat values[]) {
      obj.scale = SCNVector3Make(values[0], values[1], obj.scale.z);
    },
    kPOPThresholdScale
  },

#endif

};
#pragma clang diagnostic pop

static NSUInteger staticIndexWithName(NSString *aName)
{
  NSUInteger idx = 0;

  while (idx < POP_ARRAY_COUNT(_staticStates)) {
    if ([_staticStates[idx].name isEqualToString:aName])
      return idx;
    idx++;
  }

  return NSNotFound;
}

/**
 Concrete static property class.
 */
@interface POPStaticAnimatableProperty : POPAnimatableProperty
{
@public
  POPStaticAnimatablePropertyState *_state;
}
@end

@implementation POPStaticAnimatableProperty

- (NSString *)name
{
  return _state->name;
}

- (POPAnimatablePropertyReadBlock)readBlock
{
  return _state->readBlock;
}

- (POPAnimatablePropertyWriteBlock)writeBlock
{
  return _state->writeBlock;
}

- (CGFloat)threshold
{
  return _state->threshold;
}

@end

#pragma mark - Concrete

/**
 Concrete immutable property class.
 */
@interface POPConcreteAnimatableProperty : POPAnimatableProperty
- (instancetype)initWithName:(NSString *)name readBlock:(POPAnimatablePropertyReadBlock)read writeBlock:(POPAnimatablePropertyWriteBlock)write threshold:(CGFloat)threshold;
@end

@implementation POPConcreteAnimatableProperty

// default synthesis
@synthesize name, readBlock, writeBlock, threshold;

- (instancetype)initWithName:(NSString *)aName readBlock:(POPAnimatablePropertyReadBlock)aReadBlock writeBlock:(POPAnimatablePropertyWriteBlock)aWriteBlock threshold:(CGFloat)aThreshold
{
  self = [super init];
  if (nil != self) {
    name = [aName copy];
    readBlock = [aReadBlock copy];
    writeBlock = [aWriteBlock copy];
    threshold = aThreshold;
  }
  return self;
}
@end

#pragma mark - Mutable

@implementation POPMutableAnimatableProperty

// default synthesis
@synthesize name, readBlock, writeBlock, threshold;

@end

#pragma mark - Cluster

/**
 Singleton placeholder property class to support class cluster.
 */
@interface POPPlaceholderAnimatableProperty : POPAnimatableProperty

@end

@implementation POPPlaceholderAnimatableProperty

// default synthesis
@synthesize name, readBlock, writeBlock, threshold;

@end

/**
 Cluster class.
 */
@implementation POPAnimatableProperty

// avoid creating backing ivars
@dynamic name, readBlock, writeBlock, threshold;

static POPAnimatableProperty *placeholder = nil;

+ (void)initialize
{
  if (self == [POPAnimatableProperty class]) {
    placeholder = [POPPlaceholderAnimatableProperty alloc];
  }
}

+ (id)allocWithZone:(struct _NSZone *)zone
{
  if (self == [POPAnimatableProperty class]) {
    if (nil == placeholder) {
      placeholder = [super allocWithZone:zone];
    }
    return placeholder;
  }
  return [super allocWithZone:zone];
}

- (id)copyWithZone:(NSZone *)zone
{
  if ([self isKindOfClass:[POPMutableAnimatableProperty class]]) {
    POPConcreteAnimatableProperty *copyProperty = [[POPConcreteAnimatableProperty alloc] initWithName:self.name readBlock:self.readBlock writeBlock:self.writeBlock threshold:self.threshold];
    return copyProperty;
  } else {
    return self;
  }
}

- (id)mutableCopyWithZone:(NSZone *)zone
{
  POPMutableAnimatableProperty *copyProperty = [[POPMutableAnimatableProperty alloc] init];
  copyProperty.name = self.name;
  copyProperty.readBlock = self.readBlock;
  copyProperty.writeBlock = self.writeBlock;
  copyProperty.threshold = self.threshold;
  return copyProperty;
}

+ (id)propertyWithName:(NSString *)aName
{
  return [self propertyWithName:aName initializer:NULL];
}

+ (id)propertyWithName:(NSString *)aName initializer:(void (^)(POPMutableAnimatableProperty *prop))aBlock
{
  POPAnimatableProperty *prop = nil;

  static NSMutableDictionary *_propertyDict = nil;
  if (nil == _propertyDict) {
    _propertyDict = [[NSMutableDictionary alloc] initWithCapacity:10];
  }

  prop = _propertyDict[aName];
  if (nil != prop) {
    return prop;
  }

  NSUInteger staticIdx = staticIndexWithName(aName);

  if (NSNotFound != staticIdx) {
    POPStaticAnimatableProperty *staticProp = [[POPStaticAnimatableProperty alloc] init];
    staticProp->_state = &_staticStates[staticIdx];
    _propertyDict[aName] = staticProp;
    prop = staticProp;
  } else if (NULL != aBlock) {
    POPMutableAnimatableProperty *mutableProp = [[POPMutableAnimatableProperty alloc] init];
    mutableProp.name = aName;
    mutableProp.threshold = 1.0;
    aBlock(mutableProp);
    prop = [mutableProp copy];
  }

  return prop;
}

- (NSString *)description
{
  NSMutableString *s = [NSMutableString stringWithFormat:@"%@ name:%@ threshold:%f", super.description, self.name, self.threshold];
  return s;
}

@end


================================================
FILE: pop/POPAnimatablePropertyTypes.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

typedef void (^POPAnimatablePropertyReadBlock)(id obj, CGFloat values[]);
typedef void (^POPAnimatablePropertyWriteBlock)(id obj, const CGFloat values[]);


================================================
FILE: pop/POPAnimation.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <Foundation/NSObject.h>

#import <pop/POPAnimationTracer.h>
#import <pop/POPGeometry.h>

@class CAMediaTimingFunction;

/**
 @abstract The abstract animation base class.
 @discussion Instantiate and use one of the concrete animation subclasses.
 */
@interface POPAnimation : NSObject

/**
 @abstract The name of the animation.
 @discussion Optional property to help identify the animation.
 */
@property (copy, nonatomic) NSString *name;

/**
 @abstract The beginTime of the animation in media time.
 @discussion Defaults to 0 and starts immediately.
 */
@property (assign, nonatomic) CFTimeInterval beginTime;

/**
 @abstract The animation delegate.
 @discussion See {@ref POPAnimationDelegate} for details.
 */
@property (weak, nonatomic) id delegate;

/**
 @abstract The animation tracer.
 @discussion Returns the existing tracer, creating one if needed. Call start/stop on the tracer to toggle event collection.
 */
@property (readonly, nonatomic) POPAnimationTracer *tracer;

/**
 @abstract Optional block called on animation start.
 */
@property (copy, nonatomic) void (^animationDidStartBlock)(POPAnimation *anim);

/**
 @abstract Optional block called when value meets or exceeds to value.
 */
@property (copy, nonatomic) void (^animationDidReachToValueBlock)(POPAnimation *anim);

/**
 @abstract Optional block called on animation completion.
 */
@property (copy, nonatomic) void (^completionBlock)(POPAnimation *anim, BOOL finished);

/**
 @abstract Optional block called each frame animation is applied.
 */
@property (copy, nonatomic) void (^animationDidApplyBlock)(POPAnimation *anim);

/**
 @abstract Flag indicating whether animation should be removed on completion.
 @discussion Setting to NO can facilitate animation reuse. Defaults to YES.
 */
@property (assign, nonatomic) BOOL removedOnCompletion;

/**
 @abstract Flag indicating whether animation is paused.
 @discussion A paused animation is excluded from the list of active animations. On initial creation, defaults to YES. On animation addition, the animation is implicity unpaused. On animation completion, the animation is implicity paused including for animations with removedOnCompletion set to NO.
 */
@property (assign, nonatomic, getter = isPaused) BOOL paused;

/**
 @abstract Flag indicating whether animation autoreverses.
 @discussion An animation that autoreverses will have twice the duration before it is considered finished. It will animate to the toValue, stop, then animate back to the original fromValue. The delegate methods are called as follows:

     1) animationDidStart: is called at the beginning, as usual, and then after each toValue is reached and the autoreverse is going to start.
     2) animationDidReachToValue: is called every time the toValue is reached. The toValue is swapped with the fromValue at the end of each animation segment. This means that with autoreverses set to YES, the animationDidReachToValue: delegate method will be called a minimum of twice.
     3) animationDidStop:finished: is called every time the toValue is reached, the finished argument will be NO if the autoreverse is not yet complete.
 */
@property (assign, nonatomic) BOOL autoreverses;

/**
 @abstract The number of times to repeat the animation.
 @discussion A repeatCount of 0 or 1 means that the animation will not repeat, just like Core Animation. A repeatCount of 2 or greater means that the animation will run that many times before stopping. The delegate methods are called as follows:

     1) animationDidStart: is called at the beginning of each animation repeat.
     2) animationDidReachToValue: is called every time the toValue is reached.
     3) animationDidStop:finished: is called every time the toValue is reached, the finished argument will be NO if the autoreverse is not yet complete.

When combined with the autoreverses property, a singular animation is effectively twice as long.
 */
@property (assign, nonatomic) NSInteger repeatCount;

/**
 @abstract Repeat the animation forever.
 @discussion This property will make the animation repeat forever. The value of the repeatCount property is undefined when this property is set. The finished parameter of the delegate callback animationDidStop:finished: will always be NO.
 */
@property (assign, nonatomic) BOOL repeatForever;

@end

/**
 @abstract The animation delegate.
 */
@protocol POPAnimationDelegate <NSObject>
@optional

/**
 @abstract Called on animation start.
 @param anim The relevant animation.
 */
- (void)pop_animationDidStart:(POPAnimation *)anim;

/**
 @abstract Called when value meets or exceeds to value.
 @param anim The relevant animation.
 */
- (void)pop_animationDidReachToValue:(POPAnimation *)anim;

/**
 @abstract Called on animation stop.
 @param anim The relevant animation.
 @param finished Flag indicating finished state. Flag is true if the animation reached completion before being removed.
 */
- (void)pop_animationDidStop:(POPAnimation *)anim finished:(BOOL)finished;

/**
 @abstract Called each frame animation is applied.
 @param anim The relevant animation.
 */
- (void)pop_animationDidApply:(POPAnimation *)anim;

@end


@interface NSObject (POP)

/**
 @abstract Add an animation to the reciver.
 @param anim The animation to add.
 @param key The key used to identify the animation.
 @discussion The 'key' may be any string such that only one animation per unique key is added per object.
 */
- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key;

/**
 @abstract Remove all animations attached to the receiver.
 */
- (void)pop_removeAllAnimations;

/**
 @abstract Remove any animation attached to the receiver for 'key'.
 @param key The key used to identify the animation.
 */
- (void)pop_removeAnimationForKey:(NSString *)key;

/**
 @abstract Returns an array containing the keys of all animations currently attached to the receiver.
 The order of keys reflects the order in which animations will be applied.
 */
- (NSArray *)pop_animationKeys;

/**
 @abstract Returns any animation attached to the receiver.
 @param key The key used to identify the animation.
 @returns The animation currently attached, or nil if no such animation exists.
 */
- (id)pop_animationForKey:(NSString *)key;

@end

/**
 *  This implementation of NSCopying does not do any copying of animation's state, but only configuration.
 *  i.e. you cannot copy an animation and expect to apply it to a view and have the copied animation pick up where the original left off.
 *  Two common uses of copying animations:
 *  * you need to apply the same animation to multiple different views.
 *  * you need to absolutely ensure that the the caller of your function cannot mutate the animation once it's been passed in.
 */
@interface POPAnimation (NSCopying) <NSCopying>

@end


================================================
FILE: pop/POPAnimation.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPAnimationExtras.h"
#import "POPAnimationInternal.h"

#import <objc/runtime.h>

#import "POPAction.h"
#import "POPAnimationRuntime.h"
#import "POPAnimationTracerInternal.h"
#import "POPAnimatorPrivate.h"

using namespace POP;

#pragma mark - POPAnimation

@implementation POPAnimation
@synthesize solver = _solver;
@synthesize currentValue = _currentValue;
@synthesize progressMarkers = _progressMarkers;

#pragma mark - Lifecycle

- (id)init
{
  [NSException raise:NSStringFromClass([self class]) format:@"Attempting to instantiate an abstract class. Use a concrete subclass instead."];
  return nil;
}

- (id)_init
{
  self = [super init];
  if (nil != self) {
    [self _initState];
  }
  return self;
}

- (void)_initState
{
  _state = new POPAnimationState(self);
}

- (void)dealloc
{
  if (_state) {
    delete _state;
    _state = NULL;
  };
}

#pragma mark - Properties

- (id)delegate
{
  return _state->delegate;
}

- (void)setDelegate:(id)delegate
{
  _state->setDelegate(delegate);
}

- (BOOL)isPaused
{
  return _state->paused;
}

- (void)setPaused:(BOOL)paused
{
  _state->setPaused(paused ? true : false);
}

- (NSInteger)repeatCount
{
  if (_state->autoreverses) {
    return _state->repeatCount / 2;
  } else {
    return _state->repeatCount;
  }
}

- (void)setRepeatCount:(NSInteger)repeatCount
{
  if (repeatCount > 0) {
    if (repeatCount > NSIntegerMax / 2) {
      repeatCount = NSIntegerMax / 2;
    }

    if (_state->autoreverses) {
      _state->repeatCount = (repeatCount * 2);
    } else {
      _state->repeatCount = repeatCount;
    }
  }
}

- (BOOL)autoreverses
{
  return _state->autoreverses;
}

- (void)setAutoreverses:(BOOL)autoreverses
{
  _state->autoreverses = autoreverses;
  if (autoreverses) {
    if (_state->repeatCount == 0) {
      [self setRepeatCount:1];
    }
  }
}

FB_PROPERTY_GET(POPAnimationState, type, POPAnimationType);
DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, animationDidStartBlock, setAnimationDidStartBlock:, POPAnimationDidStartBlock);
DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, animationDidReachToValueBlock, setAnimationDidReachToValueBlock:, POPAnimationDidReachToValueBlock);
DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, completionBlock, setCompletionBlock:, POPAnimationCompletionBlock);
DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, animationDidApplyBlock, setAnimationDidApplyBlock:, POPAnimationDidApplyBlock);
DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, name, setName:, NSString*);
DEFINE_RW_PROPERTY(POPAnimationState, beginTime, setBeginTime:, CFTimeInterval);
DEFINE_RW_FLAG(POPAnimationState, removedOnCompletion, removedOnCompletion, setRemovedOnCompletion:);
DEFINE_RW_FLAG(POPAnimationState, repeatForever, repeatForever, setRepeatForever:);

- (id)valueForUndefinedKey:(NSString *)key
{
  return _state->dict[key];
}

- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
  if (!value) {
    [_state->dict removeObjectForKey:key];
  } else {
    if (!_state->dict)
      _state->dict = [[NSMutableDictionary alloc] init];
    _state->dict[key] = value;
  }
}

- (POPAnimationTracer *)tracer
{
  if (!_state->tracer) {
    _state->tracer = [[POPAnimationTracer alloc] initWithAnimation:self];
  }
  return _state->tracer;
}

- (NSString *)description
{
  NSMutableString *s = [NSMutableString stringWithFormat:@"<%@:%p", NSStringFromClass([self class]), self];
  [self _appendDescription:s debug:NO];
  [s appendString:@">"];
  return s;
}

- (NSString *)debugDescription
{
  NSMutableString *s = [NSMutableString stringWithFormat:@"<%@:%p", NSStringFromClass([self class]), self];
  [self _appendDescription:s debug:YES];
  [s appendString:@">"];
  return s;
}

#pragma mark - Utility

POPAnimationState *POPAnimationGetState(POPAnimation *a)
{
  return a->_state;
}

- (BOOL)_advance:(id)object currentTime:(CFTimeInterval)currentTime elapsedTime:(CFTimeInterval)elapsedTime
{
  return YES;
}

- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug
{
  if (_state->name)
    [s appendFormat:@"; name = %@", _state->name];
  
  if (!self.removedOnCompletion)
    [s appendFormat:@"; removedOnCompletion = %@", POPStringFromBOOL(self.removedOnCompletion)];
  
  if (debug) {
    if (_state->active)
      [s appendFormat:@"; active = %@", POPStringFromBOOL(_state->active)];
    
    if (_state->paused)
      [s appendFormat:@"; paused = %@", POPStringFromBOOL(_state->paused)];
  }
  
  if (_state->beginTime) {
    [s appendFormat:@"; beginTime = %f", _state->beginTime];
  }
  
  for (NSString *key in _state->dict) {
    [s appendFormat:@"; %@ = %@", key, _state->dict[key]];
  }
}

@end


#pragma mark - POPPropertyAnimation

#pragma mark - POPBasicAnimation

#pragma mark - POPDecayAnimation

@implementation NSObject (POP)

- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key
{
  [[POPAnimator sharedAnimator] addAnimation:anim forObject:self key:key];
}

- (void)pop_removeAllAnimations
{
  [[POPAnimator sharedAnimator] removeAllAnimationsForObject:self];
}

- (void)pop_removeAnimationForKey:(NSString *)key
{
  [[POPAnimator sharedAnimator] removeAnimationForObject:self key:key];
}

- (NSArray *)pop_animationKeys
{
  return [[POPAnimator sharedAnimator] animationKeysForObject:self];
}

- (id)pop_animationForKey:(NSString *)key
{
  return [[POPAnimator sharedAnimator] animationForObject:self key:key];
}

@end

@implementation NSProxy (POP)

- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key
{
  [[POPAnimator sharedAnimator] addAnimation:anim forObject:self key:key];
}

- (void)pop_removeAllAnimations
{
  [[POPAnimator sharedAnimator] removeAllAnimationsForObject:self];
}

- (void)pop_removeAnimationForKey:(NSString *)key
{
  [[POPAnimator sharedAnimator] removeAnimationForObject:self key:key];
}

- (NSArray *)pop_animationKeys
{
  return [[POPAnimator sharedAnimator] animationKeysForObject:self];
}

- (id)pop_animationForKey:(NSString *)key
{
  return [[POPAnimator sharedAnimator] animationForObject:self key:key];
}

@end

@implementation POPAnimation (NSCopying)

- (instancetype)copyWithZone:(NSZone *)zone
{
  /*
   * Must use [self class] instead of POPAnimation so that subclasses can call this via super.
   * Even though POPAnimation and POPPropertyAnimation throw exceptions on init,
   * it's safe to call it since you can only copy objects that have been successfully created.
   */
  POPAnimation *copy = [[[self class] allocWithZone:zone] init];
  
  if (copy) {
    copy.name = self.name;
    copy.beginTime = self.beginTime;
    copy.delegate = self.delegate;
    copy.animationDidStartBlock = self.animationDidStartBlock;
    copy.animationDidReachToValueBlock = self.animationDidReachToValueBlock;
    copy.completionBlock = self.completionBlock;
    copy.animationDidApplyBlock = self.animationDidApplyBlock;
    copy.removedOnCompletion = self.removedOnCompletion;
    
    copy.autoreverses = self.autoreverses;
    copy.repeatCount = self.repeatCount;
    copy.repeatForever = self.repeatForever;
  }
    
  return copy;
}

@end

================================================
FILE: pop/POPAnimationEvent.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <Foundation/Foundation.h>

/**
 @abstract Enumeraton of animation event types.
 */
typedef NS_ENUM(NSUInteger, POPAnimationEventType) {
  kPOPAnimationEventPropertyRead = 0,
  kPOPAnimationEventPropertyWrite,
  kPOPAnimationEventToValueUpdate,
  kPOPAnimationEventFromValueUpdate,
  kPOPAnimationEventVelocityUpdate,
  kPOPAnimationEventBouncinessUpdate,
  kPOPAnimationEventSpeedUpdate,
  kPOPAnimationEventFrictionUpdate,
  kPOPAnimationEventMassUpdate,
  kPOPAnimationEventTensionUpdate,
  kPOPAnimationEventDidStart,
  kPOPAnimationEventDidStop,
  kPOPAnimationEventDidReachToValue,
  kPOPAnimationEventAutoreversed
};

/**
 @abstract The base animation event class.
 */
@interface POPAnimationEvent : NSObject

/**
 @abstract The event type. See {@ref POPAnimationEventType} for possible values.
 */
@property (readonly, nonatomic, assign) POPAnimationEventType type;

/**
 @abstract The time of event.
 */
@property (readonly, nonatomic, assign) CFTimeInterval time;

/**
 @abstract Optional string describing the animation at time of event.
 */
@property (readonly, nonatomic, copy) NSString *animationDescription;

@end

/**
 @abstract An animation event subclass for recording value and velocity.
 */
@interface POPAnimationValueEvent : POPAnimationEvent

/**
 @abstract The value recorded.
 */
@property (readonly, nonatomic, strong) id value;

/**
 @abstract The velocity recorded, if any.
 */
@property (readonly, nonatomic, strong) id velocity;

@end


================================================
FILE: pop/POPAnimationEvent.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPAnimationEvent.h"
#import "POPAnimationEventInternal.h"

static NSString *stringFromType(POPAnimationEventType aType)
{
  switch (aType) {
    case kPOPAnimationEventPropertyRead:
      return @"read";
    case kPOPAnimationEventPropertyWrite:
      return @"write";
    case kPOPAnimationEventToValueUpdate:
      return @"toValue";
    case kPOPAnimationEventFromValueUpdate:
      return @"fromValue";
    case kPOPAnimationEventVelocityUpdate:
      return @"velocity";
    case kPOPAnimationEventSpeedUpdate:
      return @"speed";
    case kPOPAnimationEventBouncinessUpdate:
      return @"bounciness";
    case kPOPAnimationEventFrictionUpdate:
      return @"friction";
    case kPOPAnimationEventMassUpdate:
      return @"mass";
    case kPOPAnimationEventTensionUpdate:
      return @"tension";
    case kPOPAnimationEventDidStart:
      return @"didStart";
    case kPOPAnimationEventDidStop:
      return @"didStop";
    case kPOPAnimationEventDidReachToValue:
      return @"didReachToValue";
    case kPOPAnimationEventAutoreversed:
      return @"autoreversed";
    default:
      return nil;
  }
}

@implementation POPAnimationEvent
@synthesize type = _type;
@synthesize time = _time;
@synthesize animationDescription = _animationDescription;

- (instancetype)initWithType:(POPAnimationEventType)aType time:(CFTimeInterval)aTime
{
  self = [super init];
  if (nil != self) {
    _type = aType;
    _time = aTime;
  }
  return self;
}

- (NSString *)description
{
  NSMutableString *s = [NSMutableString stringWithFormat:@"<POPAnimationEvent:%f; type = %@", _time, stringFromType(_type)];
  [self _appendDescription:s];
  [s appendString:@">"];
  return s;
}

// subclass override
- (void)_appendDescription:(NSMutableString *)s
{
  if (0 != _animationDescription.length) {
    [s appendFormat:@"; animation = %@", _animationDescription];
  }
}

@end

@implementation POPAnimationValueEvent
@synthesize value = _value;
@synthesize velocity = _velocity;

- (instancetype)initWithType:(POPAnimationEventType)aType time:(CFTimeInterval)aTime value:(id)aValue
{
  self = [self initWithType:aType time:aTime];
  if (nil != self) {
    _value = aValue;
  }
  return self;
}

- (void)_appendDescription:(NSMutableString *)s
{
  [super _appendDescription:s];

  if (nil != _value) {
    [s appendFormat:@"; value = %@", _value];
  }

  if (nil != _velocity) {
    [s appendFormat:@"; velocity = %@", _velocity];
  }
}

@end


================================================
FILE: pop/POPAnimationEventInternal.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <Foundation/Foundation.h>

#import "POPAnimationEvent.h"

@interface POPAnimationEvent ()

/**
 @abstract Default initializer.
 */
- (instancetype)initWithType:(POPAnimationEventType)type time:(CFTimeInterval)time;

/**
 @abstract Readwrite redefinition of public property.
 */
@property (readwrite, nonatomic, copy) NSString *animationDescription;

@end

@interface POPAnimationValueEvent ()

/**
 @abstract Default initializer.
 */
- (instancetype)initWithType:(POPAnimationEventType)type time:(CFTimeInterval)time value:(id)value;

/**
 @abstract Readwrite redefinition of public property.
 */
@property (readwrite, nonatomic, strong) id velocity;

@end



================================================
FILE: pop/POPAnimationExtras.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <QuartzCore/CAAnimation.h>

#import <pop/POPDefines.h>
#import <pop/POPSpringAnimation.h>

/**
 @abstract The current drag coefficient.
 @discussion A value greater than 1.0 indicates Simulator slow-motion animations are enabled. Defaults to 1.0.
 */
extern CGFloat POPAnimationDragCoefficient(void);

@interface CAAnimation (POPAnimationExtras)

/**
 @abstract Apply the current drag coefficient to animation speed.
 @discussion Convenience utility to respect Simulator slow-motion animation settings.
 */
- (void)pop_applyDragCoefficient;

@end

@interface POPSpringAnimation (POPAnimationExtras)

/**
 @abstract Converts from spring bounciness and speed to tension, friction and mass dynamics values.
 */
+ (void)convertBounciness:(CGFloat)bounciness speed:(CGFloat)speed toTension:(CGFloat *)outTension friction:(CGFloat *)outFriction mass:(CGFloat *)outMass;

/**
 @abstract Converts from dynamics tension, friction and mass to spring bounciness and speed values.
 */
+ (void)convertTension:(CGFloat)tension friction:(CGFloat)friction toBounciness:(CGFloat *)outBounciness speed:(CGFloat *)outSpeed;

@end


================================================
FILE: pop/POPAnimationExtras.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPAnimationExtras.h"
#import "POPAnimationPrivate.h"

#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#endif

#if TARGET_IPHONE_SIMULATOR
UIKIT_EXTERN float UIAnimationDragCoefficient(); // UIKit private drag coefficient, use judiciously
#endif

#import "POPMath.h"

CGFloat POPAnimationDragCoefficient()
{
#if TARGET_IPHONE_SIMULATOR
  return UIAnimationDragCoefficient();
#else
  return 1.0;
#endif
}

@implementation CAAnimation (POPAnimationExtras)

- (void)pop_applyDragCoefficient
{
  CGFloat k = POPAnimationDragCoefficient();
  if (k != 0 && k != 1)
    self.speed = 1 / k;
}

@end

@implementation POPSpringAnimation (POPAnimationExtras)

static const CGFloat POPBouncy3NormalizationRange = 20.0;
static const CGFloat POPBouncy3NormalizationScale = 1.7;
static const CGFloat POPBouncy3BouncinessNormalizedMin = 0.0;
static const CGFloat POPBouncy3BouncinessNormalizedMax = 0.8;
static const CGFloat POPBouncy3SpeedNormalizedMin = 0.5;
static const CGFloat POPBouncy3SpeedNormalizedMax = 200;
static const CGFloat POPBouncy3FrictionInterpolationMax = 0.01;

+ (void)convertBounciness:(CGFloat)bounciness speed:(CGFloat)speed toTension:(CGFloat *)outTension friction:(CGFloat *)outFriction mass:(CGFloat *)outMass
{
  double b = POPNormalize(bounciness / POPBouncy3NormalizationScale, 0, POPBouncy3NormalizationRange);
  b = POPProjectNormal(b, POPBouncy3BouncinessNormalizedMin, POPBouncy3BouncinessNormalizedMax);

  double s = POPNormalize(speed / POPBouncy3NormalizationScale, 0, POPBouncy3NormalizationRange);

  CGFloat tension = POPProjectNormal(s, POPBouncy3SpeedNormalizedMin, POPBouncy3SpeedNormalizedMax);
  CGFloat friction = POPQuadraticOutInterpolation(b, POPBouncy3NoBounce(tension), POPBouncy3FrictionInterpolationMax);

  tension = POP_ANIMATION_TENSION_FOR_QC_TENSION(tension);
  friction = POP_ANIMATION_FRICTION_FOR_QC_FRICTION(friction);

  if (outTension) {
    *outTension = tension;
  }

  if (outFriction) {
    *outFriction = friction;
  }

  if (outMass) {
    *outMass = 1.0;
  }
}

+ (void)convertTension:(CGFloat)tension friction:(CGFloat)friction toBounciness:(CGFloat *)outBounciness speed:(CGFloat *)outSpeed
{
  // Convert to QC values, in which our calculations are done.
  CGFloat qcFriction = QC_FRICTION_FOR_POP_ANIMATION_FRICTION(friction);
  CGFloat qcTension = QC_TENSION_FOR_POP_ANIMATION_TENSION(tension);

  // Friction is a function of bounciness and tension, according to the following:
  // friction = POPQuadraticOutInterpolation(b, POPBouncy3NoBounce(tension), POPBouncy3FrictionInterpolationMax);
  // Solve for bounciness, given a tension and friction.

  CGFloat nobounceTension = POPBouncy3NoBounce(qcTension);
  CGFloat bounciness1, bounciness2;

  POPQuadraticSolve((nobounceTension - POPBouncy3FrictionInterpolationMax),      // a
                  2 * (POPBouncy3FrictionInterpolationMax - nobounceTension),  // b
                  (nobounceTension - qcFriction),                             // c
                  bounciness1,                                                // x1
                  bounciness2);                                               // x2


  // Choose the quadratic solution within the normalized bounciness range
  CGFloat projectedNormalizedBounciness = (bounciness2 < POPBouncy3BouncinessNormalizedMax) ? bounciness2 : bounciness1;
  CGFloat projectedNormalizedSpeed = qcTension;

  // Reverse projection + normalization
  CGFloat bounciness = ((POPBouncy3NormalizationRange * POPBouncy3NormalizationScale) / (POPBouncy3BouncinessNormalizedMax - POPBouncy3BouncinessNormalizedMin)) * (projectedNormalizedBounciness - POPBouncy3BouncinessNormalizedMin);
  CGFloat speed = ((POPBouncy3NormalizationRange * POPBouncy3NormalizationScale) / (POPBouncy3SpeedNormalizedMax - POPBouncy3SpeedNormalizedMin)) * (projectedNormalizedSpeed - POPBouncy3SpeedNormalizedMin);

  // Write back results
  if (outBounciness) {
    *outBounciness = bounciness;
  }

  if (outSpeed) {
    *outSpeed = speed;
  }
}

@end


================================================
FILE: pop/POPAnimationInternal.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPAnimation.h"

#import <QuartzCore/CAMediaTimingFunction.h>

#import "POPAction.h"
#import "POPAnimationRuntime.h"
#import "POPAnimationTracerInternal.h"
#import "POPMath.h"
#import "POPSpringSolver.h"

using namespace POP;

/**
 Enumeration of supported animation types.
 */
enum POPAnimationType
{
  kPOPAnimationSpring,
  kPOPAnimationDecay,
  kPOPAnimationBasic,
  kPOPAnimationCustom,
};

typedef struct
{
  CGFloat progress;
  bool reached;
} POPProgressMarker;

typedef void (^POPAnimationDidStartBlock)(POPAnimation *anim);
typedef void (^POPAnimationDidReachToValueBlock)(POPAnimation *anim);
typedef void (^POPAnimationCompletionBlock)(POPAnimation *anim, BOOL finished);
typedef void (^POPAnimationDidApplyBlock)(POPAnimation *anim);

@interface POPAnimation()
- (instancetype)_init;

@property (assign, nonatomic) SpringSolver4d *solver;
@property (readonly, nonatomic) POPAnimationType type;

/**
 The current animation value, updated while animation is progressing.
 */
@property (copy, nonatomic, readonly) id currentValue;

/**
 An array of optional progress markers. For each marker specified, the animation delegate will be informed when progress meets or exceeds the value specified. Specifying values outside of the [0, 1] range will give undefined results.
 */
@property (copy, nonatomic) NSArray *progressMarkers;

/**
 Return YES to indicate animation should continue animating.
 */
- (BOOL)_advance:(id)object currentTime:(CFTimeInterval)currentTime elapsedTime:(CFTimeInterval)elapsedTime;

/**
 Subclass override point to append animation description.
 */
- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug;

@end

NS_INLINE NSString *describe(VectorConstRef vec)
{
  return NULL == vec ? @"null" : vec->toString();
}

NS_INLINE Vector4r vector4(VectorConstRef vec)
{
  return NULL == vec ? Vector4r::Zero() : vec->vector4r();
}

NS_INLINE Vector4d vector4d(VectorConstRef vec)
{
  if (NULL == vec) {
    return Vector4d::Zero();
  } else {
    return vec->vector4r().cast<double>();
  }
}

NS_INLINE bool vec_equal(VectorConstRef v1, VectorConstRef v2)
{
  if (v1 == v2) {
    return true;
  }
  if (!v1 || !v2) {
    return false;
  }
  return *v1 == *v2;
}

NS_INLINE CGFloat * vec_data(VectorRef vec)
{
  return NULL == vec ? NULL : vec->data();
}

template<class T>
struct ComputeProgressFunctor {
  CGFloat operator()(const T &value, const T &start, const T &end) const {
    return 0;
  }
};

template<>
struct ComputeProgressFunctor<Vector4r> {
  CGFloat operator()(const Vector4r &value, const Vector4r &start, const Vector4r &end) const {
    CGFloat s = (value - start).squaredNorm(); // distance from start
    CGFloat e = (value - end).squaredNorm();   // distance from end
    CGFloat d = (end - start).squaredNorm();   // distance from start to end

    if (0 == d) {
      return 1;
    } else if (s > e) {
      // s -------- p ---- e   OR   s ------- e ---- p
      return sqrtr(s/d);
    } else {
      // s --- p --------- e   OR   p ---- s ------- e
      return 1 - sqrtr(e/d);
    }
  }
};

struct _POPAnimationState;
struct _POPDecayAnimationState;
struct _POPPropertyAnimationState;

extern _POPAnimationState *POPAnimationGetState(POPAnimation *a);


#define FB_FLAG_GET(stype, flag, getter) \
- (BOOL)getter { \
  return ((stype *)_state)->flag; \
}

#define FB_FLAG_SET(stype, flag, mutator) \
- (void)mutator (BOOL)value { \
  if (value == ((stype *)_state)->flag) \
    return; \
  ((stype *)_state)->flag = value; \
}

#define DEFINE_RW_FLAG(stype, flag, getter, mutator) \
  FB_FLAG_GET (stype, flag, getter) \
  FB_FLAG_SET (stype, flag, mutator)

#define FB_PROPERTY_GET(stype, property, ctype) \
- (ctype)property { \
  return ((stype *)_state)->property; \
}

#define FB_PROPERTY_SET(stype, property, mutator, ctype, ...) \
- (void)mutator (ctype)value { \
  if (value == ((stype *)_state)->property) \
    return; \
  ((stype *)_state)->property = value; \
  __VA_ARGS__ \
}

#define FB_PROPERTY_SET_OBJ_COPY(stype, property, mutator, ctype, ...) \
- (void)mutator (ctype)value { \
  if (value == ((stype *)_state)->property) \
    return; \
  ((stype *)_state)->property = [value copy]; \
  __VA_ARGS__ \
}

#define DEFINE_RW_PROPERTY(stype, flag, mutator, ctype, ...) \
  FB_PROPERTY_GET (stype, flag, ctype) \
  FB_PROPERTY_SET (stype, flag, mutator, ctype, __VA_ARGS__)

#define DEFINE_RW_PROPERTY_OBJ(stype, flag, mutator, ctype, ...) \
  FB_PROPERTY_GET (stype, flag, ctype) \
  FB_PROPERTY_SET (stype, flag, mutator, ctype, __VA_ARGS__)

#define DEFINE_RW_PROPERTY_OBJ_COPY(stype, flag, mutator, ctype, ...) \
  FB_PROPERTY_GET (stype, flag, ctype) \
  FB_PROPERTY_SET_OBJ_COPY (stype, flag, mutator, ctype, __VA_ARGS__)


/**
 Internal delegate definition.
 */
@interface NSObject (POPAnimationDelegateInternal)
- (void)pop_animation:(POPAnimation *)anim didReachProgress:(CGFloat)progress;
@end

struct _POPAnimationState
{
  id __unsafe_unretained self;
  POPAnimationType type;
  NSString *name;
  NSUInteger ID;
  CFTimeInterval beginTime;
  CFTimeInterval startTime;
  CFTimeInterval lastTime;
  id __weak delegate;
  POPAnimationDidStartBlock animationDidStartBlock;
  POPAnimationDidReachToValueBlock animationDidReachToValueBlock;
  POPAnimationCompletionBlock completionBlock;
  POPAnimationDidApplyBlock animationDidApplyBlock;
  NSMutableDictionary *dict;
  POPAnimationTracer *tracer;
  CGFloat progress;
  NSInteger repeatCount;

  bool active:1;
  bool paused:1;
  bool removedOnCompletion:1;

  bool delegateDidStart:1;
  bool delegateDidStop:1;
  bool delegateDidProgress:1;
  bool delegateDidApply:1;
  bool delegateDidReachToValue:1;

  bool additive:1;
  bool didReachToValue:1;
  bool tracing:1; // corresponds to tracer started
  bool userSpecifiedDynamics:1;
  bool autoreverses:1;
  bool repeatForever:1;
  bool customFinished:1;

  _POPAnimationState(id __unsafe_unretained anim) :
  self(anim),
  type((POPAnimationType)0),
  name(nil),
  ID(0),
  beginTime(0),
  startTime(0),
  lastTime(0),
  delegate(nil),
  animationDidStartBlock(nil),
  animationDidReachToValueBlock(nil),
  completionBlock(nil),
  animationDidApplyBlock(nil),
  dict(nil),
  tracer(nil),
  progress(0),
  repeatCount(0),
  active(false),
  paused(true),
  removedOnCompletion(true),
  delegateDidStart(false),
  delegateDidStop(false),
  delegateDidProgress(false),
  delegateDidApply(false),
  delegateDidReachToValue(false),
  additive(false),
  didReachToValue(false),
  tracing(false),
  userSpecifiedDynamics(false),
  autoreverses(false),
  repeatForever(false),
  customFinished(false) {}

  virtual ~_POPAnimationState()
  {
    name = nil;
    dict = nil;
    tracer = nil;
    animationDidStartBlock = NULL;
    animationDidReachToValueBlock = NULL;
    completionBlock = NULL;
    animationDidApplyBlock = NULL;
  }

  bool isCustom() {
    return kPOPAnimationCustom == type;
  }

  bool isStarted() {
    return 0 != startTime;
  }

  id getDelegate() {
    return delegate;
  }

  void setDelegate(id d) {
    if (d != delegate) {
      delegate = d;
      delegateDidStart = [d respondsToSelector:@selector(pop_animationDidStart:)];
      delegateDidStop = [d respondsToSelector:@selector(pop_animationDidStop:finished:)];
      delegateDidProgress = [d respondsToSelector:@selector(pop_animation:didReachProgress:)];
      delegateDidApply = [d respondsToSelector:@selector(pop_animationDidApply:)];
      delegateDidReachToValue = [d respondsToSelector:@selector(pop_animationDidReachToValue:)];
    }
  }

  bool getPaused() {
    return paused;
  }

  void setPaused(bool f) {
    if (f != paused) {
      paused = f;
      if (!paused) {
        reset(false);
      }
    }
  }

  CGFloat getProgress() {
    return progress;
  }

  /* returns true if started */
  bool startIfNeeded(id obj, CFTimeInterval time, CFTimeInterval offset)
  {
    bool started = false;

    // detect start based on time
    if (0 == startTime && time >= beginTime + offset) {

      // activate & unpause
      active = true;
      setPaused(false);

      // note start time
      startTime = lastTime = time;
      started = true;
    }

    // ensure values for running animation
    bool running = active && !paused;
    if (running) {
      willRun(started, obj);
    }

    // handle start
    if (started) {
      handleDidStart();
    }

    return started;
  }

  void stop(bool removing, bool done) {
    if (active)
    {
      // delegate progress one last time
      if (done) {
        delegateProgress();
      }

      if (removing) {
        active = false;
      }

      handleDidStop(done);
    } else {

      // stopped before even started
      // delegate start and stop regardless; matches CA behavior
      if (!isStarted()) {
        handleDidStart();
        handleDidStop(false);
      }
    }

    setPaused(true);
  }

  virtual void handleDidStart()
  {
    if (delegateDidStart) {
      ActionEnabler enabler;
      [delegate pop_animationDidStart:self];
    }

    POPAnimationDidStartBlock block = animationDidStartBlock;
    if (block != NULL) {
      ActionEnabler enabler;
      block(self);
    }

    if (tracing) {
      [tracer didStart];
    }
  }

  void handleDidStop(BOOL done)
  {
    if (delegateDidStop) {
      ActionEnabler enabler;
      [delegate pop_animationDidStop:self finished:done];
    }

    // add another strong reference to completion block before callout
    POPAnimationCompletionBlock block = completionBlock;
    if (block != NULL) {
      ActionEnabler enabler;
      block(self, done);
    }

    if (tracing) {
      [tracer didStop:done];
    }
  }

  /* virtual functions */
  virtual bool isDone() {
    if (isCustom()) {
      return customFinished;
    }

    return false;
  }

  bool advanceTime(CFTimeInterval time, id obj) {
    bool advanced = false;
    bool computedProgress = false;
    CFTimeInterval dt = time - lastTime;

    switch (type) {
      case kPOPAnimationSpring:
        advanced = advance(time, dt, obj);
        break;
      case kPOPAnimationDecay:
        advanced = advance(time, dt, obj);
        break;
      case kPOPAnimationBasic: {
        advanced = advance(time, dt, obj);
        computedProgress = true;
        break;
      }
      case kPOPAnimationCustom: {
        customFinished = [self _advance:obj currentTime:time elapsedTime:dt] ? false : true;
        advanced = true;
        break;
      }
      default:
        break;
    }

    if (advanced) {

      // estimate progress
      if (!computedProgress) {
        computeProgress();
      }

      // delegate progress
      delegateProgress();

      // update time
      lastTime = time;
    }

    return advanced;
  }

  virtual void willRun(bool started, id obj) {}
  virtual bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) { return false; }
  virtual void computeProgress() {}
  virtual void delegateProgress() {}

  virtual void delegateApply() {
    if (delegateDidApply) {
      ActionEnabler enabler;
      [delegate pop_animationDidApply:self];
    }

    POPAnimationDidApplyBlock block = animationDidApplyBlock;
    if (block != NULL) {
      ActionEnabler enabler;
      block(self);
    }
  }

  virtual void reset(bool all) {
    startTime = 0;
    lastTime = 0;
  }
};

typedef struct _POPAnimationState POPAnimationState;


@interface POPAnimation ()
{
@protected
  struct _POPAnimationState *_state;
}

@end

// NSProxy extensions, for testing purposes
@interface NSProxy (POP)
- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key;
- (void)pop_removeAllAnimations;
- (void)pop_removeAnimationForKey:(NSString *)key;
- (NSArray *)pop_animationKeys;
- (POPAnimation *)pop_animationForKey:(NSString *)key;
@end


================================================
FILE: pop/POPAnimationPrivate.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <pop/POPAnimation.h>

#define POP_ANIMATION_FRICTION_FOR_QC_FRICTION(qcFriction) (25.0 + (((qcFriction - 8.0) / 2.0) * (25.0 - 19.0)))
#define POP_ANIMATION_TENSION_FOR_QC_TENSION(qcTension) (194.0 + (((qcTension - 30.0) / 50.0) * (375.0 - 194.0)))

#define QC_FRICTION_FOR_POP_ANIMATION_FRICTION(fbFriction) (8.0 + 2.0 * ((fbFriction - 25.0)/(25.0 - 19.0)))
#define QC_TENSION_FOR_POP_ANIMATION_TENSION(fbTension) (30.0 + 50.0 * ((fbTension - 194.0)/(375.0 - 194.0)))


================================================
FILE: pop/POPAnimationRuntime.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <objc/runtime.h>

#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>

#import "POPAnimatablePropertyTypes.h"
#import "POPVector.h"

enum POPValueType
{
  kPOPValueUnknown = 0,
  kPOPValueInteger,
  kPOPValueFloat,
  kPOPValuePoint,
  kPOPValueSize,
  kPOPValueRect,
  kPOPValueEdgeInsets,
  kPOPValueAffineTransform,
  kPOPValueTransform,
  kPOPValueRange,
  kPOPValueColor,
  kPOPValueSCNVector3,
  kPOPValueSCNVector4,
};

using namespace POP;

/**
 Returns value type based on objc type description, given list of supported value types and length.
 */
extern POPValueType POPSelectValueType(const char *objctype, const POPValueType *types, size_t length);

/**
 Returns value type based on objc object, given a list of supported value types and length.
 */
extern POPValueType POPSelectValueType(id obj, const POPValueType *types, size_t length);

/**
 Array of all value types.
 */
extern const POPValueType kPOPAnimatableAllTypes[12];

/**
 Array of all value types supported for animation.
 */
extern const POPValueType kPOPAnimatableSupportTypes[10];

/**
 Returns a string description of a value type.
 */
extern NSString *POPValueTypeToString(POPValueType t);

/**
 Returns a mutable dictionary of weak pointer keys to weak pointer values.
 */
extern CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToWeakPointer(NSUInteger capacity) CF_RETURNS_RETAINED;

/**
 Returns a mutable dictionary of weak pointer keys to weak pointer values.
 */
extern CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToStrongObject(NSUInteger capacity) CF_RETURNS_RETAINED;

/**
 Box a vector.
 */
extern id POPBox(VectorConstRef vec, POPValueType type, bool force = false);

/**
 Unbox a vector.
 */
extern VectorRef POPUnbox(id value, POPValueType &type, NSUInteger &count, bool validate);

/**
 Read object value and return a Vector4r.
 */
NS_INLINE Vector4r read_values(POPAnimatablePropertyReadBlock read, id obj, size_t count)
{
  Vector4r vec = Vector4r::Zero();
  if (0 == count)
    return vec;

  read(obj, vec.data());

  return vec;
}

NS_INLINE NSString *POPStringFromBOOL(BOOL value)
{
  return value ? @"YES" : @"NO";
}


================================================
FILE: pop/POPAnimationRuntime.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPAnimationRuntime.h"

#import <objc/objc.h>

#import <QuartzCore/QuartzCore.h>

#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#endif

#import "POPCGUtils.h"
#import "POPDefines.h"
#import "POPGeometry.h"
#import "POPVector.h"

static Boolean pointerEqual(const void *ptr1, const void *ptr2) {
  return ptr1 == ptr2;
}

static CFHashCode pointerHash(const void *ptr) {
  return (CFHashCode)(ptr);
}

CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToWeakPointer(NSUInteger capacity)
{
  CFDictionaryKeyCallBacks kcb = kCFTypeDictionaryKeyCallBacks;

  // weak, pointer keys
  kcb.retain = NULL;
  kcb.release = NULL;
  kcb.equal = pointerEqual;
  kcb.hash = pointerHash;

  CFDictionaryValueCallBacks vcb = kCFTypeDictionaryValueCallBacks;

  // weak, pointer values
  vcb.retain = NULL;
  vcb.release = NULL;
  vcb.equal = pointerEqual;

  return CFDictionaryCreateMutable(NULL, capacity, &kcb, &vcb);
}

CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToStrongObject(NSUInteger capacity)
{
  CFDictionaryKeyCallBacks kcb = kCFTypeDictionaryKeyCallBacks;

  // weak, pointer keys
  kcb.retain = NULL;
  kcb.release = NULL;
  kcb.equal = pointerEqual;
  kcb.hash = pointerHash;

  // strong, object values
  CFDictionaryValueCallBacks vcb = kCFTypeDictionaryValueCallBacks;

  return CFDictionaryCreateMutable(NULL, capacity, &kcb, &vcb);
}

static bool FBCompareTypeEncoding(const char *objctype, POPValueType type)
{
  switch (type)
  {
    case kPOPValueFloat:
      return (strcmp(objctype, @encode(float)) == 0
              || strcmp(objctype, @encode(double)) == 0
              );

    case kPOPValuePoint:
      return (strcmp(objctype, @encode(CGPoint)) == 0
#if !TARGET_OS_IPHONE
              || strcmp(objctype, @encode(NSPoint)) == 0
#endif
              );

    case kPOPValueSize:
      return (strcmp(objctype, @encode(CGSize)) == 0
#if !TARGET_OS_IPHONE
              || strcmp(objctype, @encode(NSSize)) == 0
#endif
              );

    case kPOPValueRect:
      return (strcmp(objctype, @encode(CGRect)) == 0
#if !TARGET_OS_IPHONE
              || strcmp(objctype, @encode(NSRect)) == 0
#endif
              );
    case kPOPValueEdgeInsets:
#if TARGET_OS_IPHONE
      return strcmp(objctype, @encode(UIEdgeInsets)) == 0;
#else
      return false;
#endif
      
    case kPOPValueAffineTransform:
      return strcmp(objctype, @encode(CGAffineTransform)) == 0;

    case kPOPValueTransform:
      return strcmp(objctype, @encode(CATransform3D)) == 0;

    case kPOPValueRange:
      return strcmp(objctype, @encode(CFRange)) == 0
      || strcmp(objctype, @encode (NSRange)) == 0;

    case kPOPValueInteger:
      return (strcmp(objctype, @encode(int)) == 0
              || strcmp(objctype, @encode(unsigned int)) == 0
              || strcmp(objctype, @encode(short)) == 0
              || strcmp(objctype, @encode(unsigned short)) == 0
              || strcmp(objctype, @encode(long)) == 0
              || strcmp(objctype, @encode(unsigned long)) == 0
              || strcmp(objctype, @encode(long long)) == 0
              || strcmp(objctype, @encode(unsigned long long)) == 0
              );
      
    case kPOPValueSCNVector3:
#if SCENEKIT_SDK_AVAILABLE
      return strcmp(objctype, @encode(SCNVector3)) == 0;
#else
      return false;
#endif
      
    case kPOPValueSCNVector4:
#if SCENEKIT_SDK_AVAILABLE
      return strcmp(objctype, @encode(SCNVector4)) == 0;
#else
      return false;
#endif
      
    default:
      return false;
  }
}

POPValueType POPSelectValueType(const char *objctype, const POPValueType *types, size_t length)
{
  if (NULL != objctype) {
    for (size_t idx = 0; idx < length; idx++) {
      if (FBCompareTypeEncoding(objctype, types[idx]))
        return types[idx];
    }
  }
  return kPOPValueUnknown;
}

POPValueType POPSelectValueType(id obj, const POPValueType *types, size_t length)
{
  if ([obj isKindOfClass:[NSValue class]]) {
    return POPSelectValueType([obj objCType], types, length);
  } else if (NULL != POPCGColorWithColor(obj)) {
    return kPOPValueColor;
  }
  return kPOPValueUnknown;
}

const POPValueType kPOPAnimatableAllTypes[12] = {kPOPValueInteger, kPOPValueFloat, kPOPValuePoint, kPOPValueSize, kPOPValueRect, kPOPValueEdgeInsets, kPOPValueAffineTransform, kPOPValueTransform, kPOPValueRange, kPOPValueColor, kPOPValueSCNVector3, kPOPValueSCNVector4};

const POPValueType kPOPAnimatableSupportTypes[10] = {kPOPValueInteger, kPOPValueFloat, kPOPValuePoint, kPOPValueSize, kPOPValueRect, kPOPValueEdgeInsets, kPOPValueColor, kPOPValueSCNVector3, kPOPValueSCNVector4};

NSString *POPValueTypeToString(POPValueType t)
{
  switch (t) {
    case kPOPValueUnknown:
      return @"unknown";
    case kPOPValueInteger:
      return @"int";
    case kPOPValueFloat:
      return @"CGFloat";
    case kPOPValuePoint:
      return @"CGPoint";
    case kPOPValueSize:
      return @"CGSize";
    case kPOPValueRect:
      return @"CGRect";
    case kPOPValueEdgeInsets:
      return @"UIEdgeInsets";
    case kPOPValueAffineTransform:
      return @"CGAffineTransform";
    case kPOPValueTransform:
      return @"CATransform3D";
    case kPOPValueRange:
      return @"CFRange";
    case kPOPValueColor:
      return @"CGColorRef";
    case kPOPValueSCNVector3:
      return @"SCNVector3";
    case kPOPValueSCNVector4:
      return @"SCNVector4";
    default:
      return nil;
  }
}

id POPBox(VectorConstRef vec, POPValueType type, bool force)
{
  if (NULL == vec)
    return nil;
  
  switch (type) {
    case kPOPValueInteger:
    case kPOPValueFloat:
      return @(vec->data()[0]);
      break;
    case kPOPValuePoint:
      return [NSValue valueWithCGPoint:vec->cg_point()];
      break;
    case kPOPValueSize:
      return [NSValue valueWithCGSize:vec->cg_size()];
      break;
    case kPOPValueRect:
      return [NSValue valueWithCGRect:vec->cg_rect()];
      break;
#if TARGET_OS_IPHONE
    case kPOPValueEdgeInsets:
      return [NSValue valueWithUIEdgeInsets:vec->ui_edge_insets()];
      break;
#endif
    case kPOPValueColor: {
      return (__bridge_transfer id)vec->cg_color();
      break;
    }
#if SCENEKIT_SDK_AVAILABLE
    case kPOPValueSCNVector3: {
      return [NSValue valueWithSCNVector3:vec->scn_vector3()];
      break;
    }
    case kPOPValueSCNVector4: {
      return [NSValue valueWithSCNVector4:vec->scn_vector4()];
      break;
    }
#endif
    default:
      return force ? [NSValue valueWithCGPoint:vec->cg_point()] : nil;
      break;
  }
}

static VectorRef vectorize(id value, POPValueType type)
{
  Vector *vec = NULL;

  switch (type) {
    case kPOPValueInteger:
    case kPOPValueFloat:
#if CGFLOAT_IS_DOUBLE
      vec = Vector::new_cg_float([value doubleValue]);
#else
      vec = Vector::new_cg_float([value floatValue]);
#endif
      break;
    case kPOPValuePoint:
      vec = Vector::new_cg_point([value CGPointValue]);
      break;
    case kPOPValueSize:
      vec = Vector::new_cg_size([value CGSizeValue]);
      break;
    case kPOPValueRect:
      vec = Vector::new_cg_rect([value CGRectValue]);
      break;
#if TARGET_OS_IPHONE
    case kPOPValueEdgeInsets:
      vec = Vector::new_ui_edge_insets([value UIEdgeInsetsValue]);
      break;
#endif
    case kPOPValueAffineTransform:
      vec = Vector::new_cg_affine_transform([value CGAffineTransformValue]);
      break;
    case kPOPValueColor:
      vec = Vector::new_cg_color(POPCGColorWithColor(value));
      break;
#if SCENEKIT_SDK_AVAILABLE
    case kPOPValueSCNVector3:
      vec = Vector::new_scn_vector3([value SCNVector3Value]);
      break;
    case kPOPValueSCNVector4:
      vec = Vector::new_scn_vector4([value SCNVector4Value]);
      break;
#endif
    default:
      break;
  }
  
  return VectorRef(vec);
}

VectorRef POPUnbox(id value, POPValueType &animationType, NSUInteger &count, bool validate)
{
  if (nil == value) {
    count = 0;
    return VectorRef(NULL);
  }

  // determine type of value
  POPValueType valueType = POPSelectValueType(value, kPOPAnimatableSupportTypes, POP_ARRAY_COUNT(kPOPAnimatableSupportTypes));

  // handle unknown types
  if (kPOPValueUnknown == valueType) {
    NSString *valueDesc = [[value class] description];
    [NSException raise:@"Unsuported value" format:@"Animating %@ values is not supported", valueDesc];
  }

  // vectorize
  VectorRef vec = vectorize(value, valueType);

  if (kPOPValueUnknown == animationType || 0 == count) {
    // update animation type based on value type
    animationType = valueType;
    if (NULL != vec) {
      count = vec->size();
    }
  } else if (validate) {
    // allow for mismatched types, so long as vector size matches
    if (count != vec->size()) {
      [NSException raise:@"Invalid value" format:@"%@ should be of type %@", value, POPValueTypeToString(animationType)];
    }
  }
  
  return vec;
}


================================================
FILE: pop/POPAnimationTracer.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <Foundation/Foundation.h>

#import <pop/POPAnimationEvent.h>

@class POPAnimation;

/**
 @abstract Tracer of animation events to facilitate unit testing & debugging.
 */
@interface POPAnimationTracer : NSObject

/**
 @abstract Start recording events.
 */
- (void)start;

/**
 @abstract Stop recording events.
 */
- (void)stop;

/**
 @abstract Resets any recoded events. Continues recording events if already started.
 */
- (void)reset;

/**
 @abstract Property representing all recorded events.
 @discussion Events are returned in order of occurrence.
 */
@property (nonatomic, assign, readonly) NSArray *allEvents;

/**
 @abstract Property representing all recorded write events for convenience.
 @discussion Events are returned in order of occurrence.
 */
@property (nonatomic, assign, readonly) NSArray *writeEvents;

/**
 @abstract Queries for events of specified type.
 @param type The type of event to return.
 @returns An array of events of specified type in order of occurrence.
 */
- (NSArray *)eventsWithType:(POPAnimationEventType)type;

/**
 @abstract Property indicating whether tracer should automatically log events and reset collection on animation completion.
 */
@property (nonatomic, assign) BOOL shouldLogAndResetOnCompletion;

@end


================================================
FILE: pop/POPAnimationTracer.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPAnimationTracer.h"

#import <QuartzCore/QuartzCore.h>

#import "POPAnimationEventInternal.h"
#import "POPAnimationInternal.h"
#import "POPSpringAnimation.h"

@implementation POPAnimationTracer
{
  __weak POPAnimation *_animation;
  POPAnimationState *_animationState;
  NSMutableArray *_events;
  BOOL _animationHasVelocity;
}
@synthesize shouldLogAndResetOnCompletion = _shouldLogAndResetOnCompletion;

static POPAnimationEvent *create_event(POPAnimationTracer *self, POPAnimationEventType type, id value = nil, bool recordAnimation = false)
{
  bool useLocalTime = 0 != self->_animationState->startTime;
  CFTimeInterval time = useLocalTime
    ? self->_animationState->lastTime - self->_animationState->startTime
    : self->_animationState->lastTime;

  POPAnimationEvent *event;
  __strong POPAnimation* animation = self->_animation;

  if (!value) {
    event = [[POPAnimationEvent alloc] initWithType:type time:time];
  } else {
    event = [[POPAnimationValueEvent alloc] initWithType:type time:time value:value];
    if (self->_animationHasVelocity) {
      [(POPAnimationValueEvent *)event setVelocity:[(POPSpringAnimation *)animation velocity]];
    }
  }

  if (recordAnimation) {
    event.animationDescription = [animation description];
  }

  return event;
}

- (id)initWithAnimation:(POPAnimation *)anAnim
{
  self = [super init];
  if (nil != self) {
    _animation = anAnim;
    _animationState = POPAnimationGetState(anAnim);
    _events = [[NSMutableArray alloc] initWithCapacity:50];
    _animationHasVelocity = [anAnim respondsToSelector:@selector(velocity)];
  }
  return self;
}

- (void)readPropertyValue:(id)aValue
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventPropertyRead, aValue);
  [_events addObject:event];
}

- (void)writePropertyValue:(id)aValue
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventPropertyWrite, aValue);
  [_events addObject:event];
}

- (void)updateToValue:(id)aValue
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventToValueUpdate, aValue);
  [_events addObject:event];
}

- (void)updateFromValue:(id)aValue
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventFromValueUpdate, aValue);
  [_events addObject:event];
}

- (void)updateVelocity:(id)aValue
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventVelocityUpdate, aValue);
  [_events addObject:event];
}

- (void)updateSpeed:(float)aFloat
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventSpeedUpdate, @(aFloat));
  [_events addObject:event];
}

- (void)updateBounciness:(float)aFloat
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventBouncinessUpdate, @(aFloat));
  [_events addObject:event];
}

- (void)updateFriction:(float)aFloat
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventFrictionUpdate, @(aFloat));
  [_events addObject:event];
}

- (void)updateMass:(float)aFloat
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventMassUpdate, @(aFloat));
  [_events addObject:event];
}

- (void)updateTension:(float)aFloat
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventTensionUpdate, @(aFloat));
  [_events addObject:event];
}

- (void)didStart
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventDidStart, nil, true);
  [_events addObject:event];
}

- (void)didStop:(BOOL)finished
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventDidStop, @(finished), true);
  [_events addObject:event];

  if (_shouldLogAndResetOnCompletion) {
    NSLog(@"events:%@", self.allEvents);
    [self reset];
  }
}

- (void)didReachToValue:(id)aValue
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventDidReachToValue, aValue);
  [_events addObject:event];
}

- (void)autoreversed
{
  POPAnimationEvent *event = create_event(self, kPOPAnimationEventAutoreversed);
  [_events addObject:event];
}

- (void)start
{
  POPAnimationState *s = POPAnimationGetState(_animation);
  s->tracing = true;
}

- (void)stop
{
  POPAnimationState *s = POPAnimationGetState(_animation);
  s->tracing = false;
}

- (void)reset
{
  [_events removeAllObjects];
}

- (NSArray *)allEvents
{
  return [_events copy];
}

- (NSArray *)writeEvents
{
  return [self eventsWithType:kPOPAnimationEventPropertyWrite];
}

- (NSArray *)eventsWithType:(POPAnimationEventType)aType
{
  NSMutableArray *array = [NSMutableArray array];
  for (POPAnimationEvent *event in _events) {
    if (aType == event.type) {
      [array addObject:event];
    }
  }
  return array;
}

@end


================================================
FILE: pop/POPAnimationTracerInternal.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <Foundation/Foundation.h>

#import <pop/POPAnimationTracer.h>

@interface POPAnimationTracer (Internal)

/**
 @abstract Designated initializer. Pass the animation being traced.
 */
- (instancetype)initWithAnimation:(POPAnimation *)anAnim;

/**
 @abstract Records read value.
 */
- (void)readPropertyValue:(id)aValue;

/**
 @abstract Records write value.
 */
- (void)writePropertyValue:(id)aValue;

/**
 Records to value update.
 */
- (void)updateToValue:(id)aValue;

/**
 @abstract Records from value update.
 */
- (void)updateFromValue:(id)aValue;

/**
 @abstract Records from value update.
 */
- (void)updateVelocity:(id)aValue;

/**
 @abstract Records bounciness update.
 */
- (void)updateBounciness:(float)aFloat;

/**
 @abstract Records speed update.
 */
- (void)updateSpeed:(float)aFloat;

/**
 @abstract Records friction update.
 */
- (void)updateFriction:(float)aFloat;

/**
 @abstract Records mass update.
 */
- (void)updateMass:(float)aFloat;

/**
 @abstract Records tension update.
 */
- (void)updateTension:(float)aFloat;

/**
 @abstract Records did add.
 */
- (void)didAdd;

/**
 @abstract Records did start.
 */
- (void)didStart;

/**
 @abstract Records did stop.
 */
- (void)didStop:(BOOL)finished;

/**
 @abstract Records did reach to value.
 */
- (void)didReachToValue:(id)aValue;

/**
 @abstract Records when an autoreverse animation takes place.
 */
- (void)autoreversed;

@end


================================================
FILE: pop/POPAnimator.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <Foundation/Foundation.h>

@protocol POPAnimatorDelegate;

/**
 @abstract The animator class renders animations.
 */
@interface POPAnimator : NSObject

/**
 @abstract The shared animator instance.
 @discussion Consumers should generally use the shared instance in lieu of creating new instances.
 */
+ (instancetype)sharedAnimator;

#if !TARGET_OS_IPHONE
/**
 @abstract Allows to select display to bind. Returns nil if failed to create the display link.
 */
- (instancetype)initWithDisplayID:(CGDirectDisplayID)displayID;
#endif

/**
 @abstract The optional animator delegate.
 */
@property (weak, nonatomic) id<POPAnimatorDelegate> delegate;

/**
 @abstract Retrieves the nominal refresh period of a display link. Returns zero if unavailable.
 */
@property (readonly, nonatomic) CFTimeInterval refreshPeriod;

@end

/**
 @abstract The animator delegate.
 */
@protocol POPAnimatorDelegate <NSObject>

/**
 @abstract Called on each frame before animation application.
 */
- (void)animatorWillAnimate:(POPAnimator *)animator;

/**
 @abstract Called on each frame after animation application.
 */
- (void)animatorDidAnimate:(POPAnimator *)animator;

@end


================================================
FILE: pop/POPAnimator.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPAnimator.h"
#import "POPAnimatorPrivate.h"

#import <list>
#import <vector>

#if !TARGET_OS_IPHONE
#import <libkern/OSAtomic.h>
#endif

#import <objc/objc-auto.h>

#import <QuartzCore/QuartzCore.h>

#import "POPAnimation.h"
#import "POPAnimationExtras.h"
#import "POPBasicAnimationInternal.h"
#import "POPDecayAnimation.h"

using namespace std;
using namespace POP;

#define ENABLE_LOGGING_DEBUG 0
#define ENABLE_LOGGING_INFO 0

#if ENABLE_LOGGING_DEBUG
#define FBLogAnimDebug NSLog
#else
#define FBLogAnimDebug(...)
#endif

#if ENABLE_LOGGING_INFO
#define FBLogAnimInfo NSLog
#else
#define FBLogAnimInfo(...)
#endif

#if !TARGET_OS_IPHONE
static const uint64_t kDisplayTimerFrequency = 60ull; // Hz
#endif

class POPAnimatorItem
{
public:
  id __weak object;
  NSString *key;
  POPAnimation *animation;
  NSInteger refCount;
  id __unsafe_unretained unretainedObject;

  POPAnimatorItem(id o, NSString *k, POPAnimation *a) POP_NOTHROW
  {
    object = o;
    key = [k copy];
    animation = a;
    refCount = 1;
    unretainedObject = o;
  }

  ~POPAnimatorItem()
  {
  }

  bool operator==(const POPAnimatorItem& o) const {
    return unretainedObject == o.unretainedObject && animation == o.animation && [key isEqualToString:o.key];
  }

};

typedef std::shared_ptr<POPAnimatorItem> POPAnimatorItemRef;
typedef std::shared_ptr<const POPAnimatorItem> POPAnimatorItemConstRef;

typedef std::list<POPAnimatorItemRef> POPAnimatorItemList;
typedef POPAnimatorItemList::iterator POPAnimatorItemListIterator;
typedef POPAnimatorItemList::const_iterator POPAnimatorItemListConstIterator;

#if !TARGET_OS_IPHONE
static BOOL _disableBackgroundThread = YES;
static uint64_t _displayTimerFrequency = kDisplayTimerFrequency;
#endif

@interface POPAnimator ()
{
#if TARGET_OS_IPHONE
  CADisplayLink *_displayLink;
#else
  CVDisplayLinkRef _displayLink;
  dispatch_source_t _displayTimer;
  BOOL _displayTimerRunning;
  int32_t _enqueuedRender;
#endif
  POPAnimatorItemList _list;
  CFMutableDictionaryRef _dict;
  NSMutableArray *_observers;
  POPAnimatorItemList _pendingList;
  CFRunLoopObserverRef _pendingListObserver;
  CFTimeInterval _slowMotionStartTime;
  CFTimeInterval _slowMotionLastTime;
  CFTimeInterval _slowMotionAccumulator;
  CFTimeInterval _beginTime;
  pthread_mutex_t _lock;
  BOOL _disableDisplayLink;
}
@end

@implementation POPAnimator
@synthesize delegate = _delegate;
@synthesize disableDisplayLink = _disableDisplayLink;
@synthesize beginTime = _beginTime;

#if !TARGET_OS_IPHONE
static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *context)
{
  if (_disableBackgroundThread) {
    __unsafe_unretained POPAnimator *pa = (__bridge POPAnimator *)context;
    int32_t* enqueuedRender = &pa->_enqueuedRender;
    if (*enqueuedRender == 0) {
      OSAtomicIncrement32(enqueuedRender);
      dispatch_async(dispatch_get_main_queue(), ^{
        [(__bridge POPAnimator*)context render];
        OSAtomicDecrement32(enqueuedRender);
      });
    }
  } else {
    [(__bridge POPAnimator*)context render];
  }
  return kCVReturnSuccess;
}
#endif

// call while holding lock
static void updateDisplayLink(POPAnimator *self)
{
  BOOL paused = (0 == self->_observers.count && self->_list.empty()) || self->_disableDisplayLink;

#if TARGET_OS_IPHONE
  if (paused != self->_displayLink.paused) {
    FBLogAnimInfo(paused ? @"pausing display link" : @"unpausing display link");
    self->_displayLink.paused = paused;
  }
#else
  if (NULL != self->_displayLink) {
    if (paused == CVDisplayLinkIsRunning(self->_displayLink)) {
      FBLogAnimInfo(paused ? @"pausing display link" : @"unpausing display link");
      if (paused) {
        CVDisplayLinkStop(self->_displayLink);
      } else {
        CVDisplayLinkStart(self->_displayLink);
      }
    }
  } else {
    if (paused == self->_displayTimerRunning) {
      FBLogAnimInfo(paused ? @"pausing display timer" : @"unpausing display timer");
      if (paused) {
        self->_displayTimerRunning = NO;
        dispatch_suspend(self->_displayTimer);
      } else {
        self->_displayTimerRunning = YES;
        dispatch_resume(self->_displayTimer);
      }
    }
  }
#endif
}

static void updateAnimatable(id obj, POPPropertyAnimationState *anim, bool shouldAvoidExtraneousWrite = false)
{
  // handle user-initiated stop or pause; halt animation
  if (!anim->active || anim->paused)
    return;

  if (anim->hasValue()) {
    POPAnimatablePropertyWriteBlock write = anim->property.writeBlock;
    if (NULL == write)
      return;

    // current animation value
    VectorRef currentVec = anim->currentValue();

    if (!anim->additive) {

      // if avoiding extraneous writes and we have a read block defined
      if (shouldAvoidExtraneousWrite) {

        POPAnimatablePropertyReadBlock read = anim->property.readBlock;
        if (read) {
          // compare current animation value with object value
          Vector4r currentValue = currentVec->vector4r();
          Vector4r objectValue = read_values(read, obj, anim->valueCount);
          if (objectValue == currentValue) {
            return;
          }
        }
      }
      
      // update previous values; support animation convergence
      anim->previous2Vec = anim->previousVec;
      anim->previousVec = currentVec;

      // write value
      write(obj, currentVec->data());
      if (anim->tracing) {
        [anim->tracer writePropertyValue:POPBox(currentVec, anim->valueType, true)];
      }
    } else {
      POPAnimatablePropertyReadBlock read = anim->property.readBlock;
      NSCAssert(read, @"additive requires an animatable property readBlock");
      if (NULL == read) {
        return;
      }

      // object value
      Vector4r objectValue = read_values(read, obj, anim->valueCount);

      // current value
      Vector4r currentValue = currentVec->vector4r();
      
      // determine animation change
      if (anim->previousVec) {
        Vector4r previousValue = anim->previousVec->vector4r();
        currentValue -= previousValue;
      }

      // avoid writing no change
      if (shouldAvoidExtraneousWrite && currentValue == Vector4r::Zero()) {
        return;
      }
      
      // add to object value
      currentValue += objectValue;
      
      // update previous values; support animation convergence
      anim->previous2Vec = anim->previousVec;
      anim->previousVec = currentVec;
      
      // write value
      write(obj, currentValue.data());
      if (anim->tracing) {
        [anim->tracer writePropertyValue:POPBox(currentVec, anim->valueType, true)];
      }
    }
  }
}

static void applyAnimationTime(id obj, POPAnimationState *state, CFTimeInterval time)
{
  if (!state->advanceTime(time, obj)) {
    return;
  }
  
  POPPropertyAnimationState *ps = dynamic_cast<POPPropertyAnimationState*>(state);
  if (NULL != ps) {
    updateAnimatable(obj, ps);
  }
  
  state->delegateApply();
}

static void applyAnimationToValue(id obj, POPAnimationState *state)
{
  POPPropertyAnimationState *ps = dynamic_cast<POPPropertyAnimationState*>(state);

  if (NULL != ps) {
    
    // finalize progress
    ps->finalizeProgress();
    
    // write to value, updating only if needed
    updateAnimatable(obj, ps, true);
  }
  
  state->delegateApply();
}

static POPAnimation *deleteDictEntry(POPAnimator *self, id __unsafe_unretained obj, NSString *key, BOOL cleanup = YES)
{
  POPAnimation *anim = nil;

  // lock
  pthread_mutex_lock(&self->_lock);

  NSMutableDictionary *keyAnimationsDict = (__bridge id)CFDictionaryGetValue(self->_dict, (__bridge void *)obj);
  if (keyAnimationsDict) {

    anim = keyAnimationsDict[key];
    if (anim) {

      // remove key
      [keyAnimationsDict removeObjectForKey:key];

      // cleanup empty dictionaries
      if (cleanup && 0 == keyAnimationsDict.count) {
        CFDictionaryRemoveValue(self->_dict, (__bridge void *)obj);
      }
    }
  }

  // unlock
  pthread_mutex_unlock(&self->_lock);
  return anim;
}

static void stopAndCleanup(POPAnimator *self, POPAnimatorItemRef item, bool shouldRemove, bool finished)
{
  // remove
  if (shouldRemove) {
    deleteDictEntry(self, item->unretainedObject, item->key);
  }

  // stop
  POPAnimationState *state = POPAnimationGetState(item->animation);
  state->stop(shouldRemove, finished);

  if (shouldRemove) {
    // lock
    pthread_mutex_lock(&self->_lock);

    // find item in list
    // may have already been removed on animationDidStop:
    POPAnimatorItemListIterator find_iter = find(self->_list.begin(), self->_list.end(), item);
    BOOL found = find_iter != self->_list.end();

    if (found) {
      self->_list.erase(find_iter);
    }

    // unlock
    pthread_mutex_unlock(&self->_lock);
  }
}

+ (id)sharedAnimator
{
  static POPAnimator* _animator = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    _animator = [[POPAnimator alloc] init];
  });
  return _animator;
}

#if !TARGET_OS_IPHONE
+ (BOOL)disableBackgroundThread
{
  return _disableBackgroundThread;
}

+ (void)setDisableBackgroundThread:(BOOL)flag
{
  _disableBackgroundThread = flag;
}

+ (uint64_t)displayTimerFrequency
{
  return _displayTimerFrequency;
}

+ (void)setDisplayTimerFrequency:(uint64_t)frequency
{
  _displayTimerFrequency = frequency;
}
#endif

#pragma mark - Lifecycle

- (instancetype)init
{
  self = [super init];
  if (nil == self) return nil;

#if TARGET_OS_IPHONE
  _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
  _displayLink.paused = YES;
  [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
#else
  CVReturn ret = CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
  if (kCVReturnSuccess != ret) {
    ret = CVDisplayLinkCreateWithCGDisplay(CGMainDisplayID(), &_displayLink);
  }
  if (kCVReturnSuccess == ret) {
    CVDisplayLinkSetOutputCallback(_displayLink, displayLinkCallback, (__bridge void *)self);
  } else {
    FBLogAnimInfo(@"cannot create display link: ret=%ld, falling back to display timer at %llu Hz", (long)ret, _displayTimerFrequency);
    // Thanks to Apple, on older OSes DISPATCH_TIMER_STRICT is not supported and dispatch_source_create failed if we use it.
    unsigned long mask = (NSFoundationVersionNumber >= NSFoundationVersionNumber10_9) ? DISPATCH_TIMER_STRICT : 0;
    _displayTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, mask, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
    NSAssert(nil != _displayTimer, @"Cannot create display timer");
    dispatch_source_set_timer(_displayTimer, DISPATCH_TIME_NOW, NSEC_PER_SEC / _displayTimerFrequency, 0);
    __weak POPAnimator *weakSelf = self;
    dispatch_source_set_event_handler(_displayTimer, ^{
      __strong POPAnimator *strongSelf = weakSelf;
      if (__builtin_expect(nil != strongSelf, 1)) {
        (void) displayLinkCallback(NULL, NULL, NULL, 0, NULL, (__bridge void *)strongSelf);
      }
    });
  }
#endif

  _dict = POPDictionaryCreateMutableWeakPointerToStrongObject(5);
  pthread_mutex_init(&_lock, NULL);

  return self;
}

#if !TARGET_OS_IPHONE
- (instancetype)initWithDisplayID:(CGDirectDisplayID)displayID
{
  if (kCGNullDirectDisplay == displayID) {
    return [self init];
  }
  
  self = [super init];
  if (nil == self) return nil;
  
  CVReturn ret = CVDisplayLinkCreateWithCGDisplay(displayID, &_displayLink);
  if (kCVReturnSuccess != ret) {
    return nil;
  }
  CVDisplayLinkSetOutputCallback(_displayLink, displayLinkCallback, (__bridge void *)self);
  
  _dict = POPDictionaryCreateMutableWeakPointerToStrongObject(5);
  pthread_mutex_init(&_lock, NULL);
  
  return self;
}
#endif

- (void)dealloc
{
#if TARGET_OS_IPHONE
  [_displayLink invalidate];
#else
  if (_displayLink != NULL) {
    CVDisplayLinkStop(_displayLink);
    CVDisplayLinkRelease(_displayLink);
  }
  if (_displayTimer != NULL) {
    dispatch_source_cancel(_displayTimer);
#if !OS_OBJECT_USE_OBJC
    dispatch_release(_displayTimer);
#endif
    _displayTimer = NULL;
  }
#endif
  [self _clearPendingListObserver];
  
  pthread_mutex_destroy(&_lock);
}

#pragma mark - Utility

- (void)_processPendingList
{
  // rendering pending animations
  CFTimeInterval time = [self _currentRenderTime];
  [self _renderTime:(0 != _beginTime) ? _beginTime : time items:_pendingList];

  // lock
  pthread_mutex_lock(&_lock);

  // clear list and observer
  _pendingList.clear();
  [self _clearPendingListObserver];

  // unlock
  pthread_mutex_unlock(&_lock);
}

- (void)_clearPendingListObserver
{
  if (_pendingListObserver) {
    CFRunLoopRemoveObserver(CFRunLoopGetMain(), _pendingListObserver, kCFRunLoopCommonModes);
    CFRelease(_pendingListObserver);
    _pendingListObserver = NULL;
  }
}

- (void)_scheduleProcessPendingList
{
  // see WebKit for magic numbers, eg http://trac.webkit.org/changeset/166540
  static const CFIndex CATransactionCommitRunLoopOrder = 2000000;
  static const CFIndex POPAnimationApplyRunLoopOrder = CATransactionCommitRunLoopOrder - 1;

  // lock
  pthread_mutex_lock(&_lock);

  if (!_pendingListObserver) {
    __weak POPAnimator *weakSelf = self;

    _pendingListObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
      [weakSelf _processPendingList];
    });

    if (_pendingListObserver) {
      CFRunLoopAddObserver(CFRunLoopGetMain(), _pendingListObserver,  kCFRunLoopCommonModes);
    }
  }

  // unlock
  pthread_mutex_unlock(&_lock);
}

- (void)_renderTime:(CFTimeInterval)time items:(std::list<POPAnimatorItemRef>)items
{
  // begin transaction with actions disabled
  [CATransaction begin];
  [CATransaction setDisableActions:YES];

  // notify delegate
  __strong __typeof__(_delegate) delegate = _delegate;
  [delegate animatorWillAnimate:self];

  // lock
  pthread_mutex_lock(&_lock);

  // count active animations
  const NSUInteger count = items.size();
  if (0 == count) {
    // unlock
    pthread_mutex_unlock(&_lock);
  } else {
    // copy list into vector
    std::vector<POPAnimatorItemRef> vector{ items.begin(), items.end() };

    // unlock
    pthread_mutex_unlock(&_lock);

    for (auto item : vector) {
      [self _renderTime:time item:item];
    }
  }

  // notify observers
  for (id observer in self.observers) {
    [observer animatorDidAnimate:(id)self];
  }

  // lock
  pthread_mutex_lock(&_lock);

  // update display link
  updateDisplayLink(self);

  // unlock
  pthread_mutex_unlock(&_lock);

  // notify delegate and commit
  [delegate animatorDidAnimate:self];
  [CATransaction commit];
}

- (void)_renderTime:(CFTimeInterval)time item:(POPAnimatorItemRef)item
{
  id obj = item->object;
  POPAnimation *anim = item->animation;
  POPAnimationState *state = POPAnimationGetState(anim);

  if (nil == obj) {
    // object exists not; stop animating
    NSAssert(item->unretainedObject, @"object should exist");
    stopAndCleanup(self, item, true, false);
  } else {

    // start if needed
    state->startIfNeeded(obj, time, _slowMotionAccumulator);

    // only run active, not paused animations
    if (state->active && !state->paused) {
      // object exists; animate
      applyAnimationTime(obj, state, time);

      FBLogAnimDebug(@"time:%f running:%@", time, item->animation);
      if (state->isDone()) {
        // set end value
        applyAnimationToValue(obj, state);

        state->repeatCount--;
        if (state->repeatForever || state->repeatCount > 0) {
          if ([anim isKindOfClass:[POPPropertyAnimation class]]) {
            POPPropertyAnimation *propAnim = (POPPropertyAnimation *)anim;
            id oldFromValue = propAnim.fromValue;
            propAnim.fromValue = propAnim.toValue;

            if (state->autoreverses) {
              if (state->tracing) {
                [state->tracer autoreversed];
              }

              if (state->type == kPOPAnimationDecay) {
                POPDecayAnimation *decayAnimation = (POPDecayAnimation *)propAnim;
                decayAnimation.velocity = [decayAnimation reversedVelocity];
              } else {
                propAnim.toValue = oldFromValue;
              }
            } else {
              if (state->type == kPOPAnimationDecay) {
                POPDecayAnimation *decayAnimation = (POPDecayAnimation *)propAnim;
                id originalVelocity = decayAnimation.originalVelocity;
                decayAnimation.velocity = originalVelocity;
              } else {
                propAnim.fromValue = oldFromValue;
              }
            }
          }

          state->stop(NO, NO);
          state->reset(true);

          state->startIfNeeded(obj, time, _slowMotionAccumulator);
        } else {
          stopAndCleanup(self, item, state->removedOnCompletion, YES);
        }
      }
    }
  }
}

#pragma mark - API

- (NSArray *)observers
{
  // lock
  pthread_mutex_lock(&_lock);

  // get observers
  NSArray *observers = 0 != _observers.count ? [_observers copy] : nil;

  // unlock
  pthread_mutex_unlock(&_lock);
  return observers;
}

- (void)addAnimation:(POPAnimation *)anim forObject:(id)obj key:(NSString *)key
{
  if (!anim || !obj) {
    return;
  }

  // support arbitrarily many nil keys
  if (!key) {
    key = [[NSUUID UUID] UUIDString];
  }

  // lock
  pthread_mutex_lock(&_lock);

  // get key, animation dict associated with object
  NSMutableDictionary *keyAnimationDict = (__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj);

  // update associated animation state
  if (nil == keyAnimationDict) {
    keyAnimationDict = [NSMutableDictionary dictionary];
    CFDictionarySetValue(_dict, (__bridge void *)obj, (__bridge void *)keyAnimationDict);
  } else {
    // if the animation instance already exists, avoid cancelling only to restart
    POPAnimation *existingAnim = keyAnimationDict[key];
    if (existingAnim) {
      // unlock
      pthread_mutex_unlock(&_lock);

      if (existingAnim == anim) {
        return;
      }
      [self removeAnimationForObject:obj key:key cleanupDict:NO];
        
      // lock
      pthread_mutex_lock(&_lock);
    }
  }
  keyAnimationDict[key] = anim;

  // create entry after potential removal
  POPAnimatorItemRef item(new POPAnimatorItem(obj, key, anim));

  // add to list and pending list
  _list.push_back(item);
  _pendingList.push_back(item);

  // support animation re-use, reset all animation state
  POPAnimationGetState(anim)->reset(true);

  // update display link
  updateDisplayLink(self);

  // unlock
  pthread_mutex_unlock(&_lock);

  // schedule runloop processing of pending animations
  [self _scheduleProcessPendingList];
}

- (void)removeAllAnimationsForObject:(id)obj
{
  // lock
  pthread_mutex_lock(&_lock);

  NSArray *animations = [(__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj) allValues];
  CFDictionaryRemoveValue(_dict, (__bridge void *)obj);

  // unlock
  pthread_mutex_unlock(&_lock);

  if (0 == animations.count) {
    return;
  }

  NSHashTable *animationSet = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:animations.count];
  for (id animation in animations) {
    [animationSet addObject:animation];
  }

  // lock
  pthread_mutex_lock(&_lock);

  POPAnimatorItemRef item;
  for (auto iter = _list.begin(); iter != _list.end();) {
    item = *iter;
    if(![animationSet containsObject:item->animation]) {
      iter++;
    } else {
      iter = _list.erase(iter);
    }
  }

  // unlock
  pthread_mutex_unlock(&_lock);

  for (POPAnimation *anim in animations) {
    POPAnimationState *state = POPAnimationGetState(anim);
    state->stop(true, !state->active);
  }
}

- (void)removeAnimationForObject:(id)obj key:(NSString *)key cleanupDict:(BOOL)cleanupDict
{
  POPAnimation *anim = deleteDictEntry(self, obj, key, cleanupDict);
  if (nil == anim) {
    return;
  }

  // lock
  pthread_mutex_lock(&_lock);

  // remove from list
  POPAnimatorItemRef item;
  for (auto iter = _list.begin(); iter != _list.end();) {
    item = *iter;
    if(anim == item->animation) {
      _list.erase(iter);
      break;
    } else {
      iter++;
    }
  }

  // remove from pending list
  for (auto iter = _pendingList.begin(); iter != _pendingList.end();) {
    item = *iter;
    if(anim == item->animation) {
      _pendingList.erase(iter);
      break;
    } else {
      iter++;
    }
  }

  // unlock
  pthread_mutex_unlock(&_lock);

  // stop animation and callout
  POPAnimationState *state = POPAnimationGetState(anim);
  state->stop(true, (!state->active && !state->paused));
}

- (void)removeAnimationForObject:(id)obj key:(NSString *)key
{
  [self removeAnimationForObject:obj key:key cleanupDict:YES];
}

- (NSArray *)animationKeysForObject:(id)obj
{
  // lock
  pthread_mutex_lock(&_lock);

  // get keys
  NSArray *keys = [(__bridge NSDictionary *)CFDictionaryGetValue(_dict, (__bridge void *)obj) allKeys];

  // unlock
  pthread_mutex_unlock(&_lock);
  return keys;
}

- (id)animationForObject:(id)obj key:(NSString *)key
{
  // lock
  pthread_mutex_lock(&_lock);

  // lookup animation
  NSDictionary *keyAnimationsDict = (__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj);
  POPAnimation *animation = keyAnimationsDict[key];

  // unlock
  pthread_mutex_unlock(&_lock);
  return animation;
}

- (CFTimeInterval)refreshPeriod
{
#if TARGET_OS_IPHONE
  return self->_displayLink.duration;
#else
  if (NULL != self->_displayLink) {
    CVTime period = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(self->_displayLink);
    if (period.flags & kCVTimeIsIndefinite) {
      return 0;
    }
    return ((CFTimeInterval)period.timeValue / (CFTimeInterval)period.timeScale);
  }
  return (1.0 / (CFTimeInterval)_displayTimerFrequency);
#endif
}

- (CFTimeInterval)_currentRenderTime
{
  CFTimeInterval time = CACurrentMediaTime();

#if TARGET_IPHONE_SIMULATOR
  // support slow-motion animations
  time += _slowMotionAccumulator;
  float f = POPAnimationDragCoefficient();

  if (f > 1.0) {
    if (!_slowMotionStartTime) {
      _slowMotionStartTime = time;
    } else {
      time = (time - _slowMotionStartTime) / f + _slowMotionStartTime;
      _slowMotionLastTime = time;
    }
  } else if (_slowMotionStartTime) {
    CFTimeInterval dt = (_slowMotionLastTime - time);
    time += dt;
    _slowMotionAccumulator += dt;
    _slowMotionStartTime = 0;
  }
#endif

  return time;
}

- (void)render
{
  CFTimeInterval time = [self _currentRenderTime];
  [self renderTime:time];
}

- (void)renderTime:(CFTimeInterval)time
{
  [self _renderTime:time items:_list];
}

- (void)addObserver:(id<POPAnimatorObserving>)observer
{
  NSAssert(nil != observer, @"attempting to add nil %@ observer", self);
  if (nil == observer) {
    return;
  }

  // lock
  pthread_mutex_lock(&_lock);

  if (!_observers) {
    // use ordered collection for deterministic callout
    _observers = [[NSMutableArray alloc] initWithCapacity:1];
  }

  [_observers addObject:observer];
  updateDisplayLink(self);

  // unlock
  pthread_mutex_unlock(&_lock);
}

- (void)removeObserver:(id<POPAnimatorObserving>)observer
{
  NSAssert(nil != observer, @"attempting to remove nil %@ observer", self);
  if (nil == observer) {
    return;
  }

  // lock
  pthread_mutex_lock(&_lock);

  [_observers removeObject:observer];
  updateDisplayLink(self);

  // unlock
  pthread_mutex_unlock(&_lock);
}

@end


================================================
FILE: pop/POPAnimatorPrivate.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <pop/POPAnimator.h>

@class POPAnimation;

@protocol POPAnimatorObserving <NSObject>
@required

/**
 @abstract Called on each observer after animator has advanced. Core Animation actions are disabled by default.
 */
- (void)animatorDidAnimate:(POPAnimator *)animator;

@end

@interface POPAnimator ()

#if !TARGET_OS_IPHONE
/**
 Determines whether or not to use a high priority background thread for animation updates. Using a background thread can result in faster, more responsive updates, but may be less compatible. Defaults to YES.
 */
+ (BOOL)disableBackgroundThread;
+ (void)setDisableBackgroundThread:(BOOL)flag;

/**
 Determines the frequency (Hz) of the timer used when no display is available. Defaults to 60Hz.
 */
+ (uint64_t)displayTimerFrequency;
+ (void)setDisplayTimerFrequency:(uint64_t)frequency;
#endif

/**
 Used for externally driven animator instances.
 */
@property (assign, nonatomic) BOOL disableDisplayLink;

/**
 Time used when starting animations. Defaults to 0 meaning current media time is used. Exposed for unit testing.
 */
@property (assign, nonatomic) CFTimeInterval beginTime;

/**
 Exposed for unit testing.
 */
- (void)renderTime:(CFTimeInterval)time;

/**
 Funnel methods for category additions.
 */
- (void)addAnimation:(POPAnimation *)anim forObject:(id)obj key:(NSString *)key;
- (void)removeAllAnimationsForObject:(id)obj;
- (void)removeAnimationForObject:(id)obj key:(NSString *)key;
- (NSArray *)animationKeysForObject:(id)obj;
- (POPAnimation *)animationForObject:(id)obj key:(NSString *)key;

/**
 @abstract Add an animator observer. Observer will be notified of each subsequent animator advance until removal.
 */
- (void)addObserver:(id<POPAnimatorObserving>)observer;

/**
 @abstract Remove an animator observer.
 */
- (void)removeObserver:(id<POPAnimatorObserving>)observer;

@end


================================================
FILE: pop/POPBasicAnimation.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <pop/POPPropertyAnimation.h>

/**
 @abstract A concrete basic animation class.
 @discussion Animation is achieved through interpolation.
 */
@interface POPBasicAnimation : POPPropertyAnimation

/**
 @abstract The designated initializer.
 @returns An instance of a basic animation.
 */
+ (instancetype)animation;

/**
 @abstract Convenience initializer that returns an animation with animatable property of name.
 @param name The name of the animatable property.
 @returns An instance of a basic animation configured with specified animatable property.
 */
+ (instancetype)animationWithPropertyNamed:(NSString *)name;

/**
 @abstract Convenience constructor.
 @returns Returns a basic animation with kCAMediaTimingFunctionDefault timing function.
 */
+ (instancetype)defaultAnimation;

/**
 @abstract Convenience constructor.
 @returns Returns a basic animation with kCAMediaTimingFunctionLinear timing function.
 */
+ (instancetype)linearAnimation;

/**
 @abstract Convenience constructor.
 @returns Returns a basic animation with kCAMediaTimingFunctionEaseIn timing function.
 */
+ (instancetype)easeInAnimation;

/**
 @abstract Convenience constructor.
 @returns Returns a basic animation with kCAMediaTimingFunctionEaseOut timing function.
 */
+ (instancetype)easeOutAnimation;

/**
 @abstract Convenience constructor.
 @returns Returns a basic animation with kCAMediaTimingFunctionEaseInEaseOut timing function.
 */
+ (instancetype)easeInEaseOutAnimation;

/**
 @abstract The duration in seconds. Defaults to 0.4.
 */
@property (assign, nonatomic) CFTimeInterval duration;

/**
 @abstract A timing function defining the pacing of the animation. Defaults to nil indicating pacing according to kCAMediaTimingFunctionDefault.
 */
@property (strong, nonatomic) CAMediaTimingFunction *timingFunction;

@end


================================================
FILE: pop/POPBasicAnimation.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPBasicAnimationInternal.h"

@implementation POPBasicAnimation

#undef __state
#define __state ((POPBasicAnimationState *)_state)

#pragma mark - Lifecycle

+ (instancetype)animation
{
  return [[self alloc] init];
}

+ (instancetype)animationWithPropertyNamed:(NSString *)aName
{
  POPBasicAnimation *anim = [self animation];
  anim.property = [POPAnimatableProperty propertyWithName:aName];
  return anim;
}

- (void)_initState
{
  _state = new POPBasicAnimationState(self);
}

+ (instancetype)linearAnimation
{
  POPBasicAnimation *anim = [self animation];
  anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
  return anim;
}

+ (instancetype)easeInAnimation
{
  POPBasicAnimation *anim = [self animation];
  anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
  return anim;
}

+ (instancetype)easeOutAnimation
{
  POPBasicAnimation *anim = [self animation];
  anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
  return anim;
}

+ (instancetype)easeInEaseOutAnimation
{
  POPBasicAnimation *anim = [self animation];
  anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
  return anim;
}

+ (instancetype)defaultAnimation
{
  POPBasicAnimation *anim = [self animation];
  anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
  return anim;
}

- (id)init
{
  return [self _init];
}

#pragma mark - Properties

DEFINE_RW_PROPERTY(POPBasicAnimationState, duration, setDuration:, CFTimeInterval);
DEFINE_RW_PROPERTY_OBJ(POPBasicAnimationState, timingFunction, setTimingFunction:, CAMediaTimingFunction*, __state->updatedTimingFunction(););

#pragma mark - Utility

- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug
{
  [super _appendDescription:s debug:debug];
  if (__state->duration)
    [s appendFormat:@"; duration = %f", __state->duration];
}

@end

@implementation POPBasicAnimation (NSCopying)

- (instancetype)copyWithZone:(NSZone *)zone {
  
  POPBasicAnimation *copy = [super copyWithZone:zone];
  
  if (copy) {
    copy.duration = self.duration;
    copy.timingFunction = self.timingFunction; // not a 'copy', but timing functions are publicly immutable.
  }
  
  return copy;
}

@end

================================================
FILE: pop/POPBasicAnimationInternal.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPBasicAnimation.h"

#import "POPPropertyAnimationInternal.h"

// default animation duration
static CGFloat const kPOPAnimationDurationDefault = 0.4;

// progress threshold for computing done
static CGFloat const kPOPProgressThreshold = 1e-6;

static void interpolate(POPValueType valueType, NSUInteger count, const CGFloat *fromVec, const CGFloat *toVec, CGFloat *outVec, CGFloat p)
{
  switch (valueType) {
    case kPOPValueInteger:
    case kPOPValueFloat:
    case kPOPValuePoint:
    case kPOPValueSize:
    case kPOPValueRect:
    case kPOPValueEdgeInsets:
    case kPOPValueColor:
      POPInterpolateVector(count, outVec, fromVec, toVec, p);
      break;
    default:
      NSCAssert(false, @"unhandled type %d", valueType);
      break;
  }
}

struct _POPBasicAnimationState : _POPPropertyAnimationState
{
  CAMediaTimingFunction *timingFunction;
  double timingControlPoints[4];
  CFTimeInterval duration;
  CFTimeInterval timeProgress;

  _POPBasicAnimationState(id __unsafe_unretained anim) : _POPPropertyAnimationState(anim),
  timingFunction(nil),
  timingControlPoints{0.},
  duration(kPOPAnimationDurationDefault),
  timeProgress(0.)
  {
    type = kPOPAnimationBasic;
  }

  bool isDone() {
    if (_POPPropertyAnimationState::isDone()) {
      return true;
    }
    return timeProgress + kPOPProgressThreshold >= 1.;
  }

  void updatedTimingFunction()
  {
    float vec[4] = {0.};
    [timingFunction getControlPointAtIndex:1 values:&vec[0]];
    [timingFunction getControlPointAtIndex:2 values:&vec[2]];
    for (NSUInteger idx = 0; idx < POP_ARRAY_COUNT(vec); idx++) {
      timingControlPoints[idx] = vec[idx];
    }
  }

  bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) {
    // default timing function
    if (!timingFunction) {
      ((POPBasicAnimation *)self).timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
    }

    // solve for normalized time, aka progress [0, 1]
    CGFloat p = 1.0f;
    if (duration > 0.0f) {
        // cap local time to duration
        CFTimeInterval t = MIN(time - startTime, duration) / duration;
        p = POPTimingFunctionSolve(timingControlPoints, t, SOLVE_EPS(duration));
        timeProgress = t;
    } else {
        timeProgress = 1.;
    }

    // interpolate and advance
    interpolate(valueType, valueCount, fromVec->data(), toVec->data(), currentVec->data(), p);
    progress = p;
    clampCurrentValue();

    return true;
  }
};

typedef struct _POPBasicAnimationState POPBasicAnimationState;


================================================
FILE: pop/POPCGUtils.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <CoreGraphics/CoreGraphics.h>

#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#import <AppKit/AppKit.h>
#endif

#import "POPDefines.h"

#if SCENEKIT_SDK_AVAILABLE
#import <SceneKit/SceneKit.h>
#endif

POP_EXTERN_C_BEGIN

NS_INLINE CGPoint values_to_point(const CGFloat values[])
{
  return CGPointMake(values[0], values[1]);
}

NS_INLINE CGSize values_to_size(const CGFloat values[])
{
  return CGSizeMake(values[0], values[1]);
}

NS_INLINE CGRect values_to_rect(const CGFloat values[])
{
  return CGRectMake(values[0], values[1], values[2], values[3]);
}

#if SCENEKIT_SDK_AVAILABLE
NS_INLINE SCNVector3 values_to_vec3(const CGFloat values[])
{
  return SCNVector3Make(values[0], values[1], values[2]);
}

NS_INLINE SCNVector4 values_to_vec4(const CGFloat values[])
{
  return SCNVector4Make(values[0], values[1], values[2], values[3]);
}
#endif

#if TARGET_OS_IPHONE

NS_INLINE UIEdgeInsets values_to_edge_insets(const CGFloat values[])
{
  return UIEdgeInsetsMake(values[0], values[1], values[2], values[3]);
}

#endif

NS_INLINE void values_from_point(CGFloat values[], CGPoint p)
{
  values[0] = p.x;
  values[1] = p.y;
}

NS_INLINE void values_from_size(CGFloat values[], CGSize s)
{
  values[0] = s.width;
  values[1] = s.height;
}

NS_INLINE void values_from_rect(CGFloat values[], CGRect r)
{
  values[0] = r.origin.x;
  values[1] = r.origin.y;
  values[2] = r.size.width;
  values[3] = r.size.height;
}

#if SCENEKIT_SDK_AVAILABLE
NS_INLINE void values_from_vec3(CGFloat values[], SCNVector3 v)
{
  values[0] = v.x;
  values[1] = v.y;
  values[2] = v.z;
}

NS_INLINE void values_from_vec4(CGFloat values[], SCNVector4 v)
{
  values[0] = v.x;
  values[1] = v.y;
  values[2] = v.z;
  values[3] = v.w;
}
#endif

#if TARGET_OS_IPHONE

NS_INLINE void values_from_edge_insets(CGFloat values[], UIEdgeInsets i)
{
  values[0] = i.top;
  values[1] = i.left;
  values[2] = i.bottom;
  values[3] = i.right;
}

#endif

/**
 Takes a CGColorRef and converts it into RGBA components, if necessary.
 */
extern void POPCGColorGetRGBAComponents(CGColorRef color, CGFloat components[]);

/**
 Takes RGBA components and returns a CGColorRef.
 */
extern CGColorRef POPCGColorRGBACreate(const CGFloat components[]) CF_RETURNS_RETAINED;

/**
 Takes a color reference and returns a CGColor.
 */
extern CGColorRef POPCGColorWithColor(id color) CF_RETURNS_NOT_RETAINED;

#if TARGET_OS_IPHONE

/**
 Takes a UIColor and converts it into RGBA components, if necessary.
 */
extern void POPUIColorGetRGBAComponents(UIColor *color, CGFloat components[]);

/**
 Takes RGBA components and returns a UIColor.
 */
extern UIColor *POPUIColorRGBACreate(const CGFloat components[]) NS_RETURNS_RETAINED;

#else

/**
 Takes a NSColor and converts it into RGBA components, if necessary.
 */
extern void POPNSColorGetRGBAComponents(NSColor *color, CGFloat components[]);

/**
 Takes RGBA components and returns a NSColor.
 */
extern NSColor *POPNSColorRGBACreate(const CGFloat components[]) NS_RETURNS_RETAINED;

#endif

POP_EXTERN_C_END


================================================
FILE: pop/POPCGUtils.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPCGUtils.h"

#import <objc/runtime.h>

void POPCGColorGetRGBAComponents(CGColorRef color, CGFloat components[])
{
  if (color) {
    const CGFloat *colors = CGColorGetComponents(color);
    size_t count = CGColorGetNumberOfComponents(color);

    if (4 == count) {
      // RGB colorspace
      components[0] = colors[0];
      components[1] = colors[1];
      components[2] = colors[2];
      components[3] = colors[3];
    } else if (2 == count) {
      // Grey colorspace
      components[0] = components[1] = components[2] = colors[0];
      components[3] = colors[1];
    } else {
      // Use CI to convert
      CIColor *ciColor = [CIColor colorWithCGColor:color];
      components[0] = ciColor.red;
      components[1] = ciColor.green;
      components[2] = ciColor.blue;
      components[3] = ciColor.alpha;
    }
  } else {
    memset(components, 0, 4 * sizeof(components[0]));
  }
}

CGColorRef POPCGColorRGBACreate(const CGFloat components[])
{
#if TARGET_OS_IPHONE
  CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
  CGColorRef color = CGColorCreate(space, components);
  CGColorSpaceRelease(space);
  return color;
#else
  return CGColorCreateGenericRGB(components[0], components[1], components[2], components[3]);
#endif
}

CGColorRef POPCGColorWithColor(id color)
{
  if (CFGetTypeID((__bridge CFTypeRef)color) == CGColorGetTypeID()) {
    return ((__bridge CGColorRef)color);
  }
#if TARGET_OS_IPHONE
  else if ([color isKindOfClass:[UIColor class]]) {
    return [color CGColor];
  }
#else
  else if ([color isKindOfClass:[NSColor class]]) {
    // -[NSColor CGColor] is only supported since OSX 10.8+
    if ([color respondsToSelector:@selector(CGColor)]) {
      return [color CGColor];
    }

    /*
     * Otherwise create a CGColorRef manually.
     *
     * The original accessor is (or would be) declared as:
     *   @property(readonly) CGColorRef CGColor;
     *   - (CGColorRef)CGColor NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED;
     *
     * (Please note that OSX' accessor is atomic, while iOS' isn't.)
     *
     * The access to the NSColor object must thus be synchronized
     * and the CGColorRef be stored as an associated object,
     * to return a reference which doesn't need to be released manually.
     */
    @synchronized(color) {
      static const void* key = &key;

      CGColorRef colorRef = (__bridge CGColorRef)objc_getAssociatedObject(color, key);

      if (!colorRef) {
        size_t numberOfComponents = [(NSColor *)color numberOfComponents];
        CGFloat components[numberOfComponents];
        CGColorSpaceRef colorSpace = [[(NSColor *)color colorSpace] CGColorSpace];

        [color getComponents:components];

        colorRef = CGColorCreate(colorSpace, components);

        objc_setAssociatedObject(color, key, (__bridge id)colorRef, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        CGColorRelease(colorRef);
      }

      return colorRef;
    }
  }
#endif
  return nil;
}

#if TARGET_OS_IPHONE

void POPUIColorGetRGBAComponents(UIColor *color, CGFloat components[])
{
  return POPCGColorGetRGBAComponents(POPCGColorWithColor(color), components);
}

UIColor *POPUIColorRGBACreate(const CGFloat components[])
{
  CGColorRef colorRef = POPCGColorRGBACreate(components);
  UIColor *color = [[UIColor alloc] initWithCGColor:colorRef];
  CGColorRelease(colorRef);
  return color;
}

#else

void POPNSColorGetRGBAComponents(NSColor *color, CGFloat components[])
{
  return POPCGColorGetRGBAComponents(POPCGColorWithColor(color), components);
}

NSColor *POPNSColorRGBACreate(const CGFloat components[])
{
  CGColorRef colorRef = POPCGColorRGBACreate(components);
  NSColor *color = nil;

  if (colorRef) {
    if ([NSColor respondsToSelector:@selector(colorWithCGColor:)]) {
      color = [NSColor colorWithCGColor:colorRef];
    } else {
      color = [NSColor colorWithCIColor:[CIColor colorWithCGColor:colorRef]];
    }

    CGColorRelease(colorRef);
  }

  return color;
}

#endif



================================================
FILE: pop/POPCustomAnimation.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <pop/POPAnimation.h>

@class POPCustomAnimation;

/**
 @abstract POPCustomAnimationBlock is the callback block of a custom animation.
 @discussion This block will be executed for each animation frame and should update the property or properties being animated based on current timing.
 @param target The object being animated. Reference the passed in target to help avoid retain loops.
 @param animation The custom animation instance. Use to determine the current and elapsed time since last callback. Reference the passed in animation to help avoid retain loops.
 @return Flag indicating whether the animation should continue animating. Return NO to indicate animation is done.
 */
typedef BOOL (^POPCustomAnimationBlock)(id target, POPCustomAnimation *animation);

/**
 @abstract POPCustomAnimation is a concrete animation subclass for custom animations.
 */
@interface POPCustomAnimation : POPAnimation

/**
@abstract Creates and returns an initialized custom animation instance.
@discussion This is the designated initializer.
@param block The custom animation callback block. See {@ref POPCustomAnimationBlock}.
@return The initialized custom animation instance.
*/
+ (instancetype)animationWithBlock:(POPCustomAnimationBlock)block;

/**
 @abstract The current animation time at time of callback.
 */
@property (readonly, nonatomic) CFTimeInterval currentTime;

/**
 @abstract The elapsed animation time since last callback.
 */
@property (readonly, nonatomic) CFTimeInterval elapsedTime;

@end


================================================
FILE: pop/POPCustomAnimation.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPAnimationInternal.h"

#import "POPCustomAnimation.h"

@interface POPCustomAnimation ()
@property (nonatomic, copy) POPCustomAnimationBlock animate;
@end

@implementation POPCustomAnimation
@synthesize currentTime = _currentTime;
@synthesize elapsedTime = _elapsedTime;
@synthesize animate = _animate;

+ (instancetype)animationWithBlock:(BOOL(^)(id target, POPCustomAnimation *))block
{
  POPCustomAnimation *b = [[self alloc] _init];
  b.animate = block;
  return b;
}

- (id)_init
{
  self = [super _init];
  if (nil != self) {
    _state->type = kPOPAnimationCustom;
  }
  return self;
}

- (CFTimeInterval)beginTime
{
  POPAnimationState *s = POPAnimationGetState(self);
  return s->startTime > 0 ? s->startTime : s->beginTime;
}

- (BOOL)_advance:(id)object currentTime:(CFTimeInterval)currentTime elapsedTime:(CFTimeInterval)elapsedTime
{
  _currentTime = currentTime;
  _elapsedTime = elapsedTime;
  return _animate(object, self);
}

- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug
{
  [s appendFormat:@"; elapsedTime = %f; currentTime = %f;", _elapsedTime, _currentTime];
}

@end

/**
 *  Note that only the animate block is copied, but not the current/elapsed times
 */
@implementation POPCustomAnimation (NSCopying)

- (instancetype)copyWithZone:(NSZone *)zone {
  
  POPCustomAnimation *copy = [super copyWithZone:zone];
  
  if (copy) {
    copy.animate = self.animate;
  }
  
  return copy;
}

@end

================================================
FILE: pop/POPDecayAnimation.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <pop/POPPropertyAnimation.h>

/**
 @abstract A concrete decay animation class.
 @discussion Animation is achieved through gradual decay of animation value.
 */
@interface POPDecayAnimation : POPPropertyAnimation

/**
 @abstract The designated initializer.
 @returns An instance of a decay animation.
 */
+ (instancetype)animation;

/**
 @abstract Convenience initializer that returns an animation with animatable property of name.
 @param name The name of the animatable property.
 @returns An instance of a decay animation configured with specified animatable property.
 */
+ (instancetype)animationWithPropertyNamed:(NSString *)name;

/**
 @abstract The current velocity value.
 @discussion Set before animation start to account for initial velocity. Expressed in change of value units per second. The only POPValueTypes supported for velocity are: kPOPValuePoint, kPOPValueInteger, kPOPValueFloat, kPOPValueRect, and kPOPValueSize.
 */
@property (copy, nonatomic) id velocity;

/**
 @abstract The original velocity value.
 @discussion Since the velocity property is modified as the animation progresses, this property stores the original, passed in velocity to support autoreverse and repeatCount.
 */
@property (copy, nonatomic, readonly) id originalVelocity;

/**
 @abstract The deceleration factor.
 @discussion Values specifies should be in the range [0, 1]. Lower values results in faster deceleration. Defaults to 0.998.
 */
@property (assign, nonatomic) CGFloat deceleration;

/**
 @abstract The expected duration.
 @discussion Derived based on input velocity and deceleration values.
 */
@property (readonly, assign, nonatomic) CFTimeInterval duration;

/**
 The to value is derived based on input velocity and deceleration.
 */
- (void)setToValue:(id)toValue NS_UNAVAILABLE;

/**
 @abstract The reversed velocity.
 @discussion The reversed velocity based on the originalVelocity when the animation was set up.
 */
- (id)reversedVelocity;

@end


================================================
FILE: pop/POPDecayAnimation.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPDecayAnimationInternal.h"

#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#endif

const POPValueType supportedVelocityTypes[6] = { kPOPValuePoint, kPOPValueInteger, kPOPValueFloat, kPOPValueRect, kPOPValueSize, kPOPValueEdgeInsets };

@implementation POPDecayAnimation

#pragma mark - Lifecycle

#undef __state
#define __state ((POPDecayAnimationState *)_state)

+ (instancetype)animation
{
  return [[self alloc] init];
}

+ (instancetype)animationWithPropertyNamed:(NSString *)aName
{
  POPDecayAnimation *anim = [self animation];
  anim.property = [POPAnimatableProperty propertyWithName:aName];
  return anim;
}

- (id)init
{
  return [self _init];
}

- (void)_initState
{
  _state = new POPDecayAnimationState(self);
}

#pragma mark - Properties

DEFINE_RW_PROPERTY(POPDecayAnimationState, deceleration, setDeceleration:, CGFloat, __state->toVec = NULL;);

@dynamic velocity;

- (id)toValue
{
  [self _ensureComputedProperties];
  return POPBox(__state->toVec, __state->valueType);
}

- (CFTimeInterval)duration
{
  [self _ensureComputedProperties];
  return __state->duration;
}

- (void)setFromValue:(id)fromValue
{
  super.fromValue = fromValue;
  [self _invalidateComputedProperties];
}

- (void)setToValue:(id)aValue
{
  // no-op
  NSLog(@"ignoring to value on decay animation %@", self);
}

- (id)reversedVelocity
{
  id reversedVelocity = nil;

  POPValueType velocityType = POPSelectValueType(self.originalVelocity, supportedVelocityTypes, POP_ARRAY_COUNT(supportedVelocityTypes));
  if (velocityType == kPOPValueFloat) {
#if CGFLOAT_IS_DOUBLE
    CGFloat originalVelocityFloat = [(NSNumber *)self.originalVelocity doubleValue];
#else
    CGFloat originalVelocityFloat = [(NSNumber *)self.originalVelocity floatValue];
#endif
    NSNumber *negativeOriginalVelocityNumber = @(-originalVelocityFloat);
    reversedVelocity = negativeOriginalVelocityNumber;
  } else if (velocityType == kPOPValueInteger) {
    NSInteger originalVelocityInteger = [(NSNumber *)self.originalVelocity integerValue];
    NSNumber *negativeOriginalVelocityNumber = @(-originalVelocityInteger);
    reversedVelocity = negativeOriginalVelocityNumber;
  } else if (velocityType == kPOPValuePoint) {
    CGPoint originalVelocityPoint = [self.originalVelocity CGPointValue];
    CGPoint negativeOriginalVelocityPoint = CGPointMake(-originalVelocityPoint.x, -originalVelocityPoint.y);
    reversedVelocity = [NSValue valueWithCGPoint:negativeOriginalVelocityPoint];
  } else if (velocityType == kPOPValueRect) {
    CGRect originalVelocityRect = [self.originalVelocity CGRectValue];
    CGRect negativeOriginalVelocityRect = CGRectMake(-originalVelocityRect.origin.x, -originalVelocityRect.origin.y, -originalVelocityRect.size.width, -originalVelocityRect.size.height);
    reversedVelocity = [NSValue valueWithCGRect:negativeOriginalVelocityRect];
  } else if (velocityType == kPOPValueSize) {
    CGSize originalVelocitySize = [self.originalVelocity CGSizeValue];
    CGSize negativeOriginalVelocitySize = CGSizeMake(-originalVelocitySize.width, -originalVelocitySize.height);
    reversedVelocity = [NSValue valueWithCGSize:negativeOriginalVelocitySize];
  } else if (velocityType == kPOPValueEdgeInsets) {
#if TARGET_OS_IPHONE
    UIEdgeInsets originalVelocityInsets = [self.originalVelocity UIEdgeInsetsValue];
    UIEdgeInsets negativeOriginalVelocityInsets = UIEdgeInsetsMake(-originalVelocityInsets.top, -originalVelocityInsets.left, -originalVelocityInsets.bottom, -originalVelocityInsets.right);
    reversedVelocity = [NSValue valueWithUIEdgeInsets:negativeOriginalVelocityInsets];
#endif
  }

  return reversedVelocity;
}

- (id)originalVelocity
{
  return POPBox(__state->originalVelocityVec, __state->valueType);
}

- (id)velocity
{
  return POPBox(__state->velocityVec, __state->valueType);
}

- (void)setVelocity:(id)aValue
{
  POPValueType valueType = POPSelectValueType(aValue, supportedVelocityTypes, POP_ARRAY_COUNT(supportedVelocityTypes));
  if (valueType != kPOPValueUnknown) {
    VectorRef vec = POPUnbox(aValue, __state->valueType, __state->valueCount, YES);
    VectorRef origVec = POPUnbox(aValue, __state->valueType, __state->valueCount, YES);

    if (!vec_equal(vec, __state->velocityVec)) {
      __state->velocityVec = vec;
      __state->originalVelocityVec = origVec;

      if (__state->tracing) {
        [__state->tracer updateVelocity:aValue];
      }

      [self _invalidateComputedProperties];

      // automatically unpause active animations
      if (__state->active && __state->paused) {
        __state->fromVec = NULL;
        __state->setPaused(false);
      }
    }
  } else {
    __state->velocityVec = NULL;
    NSLog(@"Invalid velocity value for the decayAnimation: %@", aValue);
  }
}

#pragma mark - Utility

- (void)_ensureComputedProperties
{
  if (NULL == __state->toVec) {
    __state->computeDuration();
    __state->computeToValue();
  }
}

- (void)_invalidateComputedProperties
{
  __state->toVec = NULL;
  __state->duration = 0;
}

- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug
{
  [super _appendDescription:s debug:debug];

  if (0 != self.duration) {
    [s appendFormat:@"; duration = %f", self.duration];
  }

  if (__state->deceleration) {
    [s appendFormat:@"; deceleration = %f", __state->deceleration];
  }
}

@end

@implementation POPDecayAnimation (NSCopying)

- (instancetype)copyWithZone:(NSZone *)zone {
  
  POPDecayAnimation *copy = [super copyWithZone:zone];
  
  if (copy) {
    // Set the velocity to the animation's original velocity, not its current.
    copy.velocity = self.originalVelocity;
    copy.deceleration = self.deceleration;
    
  }
  
  return copy;
}

@end

================================================
FILE: pop/POPDecayAnimationInternal.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPDecayAnimation.h"

#import <cmath>

#import "POPPropertyAnimationInternal.h"

// minimal velocity factor before decay animation is considered complete, in units / s
static CGFloat kPOPAnimationDecayMinimalVelocityFactor = 5.;

// default decay animation deceleration
static CGFloat kPOPAnimationDecayDecelerationDefault = 0.998;

static void decay_position(CGFloat *x, CGFloat *v, NSUInteger count, CFTimeInterval dt, CGFloat deceleration)
{
  dt *= 1000;

  // v0 = v / 1000
  // v = v0 * powf(deceleration, dt);
  // v = v * 1000;

  // x0 = x;
  // x = x0 + v0 * deceleration * (1 - powf(deceleration, dt)) / (1 - deceleration)
  float v0[count];
  float kv = powf(deceleration, dt);
  float kx = deceleration * (1 - kv) / (1 - deceleration);

  for (NSUInteger idx = 0; idx < count; idx++) {
    v0[idx] = v[idx] / 1000.;
    v[idx] = v0[idx] * kv * 1000.;
    x[idx] = x[idx] + v0[idx] * kx;
  }
}

struct _POPDecayAnimationState : _POPPropertyAnimationState
{
  double deceleration;
  CFTimeInterval duration;

  _POPDecayAnimationState(id __unsafe_unretained anim) :
  _POPPropertyAnimationState(anim),
  deceleration(kPOPAnimationDecayDecelerationDefault),
  duration(0)
  {
    type = kPOPAnimationDecay;
  }

  bool isDone() {
    if (_POPPropertyAnimationState::isDone()) {
      return true;
    }

    CGFloat f = dynamicsThreshold * kPOPAnimationDecayMinimalVelocityFactor;
    const CGFloat *velocityValues = vec_data(velocityVec);
    for (NSUInteger idx = 0; idx < valueCount; idx++) {
      if (std::abs((velocityValues[idx])) >= f)
        return false;
    }
    return true;

  }

  void computeDuration() {

    // compute duration till threshold velocity
    Vector4r scaledVelocity = vector4(velocityVec) / 1000.;

    double k = dynamicsThreshold * kPOPAnimationDecayMinimalVelocityFactor / 1000.;
    double vx = k / scaledVelocity.x;
    double vy = k / scaledVelocity.y;
    double vz = k / scaledVelocity.z;
    double vw = k / scaledVelocity.w;
    double d = log(deceleration) * 1000.;
    duration = MAX(MAX(MAX(log(fabs(vx)) / d, log(fabs(vy)) / d), log(fabs(vz)) / d), log(fabs(vw)) / d);

    // ensure velocity threshold is exceeded
    if (std::isnan(duration) || duration < 0) {
      duration = 0;
    }
  }

  void computeToValue() {
    // to value assuming final velocity as a factor of dynamics threshold
    // derived from v' = v * d^dt used in decay_position
    // to compute the to value with maximal dt, p' = p + (v * d) / (1 - d)
    VectorRef fromValue = NULL != currentVec ? currentVec : fromVec;
    if (!fromValue) {
      return;
    }

    // ensure duration is computed
    if (0 == duration) {
      computeDuration();
    }

    // compute to value
    VectorRef toValue(Vector::new_vector(fromValue.get()));
    Vector4r velocity = velocityVec->vector4r();
    decay_position(toValue->data(), velocity.data(), valueCount, duration, deceleration);
    toVec = toValue;
  }

  bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) {
    // advance past not yet initialized animations
    if (NULL == currentVec) {
      return false;
    }

    decay_position(currentVec->data(), velocityVec->data(), valueCount, dt, deceleration);

    // clamp to compute end value; avoid possibility of decaying past
    clampCurrentValue(kPOPAnimationClampEnd | clampMode);

    return true;
  }

};

typedef struct _POPDecayAnimationState POPDecayAnimationState;


================================================
FILE: pop/POPDefines.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#ifndef POP_POPDefines_h
#define POP_POPDefines_h

#import <Availability.h>

#ifdef __cplusplus
# define POP_EXTERN_C_BEGIN extern "C" {
# define POP_EXTERN_C_END   }
#else
# define POP_EXTERN_C_BEGIN
# define POP_EXTERN_C_END
#endif

#define POP_ARRAY_COUNT(x) sizeof(x) / sizeof(x[0])

#if defined (__cplusplus) && defined (__GNUC__)
# define POP_NOTHROW __attribute__ ((nothrow))
#else
# define POP_NOTHROW
#endif

#if defined(POP_USE_SCENEKIT)
# if TARGET_OS_MAC || TARGET_OS_IPHONE
#  define SCENEKIT_SDK_AVAILABLE 1
# endif
#endif

#endif


================================================
FILE: pop/POPGeometry.h
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import <Foundation/Foundation.h>

#if TARGET_OS_IPHONE
#import <UIKit/UIGeometry.h>
#endif

#if !TARGET_OS_IPHONE

/** NSValue extensions to support animatable types. */
@interface NSValue (POP)

/**
 @abstract Creates an NSValue given a CGPoint.
 */
+ (NSValue *)valueWithCGPoint:(CGPoint)point;

/**
 @abstract Creates an NSValue given a CGSize.
 */
+ (NSValue *)valueWithCGSize:(CGSize)size;

/**
 @abstract Creates an NSValue given a CGRect.
 */
+ (NSValue *)valueWithCGRect:(CGRect)rect;

/**
 @abstract Creates an NSValue given a CFRange.
 */
+ (NSValue *)valueWithCFRange:(CFRange)range;

/**
 @abstract Creates an NSValue given a CGAffineTransform.
 */
+ (NSValue *)valueWithCGAffineTransform:(CGAffineTransform)transform;

/**
 @abstract Returns the underlying CGPoint value.
 */
- (CGPoint)CGPointValue;

/**
 @abstract Returns the underlying CGSize value.
 */
- (CGSize)CGSizeValue;

/**
 @abstract Returns the underlying CGRect value.
 */
- (CGRect)CGRectValue;

/**
 @abstract Returns the underlying CFRange value.
 */
- (CFRange)CFRangeValue;

/**
 @abstract Returns the underlying CGAffineTransform value.
 */
- (CGAffineTransform)CGAffineTransformValue;

@end

#endif


================================================
FILE: pop/POPGeometry.mm
================================================
/**
 Copyright (c) 2014-present, Facebook, Inc.
 All rights reserved.
 
 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#import "POPGeometry.h"

#if !TARGET_OS_IPHONE
@implementation NSValue (POP)

+ (NSValue *)valueWithCGPoint:(CGPoint)point {
  return [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];
}

+ (NSValue *)valueWithCGSize:(CGSize)size {
  return [NSValue valueWithBytes:&size objCType:@encode(CGSize)];
}

+ (NSValue *)valueWithCGRect:(CGRect)rect {
  return [NSValue valueWithBytes:&rect objCType:@encode(CGRect)];
}

+ (NSValue *)valueWithCFRange:(CFRange)range {
  return [NSValue valueWithBytes:&range objCType:@encode(CFRange)];
}

+ (NSValue *)valueWithCGAffineTransform:(CGAffineTransform)transform
{
  return [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)];
}

- (CGPoint)CGPointValue {
  CGPoint result;
  [self getValue:&result];
  return result;
}

- (CGSize)CGSizeValue {
  CGSize result;
  [self getValue:&result];
  return result;
}

- (CGRect)CGRectValue {
  CGRect result;
  [self getValue:&result];
  return result;
}

- (CFRange)CFRangeValue {
  CFRange result;
  [self getValue:&result];
  return result;
}

- (CGAffineTransform)CGAffineTransformValue {
  CGAffineTransform result;
  [self getValue:&result];
  return result;
}
@end

#endif

#if TARGET_OS_IPHONE
#import "POPDefines.h"

#if SCENEKIT_SDK_AVAILABLE
#import <SceneKit/SceneKit.h>

/**
  Dirty hacks because iOS is weird and decided to define both SCNVector3's and SCNVector4's objCType as "t". However @encode(SCNVector3) and @encode(SCNVector4) both return the proper definition ("{SCNVector3=fff}" and "{SCNVector4=ffff}" respectively)
 
  [[NSValue valueWithSCNVector3:SCNVector3Make(0.0, 0.0, 0.0)] objcType] returns "t", whereas it should return "{SCNVector3=fff}".
 
  *flips table*
 */
@implementation NSValue (SceneKitFixes)

+ (NSValue *)valueWithSCNVector3:(SCNVector3)vec3 {
  return [NSValue valueWithBytes:&vec3 objCType:@encode(SCNVector3)];
}

+ (NSValue *)valueWithSCNVector4:(SCNVector4)vec4 {
  return [NSValue value
Download .txt
gitextract_0i4r22d7/

├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── Configuration/
│   ├── Compiler.xcconfig
│   ├── Product/
│   │   ├── ApplicationTests.xcconfig
│   │   ├── Framework.xcconfig
│   │   ├── LogicTests.xcconfig
│   │   └── StaticLibrary.xcconfig
│   └── Project/
│       ├── Project-Debug.xcconfig
│       ├── Project-Profile.xcconfig
│       ├── Project-Release.xcconfig
│       └── Project.xcconfig
├── LICENSE
├── PATENTS
├── Podfile
├── README.md
├── codecov.yml
├── pop/
│   ├── POP.h
│   ├── POPAction.h
│   ├── POPAnimatableProperty.h
│   ├── POPAnimatableProperty.mm
│   ├── POPAnimatablePropertyTypes.h
│   ├── POPAnimation.h
│   ├── POPAnimation.mm
│   ├── POPAnimationEvent.h
│   ├── POPAnimationEvent.mm
│   ├── POPAnimationEventInternal.h
│   ├── POPAnimationExtras.h
│   ├── POPAnimationExtras.mm
│   ├── POPAnimationInternal.h
│   ├── POPAnimationPrivate.h
│   ├── POPAnimationRuntime.h
│   ├── POPAnimationRuntime.mm
│   ├── POPAnimationTracer.h
│   ├── POPAnimationTracer.mm
│   ├── POPAnimationTracerInternal.h
│   ├── POPAnimator.h
│   ├── POPAnimator.mm
│   ├── POPAnimatorPrivate.h
│   ├── POPBasicAnimation.h
│   ├── POPBasicAnimation.mm
│   ├── POPBasicAnimationInternal.h
│   ├── POPCGUtils.h
│   ├── POPCGUtils.mm
│   ├── POPCustomAnimation.h
│   ├── POPCustomAnimation.mm
│   ├── POPDecayAnimation.h
│   ├── POPDecayAnimation.mm
│   ├── POPDecayAnimationInternal.h
│   ├── POPDefines.h
│   ├── POPGeometry.h
│   ├── POPGeometry.mm
│   ├── POPLayerExtras.h
│   ├── POPLayerExtras.mm
│   ├── POPMath.h
│   ├── POPMath.mm
│   ├── POPPropertyAnimation.h
│   ├── POPPropertyAnimation.mm
│   ├── POPPropertyAnimationInternal.h
│   ├── POPSpringAnimation.h
│   ├── POPSpringAnimation.mm
│   ├── POPSpringAnimationInternal.h
│   ├── POPSpringSolver.h
│   ├── POPVector.h
│   ├── POPVector.mm
│   ├── WebCore/
│   │   ├── FloatConversion.h
│   │   ├── TransformationMatrix.cpp
│   │   ├── TransformationMatrix.h
│   │   └── UnitBezier.h
│   ├── module.modulemap
│   ├── pop-ios-Info.plist
│   ├── pop-osx-Info.plist
│   └── pop-tvos-Info.plist
├── pop-tests/
│   ├── POPAnimatable.h
│   ├── POPAnimatable.mm
│   ├── POPAnimatablePropertyTests.mm
│   ├── POPAnimationMRRTests.mm
│   ├── POPAnimationTests.mm
│   ├── POPAnimationTestsExtras.h
│   ├── POPAnimationTestsExtras.mm
│   ├── POPBaseAnimationTests.h
│   ├── POPBaseAnimationTests.mm
│   ├── POPBasicAnimationTests.mm
│   ├── POPCustomAnimationTests.mm
│   ├── POPDecayAnimationTests.mm
│   ├── POPEaseInEaseOutAnimationTests.mm
│   ├── POPSpringAnimationTests.mm
│   ├── pop-tests-ios-Info.plist
│   ├── pop-tests-osx-Info.plist
│   └── pop-tests-tvos-Info.plist
├── pop.podspec
├── pop.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   └── contents.xcworkspacedata
│   └── xcshareddata/
│       └── xcschemes/
│           ├── pop-ios-framework.xcscheme
│           ├── pop-ios-static.xcscheme
│           ├── pop-osx-framework.xcscheme
│           └── pop-tvos-framework.xcscheme
└── pop.xcworkspace/
    ├── contents.xcworkspacedata
    └── xcshareddata/
        └── IDEWorkspaceChecks.plist
Download .txt
SYMBOL INDEX (219 symbols across 16 files)

FILE: pop-tests/POPBaseAnimationTests.h
  function interface (line 22) | interface POPBaseAnimationTests : XCTestCase

FILE: pop/POPAction.h
  function namespace (line 19) | namespace POP {
  function class (line 46) | class ActionEnabler

FILE: pop/POPAnimationInternal.h
  type POPAnimationType (line 25) | enum POPAnimationType
  type POPProgressMarker (line 33) | typedef struct
  function NSString (line 72) | NSString *describe(VectorConstRef vec)
  function NS_INLINE (line 77) | NS_INLINE Vector4r vector4(VectorConstRef vec)
  function NS_INLINE (line 82) | NS_INLINE Vector4d vector4d(VectorConstRef vec)
  function NS_INLINE (line 91) | NS_INLINE bool vec_equal(VectorConstRef v1, VectorConstRef v2)
  function NS_INLINE (line 102) | NS_INLINE CGFloat * vec_data(VectorRef vec)
  function CGFloat (line 109) | CGFloat operator()(const T &value, const T &start, const T &end) const {
  function Vector4r (line 115) | struct ComputeProgressFunctor<Vector4r> {
  type _POPAnimationState (line 133) | struct _POPAnimationState
  type _POPDecayAnimationState (line 134) | struct _POPDecayAnimationState
  type _POPPropertyAnimationState (line 135) | struct _POPPropertyAnimationState
  function end (line 195) | end
  function isCustom (line 278) | bool isCustom() {
  function isStarted (line 282) | bool isStarted() {
  function id (line 286) | id getDelegate() {
  function setDelegate (line 290) | void setDelegate(id d) {
  function getPaused (line 301) | bool getPaused() {
  function setPaused (line 305) | void setPaused(bool f) {
  function CGFloat (line 314) | CGFloat getProgress() {
  function startIfNeeded (line 319) | bool startIfNeeded(id obj, CFTimeInterval time, CFTimeInterval offset)
  function stop (line 349) | void stop(bool removing, bool done) {
  function virtual (line 375) | virtual void handleDidStart()
  function handleDidStop (line 393) | void handleDidStop(BOOL done)
  function virtual (line 413) | virtual bool isDone() {
  function advanceTime (line 421) | bool advanceTime(CFTimeInterval time, id obj) {
  function virtual (line 464) | virtual void willRun(bool started, id obj) {}
  function virtual (line 465) | virtual bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) { r...
  function virtual (line 466) | virtual void computeProgress() {}
  function virtual (line 467) | virtual void delegateProgress() {}
  function virtual (line 469) | virtual void delegateApply() {
  function virtual (line 482) | virtual void reset(bool all) {
  type POPAnimationState (line 488) | typedef struct _POPAnimationState POPAnimationState;
  function interface (line 491) | interface POPAnimation ()

FILE: pop/POPAnimationRuntime.h
  type POPValueType (line 18) | enum POPValueType
  function NS_INLINE (line 85) | NS_INLINE Vector4r read_values(POPAnimatablePropertyReadBlock read, id o...
  function NS_INLINE (line 96) | NS_INLINE NSString *POPStringFromBOOL(BOOL value)

FILE: pop/POPBasicAnimationInternal.h
  function interpolate (line 20) | static void interpolate(POPValueType valueType, NSUInteger count, const ...
  function _POPPropertyAnimationState (line 38) | struct _POPBasicAnimationState : _POPPropertyAnimationState
  function isDone (line 54) | bool isDone() {
  function updatedTimingFunction (line 61) | void updatedTimingFunction()
  function advance (line 71) | bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) {
  type POPBasicAnimationState (line 97) | typedef struct _POPBasicAnimationState POPBasicAnimationState;

FILE: pop/POPCGUtils.h
  function CGPoint (line 26) | CGPoint values_to_point(const CGFloat values[])
  function NS_INLINE (line 31) | NS_INLINE CGSize values_to_size(const CGFloat values[])
  function NS_INLINE (line 36) | NS_INLINE CGRect values_to_rect(const CGFloat values[])
  function NS_INLINE (line 42) | NS_INLINE SCNVector3 values_to_vec3(const CGFloat values[])
  function NS_INLINE (line 47) | NS_INLINE SCNVector4 values_to_vec4(const CGFloat values[])
  function NS_INLINE (line 55) | NS_INLINE UIEdgeInsets values_to_edge_insets(const CGFloat values[])
  function NS_INLINE (line 62) | NS_INLINE void values_from_point(CGFloat values[], CGPoint p)
  function NS_INLINE (line 68) | NS_INLINE void values_from_size(CGFloat values[], CGSize s)
  function NS_INLINE (line 74) | NS_INLINE void values_from_rect(CGFloat values[], CGRect r)
  function NS_INLINE (line 83) | NS_INLINE void values_from_vec3(CGFloat values[], SCNVector3 v)
  function NS_INLINE (line 90) | NS_INLINE void values_from_vec4(CGFloat values[], SCNVector4 v)
  function NS_INLINE (line 101) | NS_INLINE void values_from_edge_insets(CGFloat values[], UIEdgeInsets i)

FILE: pop/POPDecayAnimationInternal.h
  function decay_position (line 22) | static void decay_position(CGFloat *x, CGFloat *v, NSUInteger count, CFT...
  function _POPPropertyAnimationState (line 43) | struct _POPDecayAnimationState : _POPPropertyAnimationState
  function isDone (line 56) | bool isDone() {
  function computeDuration (line 71) | void computeDuration() {
  function computeToValue (line 90) | void computeToValue() {
  function advance (line 111) | bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) {
  type POPDecayAnimationState (line 127) | typedef struct _POPDecayAnimationState POPDecayAnimationState;

FILE: pop/POPMath.h
  function NS_INLINE (line 16) | NS_INLINE CGFloat sqrtr(CGFloat f)
  function NS_INLINE (line 26) | NS_INLINE CGFloat POPSubRound(CGFloat f, CGFloat sub)

FILE: pop/POPPropertyAnimationInternal.h
  function clampValue (line 13) | static void clampValue(CGFloat &value, CGFloat fromValue, CGFloat toValu...
  function _POPAnimationState (line 30) | struct _POPPropertyAnimationState : _POPAnimationState
  function canProgress (line 82) | bool canProgress() {
  function shouldRound (line 86) | bool shouldRound() {
  function hasValue (line 90) | bool hasValue() {
  function isDone (line 94) | bool isDone() {
  function VectorRef (line 109) | VectorRef currentValue() {
  function resetProgressMarkerState (line 117) | void resetProgressMarkerState()
  function updatedProgressMarkers (line 125) | void updatedProgressMarkers()
  function virtual (line 145) | virtual void updatedDynamicsThreshold()
  function finalizeProgress (line 150) | void finalizeProgress()
  function computeProgress (line 165) | void computeProgress() {
  function delegateProgress (line 177) | void delegateProgress() {
  function handleDidReachToValue (line 226) | void handleDidReachToValue() {
  function readObjectValue (line 245) | void readObjectValue(VectorRef *ptrVec, id obj)
  function virtual (line 260) | virtual void willRun(bool started, id obj) {
  function virtual (line 319) | virtual void reset(bool all) {
  function clampCurrentValue (line 333) | void clampCurrentValue(NSUInteger clamp)
  function clampCurrentValue (line 348) | void clampCurrentValue()
  type POPPropertyAnimationState (line 354) | typedef struct _POPPropertyAnimationState POPPropertyAnimationState;

FILE: pop/POPSpringAnimationInternal.h
  function _POPPropertyAnimationState (line 15) | struct _POPSpringAnimationState : _POPPropertyAnimationState
  function hasConverged (line 35) | bool hasConverged()
  function isDone (line 59) | bool isDone() {
  function updatedDynamics (line 66) | void updatedDynamics()
  function updatedDynamicsThreshold (line 73) | void updatedDynamicsThreshold()
  function updatedBouncinessAndSpeed (line 81) | void updatedBouncinessAndSpeed() {
  function advance (line 86) | bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) {
  function virtual (line 122) | virtual void reset(bool all) {
  type POPSpringAnimationState (line 132) | typedef struct _POPSpringAnimationState POPSpringAnimationState;

FILE: pop/POPSpringSolver.h
  function namespace (line 14) | namespace POP {
  type SpringSolver (line 186) | typedef SpringSolver<Vector2d> SpringSolver2d;
  type SpringSolver (line 187) | typedef SpringSolver<Vector3d> SpringSolver3d;
  type SpringSolver (line 188) | typedef SpringSolver<Vector4d> SpringSolver4d;

FILE: pop/POPVector.h
  function namespace (line 33) | namespace POP {
  function Vector2 (line 99) | Vector2 operator+ (const Vector2 &v) const { return Vector2(x + v.x, y +...
  function Vector2 (line 100) | Vector2 operator- (const Vector2 &v) const { return Vector2(x - v.x, y -...
  function Vector3 (line 130) | static const Vector3 Zero() { return Vector3(0); }
  function T (line 141) | const T& operator[](size_t i) const { return this->*_v[i]; }
  function T (line 143) | const T& operator()(size_t i) const { return this->*_v[i]; }
  function T (line 147) | T * data() { return &(this->*_v[0]); }
  function T (line 148) | const T * data() const { return &(this->*_v[0]); }
  function Vector3 (line 158) | Vector3 operator- (void) const { return Vector3<T>(-x, -y, -z); }
  function operator (line 161) | bool operator== (T v) const { return (x == v && y == v && z = v); }
  function operator (line 162) | bool operator== (const Vector3 &v) const { return (x == v.x && y == v.y ...
  function operator (line 165) | bool operator!= (T v) const {return (x != v || y != v || z != v); }
  function operator (line 166) | bool operator!= (const Vector3 &v) const { return (x != v.x || y != v.y ...
  function Vector3 (line 169) | Vector3 operator+ (T v) const { return Vector3(x + v, y + v, z + v); }
  function Vector3 (line 170) | Vector3 operator- (T v) const { return Vector3(x - v, y - v, z - v); }
  function Vector3 (line 171) | Vector3 operator* (T v) const { return Vector3(x * v, y * v, z * v); }
  function Vector3 (line 172) | Vector3 operator/ (T v) const { return Vector3(x / v, y / v, z / v); }
  function Vector3 (line 179) | Vector3 operator+ (const Vector3 &v) const { return Vector3(x + v.x, y +...
  function Vector3 (line 180) | Vector3 operator- (const Vector3 &v) const { return Vector3(x - v.x, y -...
  function Vector4 (line 210) | static const Vector4 Zero() { return Vector4(0); }
  function T (line 221) | const T& operator[](size_t i) const { return this->*_v[i]; }
  function T (line 223) | const T& operator()(size_t i) const { return this->*_v[i]; }
  function T (line 227) | T * data() { return &(this->*_v[0]); }
  function T (line 228) | const T * data() const { return &(this->*_v[0]); }
  function Vector4 (line 238) | Vector4 operator- (void) const { return Vector4<T>(-x, -y, -z, -w); }
  function operator (line 241) | bool operator== (T v) const { return (x == v && y == v && z = v, w = v); }
  function operator (line 242) | bool operator== (const Vector4 &v) const { return (x == v.x && y == v.y ...
  function operator (line 245) | bool operator!= (T v) const {return (x != v || y != v || z != v || w != ...
  function operator (line 246) | bool operator!= (const Vector4 &v) const { return (x != v.x || y != v.y ...
  function Vector4 (line 249) | Vector4 operator+ (T v) const { return Vector4(x + v, y + v, z + v, w + ...
  function Vector4 (line 250) | Vector4 operator- (T v) const { return Vector4(x - v, y - v, z - v, w - ...
  function Vector4 (line 251) | Vector4 operator* (T v) const { return Vector4(x * v, y * v, z * v, w * ...
  function Vector4 (line 252) | Vector4 operator/ (T v) const { return Vector4(x / v, y / v, z / v, w / ...
  function Vector4 (line 259) | Vector4 operator+ (const Vector4 &v) const { return Vector4(x + v.x, y +...
  function Vector4 (line 260) | Vector4 operator- (const Vector4 &v) const { return Vector4(x - v.x, y -...
  type Vector2 (line 276) | typedef Vector2<float> Vector2f;
  type Vector2 (line 277) | typedef Vector2<double> Vector2d;
  type Vector2 (line 278) | typedef Vector2<CGFloat> Vector2r;
  type Vector3 (line 279) | typedef Vector3<float> Vector3f;
  type Vector3 (line 280) | typedef Vector3<double> Vector3d;
  type Vector3 (line 281) | typedef Vector3<CGFloat> Vector3r;
  type Vector4 (line 282) | typedef Vector4<float> Vector4f;
  type Vector4 (line 283) | typedef Vector4<double> Vector4d;
  type Vector4 (line 284) | typedef Vector4<CGFloat> Vector4r;
  function class (line 287) | class Vector
  type std (line 391) | typedef std::shared_ptr<Vector> VectorRef;
  type std (line 392) | typedef std::shared_ptr<const Vector> VectorConstRef;

FILE: pop/WebCore/FloatConversion.h
  function namespace (line 34) | namespace WebCore {

FILE: pop/WebCore/TransformationMatrix.cpp
  function deg2rad (line 33) | inline double deg2rad(double d)  { return d * M_PI / 180.0; }
  function rad2deg (line 34) | inline double rad2deg(double r)  { return r * 180.0 / M_PI; }
  function deg2grad (line 35) | inline double deg2grad(double d) { return d * 400.0 / 360.0; }
  function grad2deg (line 36) | inline double grad2deg(double g) { return g * 360.0 / 400.0; }
  function turn2deg (line 37) | inline double turn2deg(double t) { return t * 360.0; }
  function deg2turn (line 38) | inline double deg2turn(double d) { return d / 360.0; }
  function rad2grad (line 39) | inline double rad2grad(double r) { return r * 200.0 / M_PI; }
  function grad2rad (line 40) | inline double grad2rad(double g) { return g * M_PI / 200.0; }
  type WebCore (line 44) | namespace WebCore {
    function determinant2x2 (line 92) | static double determinant2x2(double a, double b, double c, double d)
    function determinant3x3 (line 106) | static double determinant3x3(double a1, double a2, double a3, double b...
    function determinant4x4 (line 117) | static double determinant4x4(const TransformationMatrix::Matrix4& m)
    function adjoint (line 164) | static void adjoint(const TransformationMatrix::Matrix4& matrix, Trans...
    function inverse (line 211) | static bool inverse(const TransformationMatrix::Matrix4& matrix, Trans...
    function transposeMatrix4 (line 239) | static void transposeMatrix4(const TransformationMatrix::Matrix4& a, T...
    function v4MulPointByMatrix (line 247) | static void v4MulPointByMatrix(const Vector4 p, const TransformationMa...
    function v3Length (line 259) | static double v3Length(Vector3 a)
    function v3Scale (line 264) | static void v3Scale(Vector3 v, double desiredLength)
    function v3Dot (line 275) | static double v3Dot(const Vector3 a, const Vector3 b)
    function v3Combine (line 282) | static void v3Combine(const Vector3 a, const Vector3 b, Vector3 result...
    function v3Cross (line 290) | static void v3Cross(const Vector3 a, const Vector3 b, Vector3 result)
    function decompose (line 297) | static bool decompose(const TransformationMatrix::Matrix4& mat, Transf...
    function slerp (line 466) | static void slerp(double qa[4], const double qb[4], double t)
    function CATransform3D (line 528) | CATransform3D TransformationMatrix::transform3d() const
    function CGAffineTransform (line 550) | CGAffineTransform TransformationMatrix::affineTransform () const
    function TransformationMatrix (line 567) | TransformationMatrix& TransformationMatrix::scale(double s)
    function TransformationMatrix (line 572) | TransformationMatrix& TransformationMatrix::rotateFromVector(double x,...
    function TransformationMatrix (line 577) | TransformationMatrix& TransformationMatrix::flipX()
    function TransformationMatrix (line 582) | TransformationMatrix& TransformationMatrix::flipY()
    function TransformationMatrix (line 587) | TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx,...
    function TransformationMatrix (line 601) | TransformationMatrix& TransformationMatrix::scale3d(double sx, double ...
    function TransformationMatrix (line 612) | TransformationMatrix& TransformationMatrix::rotate3d(double x, double ...
    function TransformationMatrix (line 699) | TransformationMatrix& TransformationMatrix::rotate3d(double rx, double...
    function TransformationMatrix (line 766) | TransformationMatrix& TransformationMatrix::translate(double tx, doubl...
    function TransformationMatrix (line 775) | TransformationMatrix& TransformationMatrix::translate3d(double tx, dou...
    function TransformationMatrix (line 784) | TransformationMatrix& TransformationMatrix::translateRight(double tx, ...
    function TransformationMatrix (line 803) | TransformationMatrix& TransformationMatrix::translateRight3d(double tx...
    function TransformationMatrix (line 816) | TransformationMatrix& TransformationMatrix::skew(double sx, double sy)
    function TransformationMatrix (line 830) | TransformationMatrix& TransformationMatrix::applyPerspective(double p)
    function TransformationMatrix (line 841) | TransformationMatrix& TransformationMatrix::multiply(const Transformat...
    function TransformationMatrix (line 922) | TransformationMatrix TransformationMatrix::inverse() const
    function blendFloat (line 961) | static inline void blendFloat(double& from, double to, double progress)

FILE: pop/WebCore/TransformationMatrix.h
  function namespace (line 35) | namespace WebCore {
  function setMatrix (line 53) | void setMatrix(double a, double b, double c, double d, double e, double f)
  function setMatrix (line 61) | void setMatrix(double m11, double m12, double m13, double m14,
  function map (line 93) | void map(double x, double y, double& x2, double& y2) const { multVecMatr...
  function setM11 (line 96) | void setM11(double f) { m_matrix[0][0] = f; }
  function setM12 (line 98) | void setM12(double f) { m_matrix[0][1] = f; }
  function setM13 (line 100) | void setM13(double f) { m_matrix[0][2] = f; }
  function setM14 (line 102) | void setM14(double f) { m_matrix[0][3] = f; }
  function setM21 (line 104) | void setM21(double f) { m_matrix[1][0] = f; }
  function setM22 (line 106) | void setM22(double f) { m_matrix[1][1] = f; }
  function setM23 (line 108) | void setM23(double f) { m_matrix[1][2] = f; }
  function setM24 (line 110) | void setM24(double f) { m_matrix[1][3] = f; }
  function setM31 (line 112) | void setM31(double f) { m_matrix[2][0] = f; }
  function setM32 (line 114) | void setM32(double f) { m_matrix[2][1] = f; }
  function setM33 (line 116) | void setM33(double f) { m_matrix[2][2] = f; }
  function setM34 (line 118) | void setM34(double f) { m_matrix[2][3] = f; }
  function setM41 (line 120) | void setM41(double f) { m_matrix[3][0] = f; }
  function setM42 (line 122) | void setM42(double f) { m_matrix[3][1] = f; }
  function setM43 (line 124) | void setM43(double f) { m_matrix[3][2] = f; }
  function setM44 (line 126) | void setM44(double f) { m_matrix[3][3] = f; }
  function setA (line 129) | void setA(double a) { m_matrix[0][0] = a; }
  function setB (line 132) | void setB(double b) { m_matrix[0][1] = b; }
  function setC (line 135) | void setC(double c) { m_matrix[1][0] = c; }
  function setD (line 138) | void setD(double d) { m_matrix[1][1] = d; }
  function setE (line 141) | void setE(double e) { m_matrix[3][0] = e; }
  function setF (line 144) | void setF(double f) { m_matrix[3][1] = f; }
  function isInvertible (line 177) | bool isInvertible() const;
  function CATransform3D (line 243) | CATransform3D transform3d () const;

FILE: pop/WebCore/UnitBezier.h
  function namespace (line 31) | namespace WebCore {
Condensed preview — 99 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (614K chars).
[
  {
    "path": ".gitignore",
    "chars": 177,
    "preview": ".DS_Store\n\n*.pbxuser\n*.perspective\n*.perspectivev3\n\n*.mode1v3\n*.mode2v3\n\n*.xcodeproj/xcuserdata/*.xcuserdatad\n\n*.xccheck"
  },
  {
    "path": ".travis.yml",
    "chars": 1520,
    "preview": "branches:\n  only:\n    - master\nlanguage: objective-c\nos: osx\nosx_image: xcode9.2\nenv:\n  matrix:\n    - TEST_TYPE=iOS\n    "
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1083,
    "preview": "# Contributing\nWe want to make contributing to Pop as easy and transparent as\npossible. If you run into problems, please"
  },
  {
    "path": "Configuration/Compiler.xcconfig",
    "chars": 2330,
    "preview": "//\n// Copyright (c) 2014-present, Facebook, Inc.\n// All rights reserved.\n//\n// This source code is licensed under the BS"
  },
  {
    "path": "Configuration/Product/ApplicationTests.xcconfig",
    "chars": 492,
    "preview": "// \n// Copyright (c) 2014-present, Facebook, Inc.\n// All rights reserved.\n// \n// This source code is licensed under the "
  },
  {
    "path": "Configuration/Product/Framework.xcconfig",
    "chars": 438,
    "preview": "//\n// Copyright (c) 2014-present, Facebook, Inc.\n// All rights reserved.\n//\n// This source code is licensed under the BS"
  },
  {
    "path": "Configuration/Product/LogicTests.xcconfig",
    "chars": 407,
    "preview": "// \n// Copyright (c) 2014-present, Facebook, Inc.\n// All rights reserved.\n// \n// This source code is licensed under the "
  },
  {
    "path": "Configuration/Product/StaticLibrary.xcconfig",
    "chars": 657,
    "preview": "// \n// Copyright (c) 2014-present, Facebook, Inc.\n// All rights reserved.\n// \n// This source code is licensed under the "
  },
  {
    "path": "Configuration/Project/Project-Debug.xcconfig",
    "chars": 659,
    "preview": "// \n// Copyright (c) 2014-present, Facebook, Inc.\n// All rights reserved.\n// \n// This source code is licensed under the "
  },
  {
    "path": "Configuration/Project/Project-Profile.xcconfig",
    "chars": 884,
    "preview": "// \n// Copyright (c) 2014-present, Facebook, Inc.\n// All rights reserved.\n// \n// This source code is licensed under the "
  },
  {
    "path": "Configuration/Project/Project-Release.xcconfig",
    "chars": 620,
    "preview": "// \n// Copyright (c) 2014-present, Facebook, Inc.\n// All rights reserved.\n// \n// This source code is licensed under the "
  },
  {
    "path": "Configuration/Project/Project.xcconfig",
    "chars": 412,
    "preview": "// \n// Copyright (c) 2014-present, Facebook, Inc.\n// All rights reserved.\n// \n// This source code is licensed under the "
  },
  {
    "path": "LICENSE",
    "chars": 1516,
    "preview": "BSD License\n\nFor Pop software\n\nCopyright (c) 2014, Facebook, Inc. All rights reserved.\n\nRedistribution and use in source"
  },
  {
    "path": "PATENTS",
    "chars": 1972,
    "preview": "Additional Grant of Patent Rights Version 2\n\n\"Software\" means the Pop software distributed by Facebook, Inc.\n\nFacebook, "
  },
  {
    "path": "Podfile",
    "chars": 244,
    "preview": "abstract_target 'Tests' do\n  pod 'OCMock', '~> 2.2'\n  \n  target :'pop-tests-ios' do\n    platform :ios, '6.0'\n  end\n\n  ta"
  },
  {
    "path": "README.md",
    "chars": 12119,
    "preview": "![pop](https://github.com/facebook/pop/blob/master/Images/pop.gif?raw=true)\n\nPop is an extensible animation engine for i"
  },
  {
    "path": "codecov.yml",
    "chars": 146,
    "preview": "coverage:\n  ignore:\n    - pop-tests/*\n  status:\n    patch: false\n    changes: false\n    project:\n      default:\n        "
  },
  {
    "path": "pop/POP.h",
    "chars": 864,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAction.h",
    "chars": 1365,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimatableProperty.h",
    "chars": 8094,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimatableProperty.mm",
    "chars": 35929,
    "preview": "/**\n  Copyright (c) 2014-present, Facebook, Inc.\n  All rights reserved.\n\n  This source code is licensed under the BSD-st"
  },
  {
    "path": "pop/POPAnimatablePropertyTypes.h",
    "chars": 453,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPAnimation.h",
    "chars": 7072,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimation.mm",
    "chars": 7333,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationEvent.h",
    "chars": 1771,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationEvent.mm",
    "chars": 2744,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationEventInternal.h",
    "chars": 965,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationExtras.h",
    "chars": 1418,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationExtras.mm",
    "chars": 4299,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationInternal.h",
    "chars": 12026,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPAnimationPrivate.h",
    "chars": 776,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationRuntime.h",
    "chars": 2473,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationRuntime.mm",
    "chars": 9125,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationTracer.h",
    "chars": 1560,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationTracer.mm",
    "chars": 4872,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimationTracerInternal.h",
    "chars": 1704,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimator.h",
    "chars": 1459,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimator.mm",
    "chars": 23825,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPAnimatorPrivate.h",
    "chars": 2139,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPBasicAnimation.h",
    "chars": 2112,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPBasicAnimation.mm",
    "chars": 2636,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPBasicAnimationInternal.h",
    "chars": 2831,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPCGUtils.h",
    "chars": 3317,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPCGUtils.mm",
    "chars": 4267,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPCustomAnimation.h",
    "chars": 1807,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPCustomAnimation.mm",
    "chars": 1740,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPDecayAnimation.h",
    "chars": 2262,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPDecayAnimation.mm",
    "chars": 5967,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPDecayAnimationInternal.h",
    "chars": 3726,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPDefines.h",
    "chars": 844,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPGeometry.h",
    "chars": 1484,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPGeometry.mm",
    "chars": 2312,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPLayerExtras.h",
    "chars": 4750,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPLayerExtras.mm",
    "chars": 5580,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPMath.h",
    "chars": 1792,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPMath.mm",
    "chars": 2133,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPPropertyAnimation.h",
    "chars": 2770,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPPropertyAnimation.mm",
    "chars": 4389,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPPropertyAnimationInternal.h",
    "chars": 9017,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPSpringAnimation.h",
    "chars": 2611,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPSpringAnimation.mm",
    "chars": 4762,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPSpringAnimationInternal.h",
    "chars": 3474,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPSpringSolver.h",
    "chars": 4740,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop/POPVector.h",
    "chars": 13321,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/POPVector.mm",
    "chars": 7211,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop/WebCore/FloatConversion.h",
    "chars": 2117,
    "preview": "/*\n * Copyright (C) 2007 Apple Inc.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with "
  },
  {
    "path": "pop/WebCore/TransformationMatrix.cpp",
    "chars": 37697,
    "preview": "/*\n * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.\n * Copyright (C) 2009 Torch Mobile, Inc.\n *\n *"
  },
  {
    "path": "pop/WebCore/TransformationMatrix.h",
    "chars": 11447,
    "preview": "/*\n * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.\n *\n * Redistribution and use in source and bin"
  },
  {
    "path": "pop/WebCore/UnitBezier.h",
    "chars": 3426,
    "preview": "/*\n * Copyright (C) 2008 Apple Inc. All Rights Reserved.\n *\n * Redistribution and use in source and binary forms, with o"
  },
  {
    "path": "pop/module.modulemap",
    "chars": 143,
    "preview": "framework module pop {\n    umbrella header \"POP.h\"\n    \n    exclude header \"POPAnimationPrivate.h\"\n    exclude header \"P"
  },
  {
    "path": "pop/pop-ios-Info.plist",
    "chars": 927,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "pop/pop-osx-Info.plist",
    "chars": 955,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "pop/pop-tvos-Info.plist",
    "chars": 824,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "pop-tests/POPAnimatable.h",
    "chars": 645,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPAnimatable.mm",
    "chars": 1567,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPAnimatablePropertyTests.mm",
    "chars": 4817,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPAnimationMRRTests.mm",
    "chars": 2056,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPAnimationTests.mm",
    "chars": 35557,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPAnimationTestsExtras.h",
    "chars": 900,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPAnimationTestsExtras.mm",
    "chars": 1719,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPBaseAnimationTests.h",
    "chars": 2147,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPBaseAnimationTests.mm",
    "chars": 6157,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPBasicAnimationTests.mm",
    "chars": 5474,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n\n This source code is licensed under the BSD-style"
  },
  {
    "path": "pop-tests/POPCustomAnimationTests.mm",
    "chars": 5519,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPDecayAnimationTests.mm",
    "chars": 20684,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPEaseInEaseOutAnimationTests.mm",
    "chars": 2841,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/POPSpringAnimationTests.mm",
    "chars": 25273,
    "preview": "/**\n Copyright (c) 2014-present, Facebook, Inc.\n All rights reserved.\n \n This source code is licensed under the BSD-styl"
  },
  {
    "path": "pop-tests/pop-tests-ios-Info.plist",
    "chars": 674,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "pop-tests/pop-tests-osx-Info.plist",
    "chars": 674,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "pop-tests/pop-tests-tvos-Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "pop.podspec",
    "chars": 1120,
    "preview": "Pod::Spec.new do |spec|\n  spec.name         = 'pop'\n  spec.version      = '1.0.11'\n  spec.license      =  { :type => 'BS"
  },
  {
    "path": "pop.xcodeproj/project.pbxproj",
    "chars": 131954,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "pop.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 148,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:pop.xcodeproj\">"
  },
  {
    "path": "pop.xcodeproj/xcshareddata/xcschemes/pop-ios-framework.xcscheme",
    "chars": 3645,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1000\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "pop.xcodeproj/xcshareddata/xcschemes/pop-ios-static.xcscheme",
    "chars": 4190,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1000\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "pop.xcodeproj/xcshareddata/xcschemes/pop-osx-framework.xcscheme",
    "chars": 2915,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1000\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "pop.xcodeproj/xcshareddata/xcschemes/pop-tvos-framework.xcscheme",
    "chars": 3650,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1000\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "pop.xcworkspace/contents.xcworkspacedata",
    "chars": 221,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:pop.xcodeproj\""
  },
  {
    "path": "pop.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  }
]

About this extraction

This page contains the full source code of the facebookarchive/pop GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 99 files (569.2 KB), approximately 165.8k tokens, and a symbol index with 219 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!