Browse Source

Add Pop!

v1.2.4
Abhishek Banthia 8 years ago
parent
commit
389e044f37
  1. BIN
      Clocker.xcodeproj/project.xcworkspace/xcuserdata/abhishekbanthia.xcuserdatad/UserInterfaceState.xcuserstate
  2. 20
      Clocker/pop/.gitignore
  3. 39
      Clocker/pop/.travis.yml
  4. 28
      Clocker/pop/CONTRIBUTING.md
  5. 12
      Clocker/pop/Configuration/Base-OSX.xcconfig
  6. 12
      Clocker/pop/Configuration/Base-iOS.xcconfig
  7. 12
      Clocker/pop/Configuration/Frameworks/Framework-iOS.xcconfig
  8. 39
      Clocker/pop/Configuration/Platform/Compiler.xcconfig
  9. 18
      Clocker/pop/Configuration/Platform/OSX.xcconfig
  10. 18
      Clocker/pop/Configuration/Platform/iOS.xcconfig
  11. 25
      Clocker/pop/Configuration/Projects/Project-Debug.xcconfig
  12. 31
      Clocker/pop/Configuration/Projects/Project-Profile.xcconfig
  13. 25
      Clocker/pop/Configuration/Projects/Project-Release.xcconfig
  14. 46
      Clocker/pop/Configuration/Projects/Project.xcconfig
  15. 27
      Clocker/pop/Configuration/Static Libraries/StaticLibrary-OSX.xcconfig
  16. 27
      Clocker/pop/Configuration/Static Libraries/StaticLibrary-iOS.xcconfig
  17. 16
      Clocker/pop/Configuration/Tests/ApplicationTests.xcconfig
  18. 23
      Clocker/pop/Configuration/Tests/LogicTests-OSX.xcconfig
  19. 26
      Clocker/pop/Configuration/Tests/LogicTests-iOS.xcconfig
  20. BIN
      Clocker/pop/Images/EmbeddedBinaries.png
  21. BIN
      Clocker/pop/Images/pop.gif
  22. BIN
      Clocker/pop/Images/pop.png
  23. 30
      Clocker/pop/LICENSE
  24. 33
      Clocker/pop/PATENTS
  25. 39
      Clocker/pop/Podfile
  26. 10
      Clocker/pop/Podfile.lock
  27. 201
      Clocker/pop/README.md
  28. 28
      Clocker/pop/pop-tests/POPAnimatable.h
  29. 76
      Clocker/pop/pop-tests/POPAnimatable.mm
  30. 142
      Clocker/pop/pop-tests/POPAnimatablePropertyTests.mm
  31. 91
      Clocker/pop/pop-tests/POPAnimationMRRTests.mm
  32. 891
      Clocker/pop/pop-tests/POPAnimationTests.mm
  33. 19
      Clocker/pop/pop-tests/POPAnimationTestsExtras.h
  34. 54
      Clocker/pop/pop-tests/POPAnimationTestsExtras.mm
  35. 63
      Clocker/pop/pop-tests/POPBaseAnimationTests.h
  36. 147
      Clocker/pop/pop-tests/POPBaseAnimationTests.mm
  37. 162
      Clocker/pop/pop-tests/POPBasicAnimationTests.mm
  38. 169
      Clocker/pop/pop-tests/POPCustomAnimationTests.mm
  39. 544
      Clocker/pop/pop-tests/POPDecayAnimationTests.mm
  40. 92
      Clocker/pop/pop-tests/POPEaseInEaseOutAnimationTests.mm
  41. 687
      Clocker/pop/pop-tests/POPSpringAnimationTests.mm
  42. 22
      Clocker/pop/pop-tests/pop-tests-ios-Info.plist
  43. 22
      Clocker/pop/pop-tests/pop-tests-osx-Info.plist
  44. 24
      Clocker/pop/pop-tests/pop-tests-tvos-Info.plist
  45. 21
      Clocker/pop/pop.podspec
  46. 2071
      Clocker/pop/pop.xcodeproj/project.pbxproj
  47. 7
      Clocker/pop/pop.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  48. 99
      Clocker/pop/pop.xcodeproj/xcshareddata/xcschemes/pop-ios-framework.xcscheme
  49. 110
      Clocker/pop/pop.xcodeproj/xcshareddata/xcschemes/pop-ios-static.xcscheme
  50. 78
      Clocker/pop/pop.xcodeproj/xcshareddata/xcschemes/pop-osx-framework.xcscheme
  51. 99
      Clocker/pop/pop.xcodeproj/xcshareddata/xcschemes/pop-tvos-framework.xcscheme
  52. 10
      Clocker/pop/pop.xcworkspace/contents.xcworkspacedata
  53. 29
      Clocker/pop/pop/POP.h
  54. 67
      Clocker/pop/pop/POPAction.h
  55. 251
      Clocker/pop/pop/POPAnimatableProperty.h
  56. 1307
      Clocker/pop/pop/POPAnimatableProperty.mm
  57. 188
      Clocker/pop/pop/POPAnimation.h
  58. 303
      Clocker/pop/pop/POPAnimation.mm
  59. 69
      Clocker/pop/pop/POPAnimationEvent.h
  60. 108
      Clocker/pop/pop/POPAnimationEvent.mm
  61. 41
      Clocker/pop/pop/POPAnimationEventInternal.h
  62. 43
      Clocker/pop/pop/POPAnimationExtras.h
  63. 117
      Clocker/pop/pop/POPAnimationExtras.mm
  64. 505
      Clocker/pop/pop/POPAnimationInternal.h
  65. 16
      Clocker/pop/pop/POPAnimationPrivate.h
  66. 103
      Clocker/pop/pop/POPAnimationRuntime.h
  67. 329
      Clocker/pop/pop/POPAnimationRuntime.mm
  68. 60
      Clocker/pop/pop/POPAnimationTracer.h
  69. 191
      Clocker/pop/pop/POPAnimationTracer.mm
  70. 96
      Clocker/pop/pop/POPAnimationTracerInternal.h
  71. 47
      Clocker/pop/pop/POPAnimator.h
  72. 806
      Clocker/pop/pop/POPAnimator.mm
  73. 68
      Clocker/pop/pop/POPAnimatorPrivate.h
  74. 71
      Clocker/pop/pop/POPBasicAnimation.h
  75. 106
      Clocker/pop/pop/POPBasicAnimation.mm
  76. 97
      Clocker/pop/pop/POPBasicAnimationInternal.h
  77. 152
      Clocker/pop/pop/POPCGUtils.h
  78. 150
      Clocker/pop/pop/POPCGUtils.mm
  79. 46
      Clocker/pop/pop/POPCustomAnimation.h
  80. 75
      Clocker/pop/pop/POPCustomAnimation.mm
  81. 66
      Clocker/pop/pop/POPDecayAnimation.h
  82. 203
      Clocker/pop/pop/POPDecayAnimation.mm
  83. 127
      Clocker/pop/pop/POPDecayAnimationInternal.h
  84. 37
      Clocker/pop/pop/POPDefines.h
  85. 73
      Clocker/pop/pop/POPGeometry.h
  86. 94
      Clocker/pop/pop/POPGeometry.mm
  87. 196
      Clocker/pop/pop/POPLayerExtras.h
  88. 288
      Clocker/pop/pop/POPLayerExtras.mm
  89. 56
      Clocker/pop/pop/POPMath.h
  90. 83
      Clocker/pop/pop/POPMath.mm
  91. 65
      Clocker/pop/pop/POPPropertyAnimation.h
  92. 125
      Clocker/pop/pop/POPPropertyAnimation.mm
  93. 359
      Clocker/pop/pop/POPPropertyAnimationInternal.h
  94. 67
      Clocker/pop/pop/POPSpringAnimation.h
  95. 192
      Clocker/pop/pop/POPSpringAnimation.mm
  96. 132
      Clocker/pop/pop/POPSpringAnimationInternal.h
  97. 190
      Clocker/pop/pop/POPSpringSolver.h
  98. 394
      Clocker/pop/pop/POPVector.h
  99. 334
      Clocker/pop/pop/POPVector.mm
  100. 56
      Clocker/pop/pop/WebCore/FloatConversion.h
  101. Some files were not shown because too many files have changed in this diff Show More

BIN
Clocker.xcodeproj/project.xcworkspace/xcuserdata/abhishekbanthia.xcuserdatad/UserInterfaceState.xcuserstate generated

Binary file not shown.

20
Clocker/pop/.gitignore vendored

@ -0,0 +1,20 @@
.DS_Store
*.pbxuser
*.perspective
*.perspectivev3
*.mode1v3
*.mode2v3
*.xcodeproj/xcuserdata/*.xcuserdatad
*.xccheckout
*.xcuserdatad
Pods
DerivedData
build
.ruby-version

39
Clocker/pop/.travis.yml

@ -0,0 +1,39 @@
branches:
only:
- master
language: objective-c
os: osx
osx_image: xcode7.3
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 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 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

28
Clocker/pop/CONTRIBUTING.md

@ -0,0 +1,28 @@
# 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.

12
Clocker/pop/Configuration/Base-OSX.xcconfig

@ -0,0 +1,12 @@
//
// 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.
//
// Base xcconfig for OS X targets.
#include "Platform/OSX.xcconfig"
#include "Platform/Compiler.xcconfig"

12
Clocker/pop/Configuration/Base-iOS.xcconfig

@ -0,0 +1,12 @@
//
// 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.
//
// Base xcconfig for iOS targets.
#include "Platform/iOS.xcconfig"
#include "Platform/Compiler.xcconfig"

12
Clocker/pop/Configuration/Frameworks/Framework-iOS.xcconfig

@ -0,0 +1,12 @@
//
// 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 "../Base-iOS.xcconfig"
IPHONEOS_DEPLOYMENT_TARGET = 8.0

39
Clocker/pop/Configuration/Platform/Compiler.xcconfig

@ -0,0 +1,39 @@
//
// 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.
//
// Don't warn on implicit property synthesis
CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES
// Treat warnings as errors
GCC_TREAT_WARNINGS_AS_ERRORS = YES
CLANG_WARN_CONSTANT_CONVERSION = YES
CLANG_WARN_ENUM_CONVERSION = YES
CLANG_WARN_INT_CONVERSION = YES
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
CLANG_WARN_BOOL_CONVERSION = YES
CLANG_WARN_EMPTY_BODY = YES
GCC_WARN_UNINITIALIZED_AUTOS = YES
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO
GCC_WARN_UNUSED_FUNCTION = YES
GCC_WARN_SHADOW = YES
GCC_WARN_SIGN_COMPARE = YES
WARNING_CFLAGS = -Wall -Wextra -Wno-unused-parameter
// Enable C++11 support
CLANG_CXX_LANGUAGE_STANDARD = c++11
CLANG_CXX_LIBRARY = libc++
// Enable ARC
CLANG_ENABLE_OBJC_ARC = YES
// Explicitly enable Clang modules
CLANG_ENABLE_MODULES=YES
// Allow #import'ing code generated headers from DERIVED_FILE_DIR.
HEADER_SEARCH_PATHS = $(inherited) $(DERIVED_FILE_DIR)

18
Clocker/pop/Configuration/Platform/OSX.xcconfig

@ -0,0 +1,18 @@
//
// 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.
//
// OS X Platform
SDKROOT = macosx
SUPPORTED_PLATFORMS = macosx
// Standard Architectures
ARCHS = $(ARCHS_STANDARD)
// Limit to OS X 64-bit (x86_64)
VALID_ARCHS = x86_64

18
Clocker/pop/Configuration/Platform/iOS.xcconfig

@ -0,0 +1,18 @@
//
// 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.
//
// iOS Platform
SDKROOT = iphoneos
IPHONEOS_DEPLOYMENT_TARGET = 6.0
// Standard Architectures, including 64-bit
ARCHS = $(ARCHS_STANDARD_INCLUDING_64_BIT)
// Code Signing
CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer

25
Clocker/pop/Configuration/Projects/Project-Debug.xcconfig

@ -0,0 +1,25 @@
//
// 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 = 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

31
Clocker/pop/Configuration/Projects/Project-Profile.xcconfig

@ -0,0 +1,31 @@
//
// 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)

25
Clocker/pop/Configuration/Projects/Project-Release.xcconfig

@ -0,0 +1,25 @@
//
// 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

46
Clocker/pop/Configuration/Projects/Project.xcconfig

@ -0,0 +1,46 @@
//
// 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 "../Base-iOS.xcconfig"
SDKROOT = iphoneos
// Build Options
GCC_VERSION = com.apple.compilers.llvm.clang.1_0
// Deployment
TARGETED_DEVICE_FAMILY = 1,2 // iPhone, iPad
// Packaging
PRODUCT_NAME = $(TARGET_NAME)
INFOPLIST_FILE = $(TARGET_NAME)/$(TARGET_NAME)-Info.plist
// Search Paths
ALWAYS_SEARCH_USER_PATHS = NO
HEADER_SEARCH_PATHS = $(SYMROOT)/Headers
FRAMEWORK_SEARCH_PATHS = $(inherited) $(BUILT_PRODUCTS_DIR)
LIBRARY_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR) $(inherited)
// Code Generation
GCC_DYNAMIC_NO_PIC = NO
GCC_INLINES_ARE_PRIVATE_EXTERN = YES
GCC_SYMBOLS_PRIVATE_EXTERN = NO
// Language
GCC_C_LANGUAGE_STANDARD = gnu99
CLANG_ENABLE_OBJC_ARC = NO
// Warnings
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
GCC_WARN_MISSING_PARENTHESES = YES
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
GCC_WARN_SHADOW = YES
GCC_WARN_UNUSED_VARIABLE = YES
CLANG_WARN_CXX0X_EXTENSIONS = NO

27
Clocker/pop/Configuration/Static Libraries/StaticLibrary-OSX.xcconfig

@ -0,0 +1,27 @@
//
// 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 "../Base-OSX.xcconfig"
// Deployment
DSTROOT = /tmp/$(TARGET_NAME)
INSTALL_PATH = $(SYMROOT)/Headers
SKIP_INSTALL = YES
// Packaging: Put headers in $(SYMROOT) instead of $(CONFIGURATION_BUILD_DIR)
// so header paths across projects don't depend on all projects having an
// identical configuration name.
//
// Note: PUBLIC_HEADERS_FOLDER_PATH is directly appended to $(CONFIGURATION_BUILD_DIR),
// so the path is relative to that.
PUBLIC_HEADERS_FOLDER_PATH = ../Headers/$(TARGET_NAME)
// declare inlines as extern
GCC_INLINES_ARE_PRIVATE_EXTERN = YES

27
Clocker/pop/Configuration/Static Libraries/StaticLibrary-iOS.xcconfig

@ -0,0 +1,27 @@
//
// 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 "../Base-iOS.xcconfig"
// Deployment
DSTROOT = /tmp/$(TARGET_NAME)
INSTALL_PATH = $(SYMROOT)/Headers
SKIP_INSTALL = YES
// Packaging: Put headers in $(SYMROOT) instead of $(CONFIGURATION_BUILD_DIR)
// so header paths across projects don't depend on all projects having an
// identical configuration name.
//
// Note: PUBLIC_HEADERS_FOLDER_PATH is directly appended to $(CONFIGURATION_BUILD_DIR),
// so the path is relative to that.
PUBLIC_HEADERS_FOLDER_PATH = ../Headers/$(TARGET_NAME)
// declare inlines as extern
GCC_INLINES_ARE_PRIVATE_EXTERN = YES

16
Clocker/pop/Configuration/Tests/ApplicationTests.xcconfig

@ -0,0 +1,16 @@
//
// 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)"

23
Clocker/pop/Configuration/Tests/LogicTests-OSX.xcconfig

@ -0,0 +1,23 @@
//
// 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 "../Base-OSX.xcconfig"
// Deployment
DSTROOT = /tmp/$(TARGET_NAME)
SKIP_INSTALL = YES
// Linking
OTHER_LDFLAGS = -framework XCTest -all_load -lc++
// Packaging
WRAPPER_EXTENSION = xctest
// Search Paths
FRAMEWORK_SEARCH_PATHS = $(PLATFORM_DIR)/Developer/Library/Frameworks $(DEVELOPER_FRAMEWORKS_DIR)

26
Clocker/pop/Configuration/Tests/LogicTests-iOS.xcconfig

@ -0,0 +1,26 @@
//
// 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 "../Base-iOS.xcconfig"
// Code Signing
CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer
// Deployment
DSTROOT = /tmp/$(TARGET_NAME)
SKIP_INSTALL = YES
// Linking
OTHER_LDFLAGS = -framework XCTest -all_load -lc++
// Packaging
WRAPPER_EXTENSION = xctest
// Search Paths
FRAMEWORK_SEARCH_PATHS = $(SDKROOT)/Developer/Library/Frameworks $(DEVELOPER_FRAMEWORKS_DIR)

BIN
Clocker/pop/Images/EmbeddedBinaries.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
Clocker/pop/Images/pop.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
Clocker/pop/Images/pop.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

30
Clocker/pop/LICENSE

@ -0,0 +1,30 @@
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.

33
Clocker/pop/PATENTS

@ -0,0 +1,33 @@
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.

39
Clocker/pop/Podfile

@ -0,0 +1,39 @@
platform :ios, '6.0'
target :'pop-tests-ios', :exclusive => true do
pod 'OCMock', '~> 2.2'
end
target :'pop-tests-tvos', :exclusive => true do
platform :tvos, "9.0"
pod 'OCMock', '~> 2.2'
end
target :'pop-tests-osx', :exclusive => true do
platform :osx, '10.8'
pod 'OCMock', '~> 2.2'
end
# Add XCTests to generated xcconfigs
post_install do
pop_test_xcconfigs = [
"./Pods/Target Support Files/Pods-pop-tests-ios/Pods-pop-tests-ios.debug.xcconfig",
"./Pods/Target Support Files/Pods-pop-tests-ios/Pods-pop-tests-ios.profile.xcconfig",
"./Pods/Target Support Files/Pods-pop-tests-ios/Pods-pop-tests-ios.release.xcconfig",
"./Pods/Target Support Files/Pods-pop-tests-tvos/Pods-pop-tests-tvos.debug.xcconfig",
"./Pods/Target Support Files/Pods-pop-tests-tvos/Pods-pop-tests-tvos.profile.xcconfig",
"./Pods/Target Support Files/Pods-pop-tests-tvos/Pods-pop-tests-tvos.release.xcconfig",
"./Pods/Target Support Files/Pods-pop-tests-osx/Pods-pop-tests-osx.debug.xcconfig",
"./Pods/Target Support Files/Pods-pop-tests-osx/Pods-pop-tests-osx.profile.xcconfig",
"./Pods/Target Support Files/Pods-pop-tests-osx/Pods-pop-tests-osx.release.xcconfig",
]
pop_test_xcconfigs.each do |pop_test_xcconfig|
new_xcconfig = File.read(pop_test_xcconfig).gsub(/OTHER_LDFLAGS/, "POD_OTHER_LDFLAGS")
new_xcconfig << "\nOTHER_LDFLAGS = $(POD_OTHER_LDFLAGS) -framework XCTest"
new_xcconfig << "\nFRAMEWORK_SEARCH_PATHS = $(inherited) \"$(PLATFORM_DIR)/Developer/Library/Frameworks\""
File.write(pop_test_xcconfig, new_xcconfig)
end
end

10
Clocker/pop/Podfile.lock

@ -0,0 +1,10 @@
PODS:
- OCMock (2.2.4)
DEPENDENCIES:
- OCMock (~> 2.2)
SPEC CHECKSUMS:
OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2
COCOAPODS: 0.39.0

201
Clocker/pop/README.md

@ -0,0 +1,201 @@
![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 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](http://www.facebook.com/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
#import <pop/POP.h>
```
or if you're using the embedded framework:
```objective-c
@import pop;
```
### Start, Stop & Update
To start an animation, add it to the object you wish to animate:
```objective-c
POPSpringAnimation *anim = [POPSpringAnimation animation];
...
[layer pop_addAnimation:anim forKey:@"myKey"];
```
To stop an animation, remove it from the object referencing the key specified on start:
```objective-c
[layer pop_removeAnimationForKey:@"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
anim = [layer pop_animationForKey:@"myKey"];
if (anim) {
/* 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
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds];
anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 400, 400)];
[layer pop_addAnimation: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
POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPositionX];
anim.velocity = @(1000.);
[layer pop_addAnimation: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
POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPViewAlpha];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.fromValue = @(0.0);
anim.toValue = @(1.0);
[view pop_addAnimation: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
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
```
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
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;
```
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
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
POPAnimationTracer *tracer = anim.tracer;
tracer.shouldLogAndResetOnCompletion = YES;
[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.

28
Clocker/pop/pop-tests/POPAnimatable.h

@ -0,0 +1,28 @@
/**
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/Foundation.h>
#import <QuartzCore/QuartzCore.h>
@interface POPAnimatable : NSObject
@property (nonatomic, assign) float radius;
@property (nonatomic, assign) CGPoint position;
- (NSArray *)recordedValuesForKey:(NSString *)key;
- (void)startRecording;
- (void)stopRecording;
@end

76
Clocker/pop/pop-tests/POPAnimatable.mm

@ -0,0 +1,76 @@
/**
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 "POPAnimatable.h"
#import <pop/POP.h>
@implementation POPAnimatable
{
BOOL _recording;
NSMutableDictionary *_recordedValuesDict;
}
@synthesize radius = _radius;
@synthesize position = _position;
static void record_value(POPAnimatable *self, NSString *key, id value)
{
if (!self->_recordedValuesDict) {
self->_recordedValuesDict = [NSMutableDictionary new];
}
NSMutableArray *values = self->_recordedValuesDict[key];
if (!values) {
values = [NSMutableArray array];
self->_recordedValuesDict[key] = values;
}
[values addObject:value];
}
static void record_value(POPAnimatable *self, NSString *key, float f)
{
record_value(self, key, @(f));
}
static void record_value(POPAnimatable *self, NSString *key, CGPoint p)
{
record_value(self, key, [NSValue valueWithCGPoint:p]);
}
- (void)setRadius:(float)radius
{
_radius = radius;
if (_recording) {
record_value(self, @"radius", radius);
}
}
- (void)setPosition:(CGPoint)position
{
_position = position;
if (_recording) {
record_value(self, @"position", position);
}
}
- (NSArray *)recordedValuesForKey:(NSString *)key
{
return _recordedValuesDict[key];
}
- (void)startRecording
{
_recording = YES;
}
- (void)stopRecording
{
_recording = NO;
}
@end

142
Clocker/pop/pop-tests/POPAnimatablePropertyTests.mm

@ -0,0 +1,142 @@
/**
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 <XCTest/XCTest.h>
#import <pop/POPAnimatableProperty.h>
static const CGFloat epsilon = 0.0001f;
static NSArray *properties = @[@"name", @"readBlock", @"writeBlock", @"threshold"];
static void assertPropertyEqual(id self, POPAnimatableProperty *prop1, POPAnimatableProperty *prop2)
{
for (NSString *property in properties) {
id value = [prop1 valueForKey:property];
id valueCopy = [prop2 valueForKey:property];
XCTAssertEqualObjects(value, valueCopy, @"unexpected inequality; value:%@ copy:%@", value, valueCopy);
}
}
@interface POPAnimatablePropertyTests : XCTestCase
@end
@implementation POPAnimatablePropertyTests
- (void)testProvidedExistence
{
NSArray *names = @[kPOPLayerPosition,
kPOPLayerOpacity,
kPOPLayerScaleXY,
kPOPLayerSubscaleXY,
kPOPLayerSubtranslationX,
kPOPLayerSubtranslationY,
kPOPLayerSubtranslationZ,
kPOPLayerSubtranslationXY,
kPOPLayerZPosition,
kPOPLayerSize,
kPOPLayerRotation,
kPOPLayerRotationY,
kPOPLayerRotationX,
kPOPLayerShadowColor,
kPOPLayerShadowOffset,
kPOPLayerShadowOpacity,
kPOPLayerShadowRadius,
kPOPLayerCornerRadius,
kPOPLayerBorderWidth,
kPOPLayerBorderColor,
kPOPShapeLayerStrokeStart,
kPOPShapeLayerStrokeEnd,
kPOPShapeLayerStrokeColor,
kPOPShapeLayerLineWidth,
kPOPShapeLayerLineDashPhase,
#if TARGET_OS_IPHONE
kPOPViewAlpha,
kPOPViewBackgroundColor,
kPOPViewCenter,
kPOPViewFrame,
kPOPViewBounds,
kPOPViewSize,
kPOPViewTintColor,
kPOPScrollViewZoomScale,
kPOPTableViewContentSize,
kPOPTableViewContentOffset,
kPOPCollectionViewContentSize,
kPOPCollectionViewContentSize,
kPOPLabelTextColor
#else
kPOPViewFrame,
kPOPViewBounds,
kPOPViewAlphaValue,
kPOPViewFrameRotation,
kPOPViewFrameCenterRotation,
kPOPViewBoundsRotation,
kPOPWindowFrame,
kPOPWindowAlphaValue,
kPOPWindowBackgroundColor
#endif
];
for (NSString *name in names) {
POPAnimatableProperty *prop = [POPAnimatableProperty propertyWithName:name];
XCTAssertNotNil(prop, @"animatable property %@ should exist", name);
}
}
- (void)testUserCreation
{
static NSString *name = @"lalalala";
static CGFloat threshold = 0.07;
POPAnimatableProperty *prop;
prop = [POPAnimatableProperty propertyWithName:name];
XCTAssertNil(prop, @"animatable property %@ should not exist", name);
prop = [POPAnimatableProperty propertyWithName:name initializer:^(POPMutableAnimatableProperty *p){
p.threshold = threshold;
}];
XCTAssertNotNil(prop, @"animatable property %@ should exist", name);
XCTAssertEqualWithAccuracy(threshold, prop.threshold, epsilon, @"property threshold %f should equal %f", prop.threshold, threshold);
}
- (void)testClassCluster
{
POPAnimatableProperty *instance1 = [[POPAnimatableProperty alloc] init];
POPAnimatableProperty *instance2 = [[POPAnimatableProperty alloc] init];
XCTAssertTrue(instance1 == instance2, @"instance1:%@ instance2:%@", instance1, instance2);
for (NSString *property in properties) {
XCTAssertNoThrow([instance1 valueForKey:property], @"exception on %@", property);
}
}
- (void)testCopying
{
// instance
POPAnimatableProperty *prop = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
// instance copy
POPAnimatableProperty *propCopy = [prop copy];
// test equality
assertPropertyEqual(self, prop, propCopy);
}
- (void)testMutableCopying
{
// instance
POPAnimatableProperty *prop = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
// instance copy
POPAnimatableProperty *propCopy = [prop mutableCopy];
// test equality
assertPropertyEqual(self, prop, propCopy);
}
@end

91
Clocker/pop/pop-tests/POPAnimationMRRTests.mm

@ -0,0 +1,91 @@
/**
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/QuartzCore.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import <pop/POP.h>
#import <pop/POPAnimatorPrivate.h>
#import "POPAnimationTestsExtras.h"
@interface POPAnimationMRRTests : XCTestCase
{
POPAnimator *_animator;
CFTimeInterval _beginTime;
}
@end
@implementation POPAnimationMRRTests
- (void)setUp
{
[super setUp];
_animator = [[POPAnimator sharedAnimator] retain];
_beginTime = CACurrentMediaTime();
_animator.beginTime = _beginTime;
}
- (void)tearDown
{
[_animator release];
_animator = nil;
[super tearDown];
}
- (void)testZeroingDelegate
{
POPBasicAnimation *anim = FBTestLinearPositionAnimation();
@autoreleasepool {
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
XCTAssertNotNil(anim.delegate, @"delegate should not be nil");
}
XCTAssertNil(anim.delegate, @"delegate should be nil");
}
- (void)testAnimationCancellationOnAnimatableDeallocation
{
id layer = nil;
POPBasicAnimation *anim = FBTestLinearPositionAnimation();
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
@autoreleasepool {
layer = [OCMockObject niceMockForClass:[CALayer class]];
anim.delegate = delegate;
// expect position start
[[delegate expect] pop_animationDidStart:anim];
// run
[layer pop_addAnimation:anim forKey:@""];
POPAnimatorRenderTimes(_animator, _beginTime, @[@0.0]);
// verify
[layer verify];
[delegate verify];
// expect stop unfinished
[[delegate expect] pop_animationDidStop:anim finished:NO];
layer = nil;
}
// run
POPAnimatorRenderTimes(_animator, _beginTime, @[@0.5]);
// verify
[delegate verify];
}
@end

891
Clocker/pop/pop-tests/POPAnimationTests.mm

@ -0,0 +1,891 @@
/**
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/QuartzCore.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import <pop/POP.h>
#import <pop/POPAnimationPrivate.h>
#import <pop/POPAnimatorPrivate.h>
#import "POPAnimatable.h"
#import "POPAnimationRuntime.h"
#import "POPAnimationTestsExtras.h"
#import "POPBaseAnimationTests.h"
#import "POPCGUtils.h"
#import "POPAnimationInternal.h"
using namespace POP;
@interface POPAnimation (TestExtensions)
@property (strong, nonatomic) NSString *sampleKey;
@end
@implementation POPAnimation (TestExtensions)
- (NSString *)sampleKey { return [self valueForUndefinedKey:@"sampleKey"]; }
- (void)setSampleKey:(NSString *)aValue { [self setValue:aValue forUndefinedKey:@"sampleKey"];}
@end
@interface POPAnimationTests : POPBaseAnimationTests
@end
@implementation POPAnimationTests
- (void)testOrneryAbstractClasses
{
XCTAssertThrows([[POPAnimation alloc] init], @"should not be able to instiate abstract class");
XCTAssertThrows([[POPPropertyAnimation alloc] init], @"should not be able to instiate abstract class");
}
- (void)testWithPropertyNamedConstruction
{
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds];
POPAnimatableProperty *prop = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
XCTAssertTrue(anim.property == prop, @"expected:%@ actual:%@", prop, anim.property);
}
- (void)testAdditionRemoval
{
CALayer *layer1 = self.layer1;
CALayer *layer2 = self.layer2;
[layer1 removeAllAnimations];
[layer2 removeAllAnimations];
POPAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
[layer1 pop_addAnimation:anim forKey:@"hello"];
NSArray *keys = [layer1 pop_animationKeys];
XCTAssertTrue(1 == keys.count);
XCTAssertTrue([@"hello" isEqualToString:keys.lastObject]);
[layer1 pop_removeAnimationForKey:@"hello"];
XCTAssertTrue(0 == [layer1 pop_animationKeys].count);
[layer1 pop_addAnimation:FBTestLinearPositionAnimation(self.beginTime) forKey:@"hello"];
[layer1 pop_addAnimation:FBTestLinearPositionAnimation(self.beginTime) forKey:@"world"];
[layer2 pop_addAnimation:FBTestLinearPositionAnimation(self.beginTime) forKey:@"hello"];
XCTAssertTrue(2 == [layer1 pop_animationKeys].count);
XCTAssertTrue(1 == [layer2 pop_animationKeys].count);
[layer1 pop_removeAllAnimations];
XCTAssertTrue(0 == [layer1 pop_animationKeys].count);
XCTAssertTrue(1 == [layer2 pop_animationKeys].count);
}
- (void)testStartStopDelegation
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// expect start, stop finished
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
anim.delegate = delegate;
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @1.0]);
// verify expectations
[delegate verify];
}
- (void)testAnimationValues
{
POPBasicAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
// avoid fractional values; simplify verification
anim.roundingFactor = 1.0;
id layer = [OCMockObject niceMockForClass:[CALayer class]];
[[layer expect] setPosition:FBTestInterpolateLinear(Vector2r([anim.fromValue CGPointValue]), Vector2r([anim.toValue CGPointValue]), 0.25).cg_point()];
[[layer expect] setPosition:FBTestInterpolateLinear(Vector2r([anim.fromValue CGPointValue]), Vector2r([anim.toValue CGPointValue]), 0.5).cg_point()];
[[layer expect] setPosition:FBTestInterpolateLinear(Vector2r([anim.fromValue CGPointValue]), Vector2r([anim.toValue CGPointValue]), 0.75).cg_point()];
[[layer expect] setPosition:[anim.toValue CGPointValue]];
[layer pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 1, 0.25);
[layer verify];
}
- (void)testNoAutoreverseRepeatCount0
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPBasicAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
anim.repeatCount = 0;
anim.roundingFactor = 1.0;
anim.autoreverses = NO;
NSValue *originalToValue = anim.toValue;
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 2.0, 0.25); // animate longer than needed to verify animation has stopped in the appropriate place
XCTAssertEqualObjects([layer1 valueForKeyPath:@"position"], originalToValue, @"expected equality; value1:%@ value2:%@", [layer1 valueForKey:@"position"], originalToValue);
}
- (void)testNoAutoreverseRepeatCount1
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPBasicAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
anim.repeatCount = 1;
anim.roundingFactor = 1.0;
anim.autoreverses = NO;
NSValue *originalToValue = anim.toValue;
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3.0, 0.25); // animate longer than needed to verify animation has stopped in the appropriate place
XCTAssertEqualObjects([layer1 valueForKeyPath:@"position"], originalToValue, @"expected equality; value1:%@ value2:%@", [layer1 valueForKey:@"position"], originalToValue);
}
- (void)testNoAutoreverseRepeatCount4
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPBasicAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
anim.repeatCount = 4;
anim.roundingFactor = 1.0;
anim.autoreverses = NO;
NSValue *originalToValue = anim.toValue;
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 6.0, 0.25); // animate longer than needed to verify animation has stopped in the appropriate place
XCTAssertEqualObjects([layer1 valueForKeyPath:@"position"], originalToValue, @"expected equality; value1:%@ value2:%@", [layer1 valueForKey:@"position"], originalToValue);
}
- (void)testAutoreverseRepeatCount0
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPBasicAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
anim.roundingFactor = 1.0;
anim.autoreverses = YES;
anim.repeatCount = 0;
[anim.tracer start];
NSValue *originalFromValue = anim.fromValue;
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3.0, 0.25); // animate longer than needed to verify animation has stopped in the appropriate place
XCTAssertEqualObjects([layer1 valueForKey:@"position"], originalFromValue, @"expected equality; value1:%@ value2:%@", [layer1 valueForKey:@"position"], originalFromValue);
NSArray *autoreversedEvents = [anim.tracer eventsWithType:kPOPAnimationEventAutoreversed];
XCTAssertTrue(1 == autoreversedEvents.count, @"unexpected autoreversed events %@", autoreversedEvents);
anim.autoreverses = NO;
}
- (void)testAutoreverseRepeatCount1
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPBasicAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
anim.roundingFactor = 1.0;
anim.autoreverses = YES;
anim.repeatCount = 1;
[anim.tracer start];
NSValue *originalFromValue = anim.fromValue;
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3.0, 0.25); // animate longer than needed to verify animation has stopped in the appropriate place
XCTAssertEqualObjects([layer1 valueForKey:@"position"], originalFromValue, @"expected equality; value1:%@ value2:%@", [layer1 valueForKey:@"position"], originalFromValue);
NSArray *autoreversedEvents = [anim.tracer eventsWithType:kPOPAnimationEventAutoreversed];
XCTAssertTrue(1 == autoreversedEvents.count, @"unexpected autoreversed events %@", autoreversedEvents);
anim.autoreverses = NO;
}
- (void)testAutoreverseRepeatCount4
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPBasicAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
anim.roundingFactor = 1.0;
anim.autoreverses = YES;
NSInteger repeatCount = 4;
anim.repeatCount = repeatCount;
[anim.tracer start];
NSValue *originalFromValue = anim.fromValue;
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 9.0, 0.25); // animate longer than needed to verify animation has stopped in the appropriate place
XCTAssertEqualObjects([layer1 valueForKey:@"position"], originalFromValue, @"expected equality; value1:%@ value2:%@", [layer1 valueForKey:@"position"], originalFromValue);
NSArray *autoreversedEvents = [anim.tracer eventsWithType:kPOPAnimationEventAutoreversed];
XCTAssertTrue((repeatCount * 2) - 1 == (int)autoreversedEvents.count, @"unexpected autoreversed events %@", autoreversedEvents);
anim.autoreverses = NO;
}
- (void)testReAddition
{
CALayer *layer1 = self.layer1;
CALayer *layer2 = self.layer2;
[layer1 removeAllAnimations];
[layer2 removeAllAnimations];
static NSString *key = @"key";
POPAnimation *anim1, *anim2;
id delegate1, delegate2;
anim1 = FBTestLinearPositionAnimation(self.beginTime);
delegate1 = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// expect start, stop not finished
[[delegate1 expect] pop_animationDidStart:anim1];
[[delegate1 expect] pop_animationDidStop:anim1 finished:NO];
anim1.delegate = delegate1;
[layer1 pop_addAnimation:anim1 forKey:key];
anim2 = FBTestLinearPositionAnimation(self.beginTime);
delegate2 = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// expect start, stop finished
[[delegate2 expect] pop_animationDidStart:anim2];
[[delegate2 expect] pop_animationDidStop:anim2 finished:YES];
anim2.delegate = delegate2;
// add with same key
[layer1 pop_addAnimation:anim2 forKey:key];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @1.0]);
// verify expectations
[delegate1 verify];
[delegate2 verify];
}
- (void)testAnimationDidStartBlock
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// set animation did start block
anim.animationDidStartBlock = ^(POPAnimation *a) {
[delegate pop_animationDidStart:a];
};
[[delegate expect] pop_animationDidStart:anim];
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @1.0]);
[delegate verify];
}
- (void)testAnimationDidReachToValueBlock
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// set animation did reach to value block
anim.animationDidReachToValueBlock = ^(POPAnimation *a) {
[delegate pop_animationDidReachToValue:a];
};
[[delegate expect] pop_animationDidReachToValue:anim];
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @1.0]);
[delegate verify];
}
- (void)testCompletionBlock
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.completionBlock = ^(POPAnimation *a, BOOL finished) {
[delegate pop_animationDidStop:a finished:finished];
};
// test for unfinished completion
[[delegate expect] pop_animationDidStop:anim finished:NO];
[layer1 pop_addAnimation:anim forKey:@"key"];
[layer1 pop_removeAllAnimations];
[delegate verify];
anim = FBTestLinearPositionAnimation(self.beginTime);
delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// set completion block
anim.completionBlock = ^(POPAnimation *a, BOOL finished) {
[delegate pop_animationDidStop:a finished:finished];
};
// test for finished completion
[[delegate expect] pop_animationDidStop:anim finished:YES];
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @1.0]);
[delegate verify];
}
- (void)testAnimationDidApplyBlock
{
CALayer *layer1 = self.layer1;
[layer1 removeAllAnimations];
POPAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// set animation did apply block
anim.animationDidApplyBlock = ^(POPAnimation *a) {
[delegate pop_animationDidApply:a];
};
[[delegate expect] pop_animationDidApply:anim];
[layer1 pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @1.0]);
[delegate verify];
}
- (void)testReuse
{
NSValue *fromValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
NSValue *toValue = [NSValue valueWithCGPoint:CGPointMake(200, 200)];
CGFloat testProgress = 0.25;
POPBasicAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
anim.fromValue = fromValue;
anim.toValue = toValue;
anim.roundingFactor = 1.0;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
anim.delegate = delegate;
id layer = [OCMockObject niceMockForClass:[CALayer class]];
[[layer expect] setPosition:FBTestInterpolateLinear(Vector2r([fromValue CGPointValue]), Vector2r([toValue CGPointValue]), testProgress).cg_point()];
[[layer expect] setPosition:[toValue CGPointValue]];
[layer pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, [NSNumber numberWithFloat:testProgress * anim.duration], [NSNumber numberWithFloat:anim.duration]]);
[layer verify];
[delegate verify];
// new delegate & layer, same animation
delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
anim.delegate = delegate;
layer = [OCMockObject niceMockForClass:[CALayer class]];
[[layer expect] setPosition:FBTestInterpolateLinear(Vector2r([fromValue CGPointValue]), Vector2r([toValue CGPointValue]), testProgress).cg_point()];
[[layer expect] setPosition:[toValue CGPointValue]];
[layer pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, [NSNumber numberWithFloat:testProgress * anim.duration], [NSNumber numberWithFloat:anim.duration]]);
[layer verify];
[delegate verify];
}
- (void)testCancelBeforeBegin
{
POPAnimation *anim = FBTestLinearPositionAnimation(self.beginTime + 100000);
[anim.tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@"key"];
[layer pop_removeAllAnimations];
NSArray *didStartEvents = [anim.tracer eventsWithType:kPOPAnimationEventDidStart];
NSArray *didStopEvents = [anim.tracer eventsWithType:kPOPAnimationEventDidStop];
XCTAssertTrue(1 == didStartEvents.count, @"unexpected start events %@", didStartEvents);
XCTAssertTrue(1 == didStopEvents.count, @"unexpected stop events %@", didStopEvents);
}
- (void)testAddedKeys
{
POPAnimation *anim = FBTestLinearPositionAnimation();
anim.sampleKey = @"value";
XCTAssertEqualObjects(anim.sampleKey, @"value", @"property value read should equal write");
}
- (void)testValueTypeResolution
{
POPSpringAnimation *anim = [POPSpringAnimation animation];
XCTAssertNil(anim.fromValue);
XCTAssertNil(anim.toValue);
XCTAssertNil(anim.velocity);
id pointValue = [NSValue valueWithCGPoint:CGPointMake(1, 2)];
anim.fromValue = pointValue;
anim.toValue = pointValue;
anim.velocity = pointValue;
XCTAssertEqualObjects(anim.fromValue, pointValue, @"property value read should equal write");
XCTAssertEqualObjects(anim.toValue, pointValue, @"property value read should equal write");
XCTAssertEqualObjects(anim.velocity, pointValue, @"property value read should equal write");
POPSpringAnimation *anim2 = [POPSpringAnimation animation];
id rectValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 20, 20)];
anim2.fromValue = rectValue;
anim2.toValue = rectValue;
anim2.velocity = rectValue;
XCTAssertEqualObjects(anim2.fromValue, rectValue, @"property value read should equal write");
XCTAssertEqualObjects(anim2.toValue, rectValue, @"property value read should equal write");
XCTAssertEqualObjects(anim2.velocity, rectValue, @"property value read should equal write");
#if TARGET_OS_IPHONE
POPSpringAnimation *anim3 = [POPSpringAnimation animation];
id edgeInsetsValue = [NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(20, 40, 20, 40)];
anim3.fromValue = edgeInsetsValue;
anim3.toValue = edgeInsetsValue;
anim3.velocity = edgeInsetsValue;
XCTAssertEqualObjects(anim3.fromValue, edgeInsetsValue, @"property value read should equal write");
XCTAssertEqualObjects(anim3.toValue, edgeInsetsValue, @"property value read should equal write");
XCTAssertEqualObjects(anim3.velocity, edgeInsetsValue, @"property value read should equal write");
#endif
POPSpringAnimation *anim4 = [POPSpringAnimation animation];
id transformValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
XCTAssertThrows(anim4.fromValue = transformValue, @"should not be able to set %@", transformValue);
}
- (void)testTracer
{
POPAnimatable *circle = [POPAnimatable new];
POPSpringAnimation *anim = [POPSpringAnimation animation];
POPAnimationTracer *tracer = anim.tracer;
XCTAssertNotNil(tracer, @"missing tracer");
[tracer start];
NSNumber *animFromValue = @0.0;
NSNumber *animToValue = @1.0;
NSNumber *animVelocity = @0.1;
float animBounciness = 4.1;
float animSpeed = 13.0;
float animFriction = 123.;
float animMass = 0.9;
float animTension = 401.;
anim.property = self.radiusProperty;
anim.fromValue = animFromValue;
anim.toValue = animToValue;
anim.velocity = animVelocity;
anim.dynamicsFriction = animFriction;
anim.dynamicsMass = animMass;
anim.dynamicsTension = animTension;
anim.springBounciness = animBounciness;
anim.springSpeed = animSpeed;
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 5, 0.01);
[tracer stop];
NSArray *allEvents = tracer.allEvents;
NSArray *fromEvents = [tracer eventsWithType:kPOPAnimationEventFromValueUpdate];
NSArray *toEvents = [tracer eventsWithType:kPOPAnimationEventToValueUpdate];
NSArray *velocityEvents = [tracer eventsWithType:kPOPAnimationEventVelocityUpdate];
NSArray *bouncinessEvents = [tracer eventsWithType:kPOPAnimationEventBouncinessUpdate];
NSArray *speedEvents = [tracer eventsWithType:kPOPAnimationEventSpeedUpdate];
NSArray *frictionEvents = [tracer eventsWithType:kPOPAnimationEventFrictionUpdate];
NSArray *massEvents = [tracer eventsWithType:kPOPAnimationEventMassUpdate];
NSArray *tensionEvents = [tracer eventsWithType:kPOPAnimationEventTensionUpdate];
NSArray *startEvents = [tracer eventsWithType:kPOPAnimationEventDidStart];
NSArray *stopEvents = [tracer eventsWithType:kPOPAnimationEventDidStop];
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// all events
XCTAssertTrue(0 != allEvents.count, @"unexpected allEvents count %@", allEvents);
// from events
XCTAssertTrue(1 == fromEvents.count, @"unexpected fromEvents count %@", fromEvents);
id eventFromValue = [(POPAnimationValueEvent *)fromEvents.lastObject value];
XCTAssertEqualObjects(animFromValue, eventFromValue, @"unexpected eventFromValue; expected:%@ actual:%@", animFromValue, eventFromValue);
// to events
XCTAssertTrue(1 == toEvents.count, @"unexpected toEvents count %@", toEvents);
id eventToValue = [(POPAnimationValueEvent *)toEvents.lastObject value];
XCTAssertEqualObjects(animToValue, eventToValue, @"unexpected eventToValue; expected:%@ actual:%@", animToValue, eventToValue);
// velocity events
XCTAssertTrue(1 == velocityEvents.count, @"unexpected velocityEvents count %@", velocityEvents);
id eventVelocity = [(POPAnimationValueEvent *)velocityEvents.lastObject value];
XCTAssertEqualObjects(animVelocity, eventVelocity, @"unexpected eventVelocity; expected:%@ actual:%@", animVelocity, eventVelocity);
// bounciness events
XCTAssertTrue(1 == bouncinessEvents.count, @"unexpected bouncinessEvents count %@", bouncinessEvents);
id eventBounciness = [(POPAnimationValueEvent *)bouncinessEvents.lastObject value];
XCTAssertEqualObjects(@(animBounciness), eventBounciness, @"unexpected bounciness; expected:%@ actual:%@", @(animBounciness), eventBounciness);
// speed events
XCTAssertTrue(1 == speedEvents.count, @"unexpected speedEvents count %@", speedEvents);
id eventSpeed = [(POPAnimationValueEvent *)speedEvents.lastObject value];
XCTAssertEqualObjects(@(animSpeed), eventSpeed, @"unexpected speed; expected:%@ actual:%@", @(animSpeed), eventSpeed);
// friction events
XCTAssertTrue(1 == frictionEvents.count, @"unexpected frictionEvents count %@", frictionEvents);
id eventFriction = [(POPAnimationValueEvent *)frictionEvents.lastObject value];
XCTAssertEqualObjects(@(animFriction), eventFriction, @"unexpected friction; expected:%@ actual:%@", @(animFriction), eventFriction);
// mass events
XCTAssertTrue(1 == massEvents.count, @"unexpected massEvents count %@", massEvents);
id eventMass = [(POPAnimationValueEvent *)massEvents.lastObject value];
XCTAssertEqualObjects(@(animMass), eventMass, @"unexpected mass; expected:%@ actual:%@", @(animMass), eventMass);
// tension events
XCTAssertTrue(1 == tensionEvents.count, @"unexpected tensionEvents count %@", tensionEvents);
id eventTension = [(POPAnimationValueEvent *)tensionEvents.lastObject value];
XCTAssertEqualObjects(@(animTension), eventTension, @"unexpected tension; expected:%@ actual:%@", @(animTension), eventTension);
// start & stop event
XCTAssertTrue(1 == startEvents.count, @"unexpected startEvents count %@", startEvents);
XCTAssertTrue(1 == stopEvents.count, @"unexpected stopEvents count %@", stopEvents);
// start before stop
NSUInteger startIdx = [allEvents indexOfObjectIdenticalTo:startEvents.firstObject];
NSUInteger stopIdx = [allEvents indexOfObjectIdenticalTo:stopEvents.firstObject];
XCTAssertTrue(startIdx < stopIdx, @"unexpected start/stop ordering startIdx:%lu stopIdx:%lu", (unsigned long)startIdx, (unsigned long)stopIdx);
// did reach event
XCTAssertTrue(1 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
// did reach after start before stop
NSUInteger didReachIdx = [allEvents indexOfObjectIdenticalTo:didReachEvents.firstObject];
XCTAssertTrue(didReachIdx > startIdx, @"unexpected didReach/start ordering didReachIdx:%lu startIdx:%lu", (unsigned long)didReachIdx, (unsigned long)startIdx);
XCTAssertTrue(didReachIdx < stopIdx, @"unexpected didReach/stop ordering didReachIdx:%lu stopIdx:%lu", (unsigned long)didReachIdx, (unsigned long)stopIdx);
// write events
XCTAssertTrue(0 != writeEvents.count, @"unexpected writeEvents count %@", writeEvents);
id firstWriteValue = [(POPAnimationValueEvent *)writeEvents.firstObject value];
XCTAssertTrue(NSOrderedSame == [anim.fromValue compare:firstWriteValue], @"unexpected firstWriteValue; fromValue:%@ actual:%@", anim.fromValue, firstWriteValue);
id lastWriteValue = [(POPAnimationValueEvent *)writeEvents.lastObject value];
XCTAssertEqualObjects(lastWriteValue, anim.toValue, @"unexpected lastWriteValue; expected:%@ actual:%@", anim.toValue, lastWriteValue);
}
- (void)testAnimationContinuation
{
POPAnimatable *circle = [POPAnimatable new];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = self.radiusProperty;
anim.fromValue = @0.0;
anim.toValue = @1.0;
anim.velocity = @10.0;
anim.springBounciness = 4;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 0.25, 0.05);
NSArray *didReachToEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
NSArray *stopEvents = [tracer eventsWithType:kPOPAnimationEventDidStop];
// assert did reach but not stop
XCTAssertTrue(1 == didReachToEvents.count, @"unexpected didReachToEvents count %@", didReachToEvents);
XCTAssertTrue(0 == stopEvents.count, @"unexpected stopEvents count %@", stopEvents);
// update to value continuing animation
anim.toValue = @0.0;
POPAnimatorRenderDuration(self.animator, self.beginTime, 2.0, 0.1);
[tracer stop];
// two did reach to events
didReachToEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
XCTAssertTrue(2 == didReachToEvents.count, @"unexpected didReachToEvents count %@", didReachToEvents);
// first event value > animation to value
id firstDidReachValue = [(POPAnimationValueEvent *)didReachToEvents.firstObject value];
XCTAssertTrue(NSOrderedAscending == [anim.toValue compare:firstDidReachValue], @"unexpected firstDidReachValue; toValue:%@ actual:%@", anim.toValue, firstDidReachValue);
// second event value < animation to value
id lastDidReachValue = [(POPAnimationValueEvent *)didReachToEvents.lastObject value];
XCTAssertTrue(NSOrderedDescending == [anim.toValue compare:lastDidReachValue], @"unexpected lastDidReachValue; toValue:%@ actual:%@", anim.toValue, lastDidReachValue);
// did stop event
stopEvents = [tracer eventsWithType:kPOPAnimationEventDidStop];
XCTAssertTrue(1 == stopEvents.count, @"unexpected stopEvents count %@", stopEvents);
XCTAssertEqualObjects([(POPAnimationValueEvent *)stopEvents.lastObject value], @YES, @"unexpected stop event: %@", stopEvents.lastObject);
}
- (void)testRoundingFactor
{
POPAnimatable *circle = [POPAnimatable new];
{
// non retina, additive & non-additive
BOOL additive = NO;
LStart:
POPBasicAnimation *anim = [POPBasicAnimation animation];
anim.property = self.radiusProperty;
anim.fromValue = @0.0;
anim.toValue = @1.0;
anim.roundingFactor = 1.0;
anim.additive = additive;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 0.25, 0.05);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
BOOL containValue = POPAnimationEventsContainValue(writeEvents, @0.5);
XCTAssertFalse(containValue, @"unexpected write value %@", writeEvents);
if (!additive) {
additive = YES;
goto LStart;
}
}
{
// retina, additive & non-additive
BOOL additive = NO;
LStartRetina:
POPBasicAnimation *anim = [POPBasicAnimation animation];
anim.property = self.radiusProperty;
anim.fromValue = @0.0;
anim.toValue = @1.0;
anim.roundingFactor = 0.5;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 0.25, 0.05);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
BOOL containValue = POPAnimationEventsContainValue(writeEvents, @0.5);
XCTAssertTrue(containValue, @"unexpected write value %@", writeEvents);
if (!additive) {
additive = YES;
goto LStartRetina;
}
}
}
- (void)testAdditiveAnimation
{
const CGFloat baseValue = 1.;
const CGFloat fromValue = 1.;
const CGFloat toValue = 2.;
POPAnimatable *circle = [POPAnimatable new];
circle.radius = baseValue;
POPBasicAnimation *anim;
anim = [POPBasicAnimation animation];
anim.property = self.radiusProperty;
anim.fromValue = @(fromValue);
anim.toValue = @(toValue);
anim.additive = YES;
[circle startRecording];
[circle pop_addAnimation:anim forKey:@"key1"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 0.5, 0.05);
NSArray *writeEvents = [circle recordedValuesForKey:@"radius"];
CGFloat firstValue = [[writeEvents firstObject] floatValue];
CGFloat lastValue = [[writeEvents lastObject] floatValue];
XCTAssertTrue(firstValue >= baseValue + fromValue, @"write value expected:%f actual:%f", baseValue + fromValue, firstValue);
XCTAssertTrue(lastValue == baseValue + toValue, @"write value expected:%f actual:%f", baseValue + toValue, lastValue);
}
- (void)testNilKey
{
POPBasicAnimation *anim = FBTestLinearPositionAnimation(self.beginTime);
// avoid fractional values; simplify verification
anim.roundingFactor = 1.0;
id layer = [OCMockObject niceMockForClass:[CALayer class]];
[[layer expect] setPosition:FBTestInterpolateLinear(Vector2r([anim.fromValue CGPointValue]), Vector2r([anim.toValue CGPointValue]), 0.25).cg_point()];
[[layer expect] setPosition:FBTestInterpolateLinear(Vector2r([anim.fromValue CGPointValue]), Vector2r([anim.toValue CGPointValue]), 0.5).cg_point()];
[[layer expect] setPosition:FBTestInterpolateLinear(Vector2r([anim.fromValue CGPointValue]), Vector2r([anim.toValue CGPointValue]), 0.75).cg_point()];
[[layer expect] setPosition:[anim.toValue CGPointValue]];
// verify nil key can be added, same as CA
[layer pop_addAnimation:anim forKey:nil];
// verify attempting to remove nil key is a noop, same as CA
XCTAssertNoThrow([layer pop_removeAnimationForKey:nil], @"unexpected exception");
POPAnimatorRenderDuration(self.animator, self.beginTime, 1, 0.25);
[layer verify];
}
- (void)testIntegerAnimation
{
const int toValue = 1;
NSNumber *boxedToValue = @1.0;
POPBasicAnimation *anim;
// literal
anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
XCTAssertNoThrow(anim.toValue = @(toValue), @"unexpected exception");
XCTAssertEqualObjects(anim.toValue, boxedToValue, @"expected equality; value1:%@ value2:%@", anim.toValue, boxedToValue);
// integer
anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
XCTAssertNoThrow(anim.toValue = @(toValue), @"unexpected exception");
XCTAssertEqualObjects(anim.toValue, boxedToValue, @"expected equality; value1:%@ value2:%@", anim.toValue, boxedToValue);
// short
anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
XCTAssertNoThrow(anim.toValue = @(toValue), @"unexpected exception");
XCTAssertEqualObjects(anim.toValue, boxedToValue, @"expected equality; value1:%@ value2:%@", anim.toValue, boxedToValue);
// unsigned short
anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
XCTAssertNoThrow(anim.toValue = @(toValue), @"unexpected exception");
XCTAssertEqualObjects(anim.toValue, boxedToValue, @"expected equality; value1:%@ value2:%@", anim.toValue, boxedToValue);
// int
anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
XCTAssertNoThrow(anim.toValue = @(toValue), @"unexpected exception");
XCTAssertEqualObjects(anim.toValue, boxedToValue, @"expected equality; value1:%@ value2:%@", anim.toValue, boxedToValue);
// unsigned int
anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
XCTAssertNoThrow(anim.toValue = @(toValue), @"unexpected exception");
XCTAssertEqualObjects(anim.toValue, boxedToValue, @"expected equality; value1:%@ value2:%@", anim.toValue, boxedToValue);
// long
anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
XCTAssertNoThrow(anim.toValue = @(toValue), @"unexpected exception");
XCTAssertEqualObjects(anim.toValue, boxedToValue, @"expected equality; value1:%@ value2:%@", anim.toValue, boxedToValue);
// unsigned long
anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
XCTAssertNoThrow(anim.toValue = @(toValue), @"unexpected exception");
XCTAssertEqualObjects(anim.toValue, boxedToValue, @"expected equality; value1:%@ value2:%@", anim.toValue, boxedToValue);
anim.fromValue = @0;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:nil];
POPAnimatorRenderDuration(self.animator, self.beginTime + 0.1, 1, 0.1);
// verify writes happened
NSArray *writeEvents = tracer.writeEvents;
XCTAssertTrue(writeEvents.count == 5, @"unexpected events:%@", writeEvents);
// verify initial value
POPAnimationValueEvent *firstWriteEvent = writeEvents.firstObject;
XCTAssertTrue([firstWriteEvent.value isEqual:anim.fromValue], @"expected equality; value1:%@ value%@", firstWriteEvent.value, anim.fromValue);
// verify final value
XCTAssertEqualObjects([layer valueForKey:@"opacity"], anim.toValue, @"expected equality; value1:%@ value2:%@", [layer valueForKey:@"opacity"], anim.toValue);
}
- (void)testPlatformColorSupport
{
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBackgroundColor];
#if TARGET_OS_IPHONE
XCTAssertNoThrow(anim.fromValue = [UIColor whiteColor], @"unexpected exception");
XCTAssertNoThrow(anim.toValue = [UIColor redColor], @"unexpected exception");
#else
XCTAssertNoThrow(anim.fromValue = [NSColor whiteColor], @"unexpected exception");
XCTAssertNoThrow(anim.toValue = [NSColor redColor], @"unexpected exception");
#endif
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@"color"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 1, 0.1);
// expect some interpolation
NSArray *writeEvents = tracer.writeEvents;
XCTAssertTrue(writeEvents.count > 1, @"unexpected write events %@", writeEvents);
// get layer color components
CGFloat layerValues[4];
POPCGColorGetRGBAComponents(layer.backgroundColor, layerValues);
// get to color components
CGFloat toValues[4];
POPCGColorGetRGBAComponents((__bridge CGColorRef)anim.toValue, toValues);
// assert equality
XCTAssertTrue(layerValues[0] == toValues[0] && layerValues[1] == toValues[1] && layerValues[2] == toValues[2] && layerValues[3] == toValues[3], @"unexpected last color: [r:%f g:%f b:%f a:%f]", layerValues[0], layerValues[1], layerValues[2], layerValues[3]);
}
- (void)testNSCopyingSupportPOPBasicAnimation
{
POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:@"test_property_name"];
configureConcretePropertyAnimation(anim);
[self testCopyingSucceedsForConcretePropertyAnimation:anim];
anim.duration = 1.8;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
POPBasicAnimation *copy = [anim copy];
XCTAssertEqual(copy.duration, anim.duration, @"expected equality; value1:%@ value2:%@", @(copy.duration), @(anim.duration));
XCTAssertEqualObjects(copy.timingFunction, anim.timingFunction, @"expected equality; value1:%@ value2:%@", copy.timingFunction, anim.timingFunction);
}
@end

19
Clocker/pop/pop-tests/POPAnimationTestsExtras.h

@ -0,0 +1,19 @@
/**
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 "POPVector.h"
@class POPAnimator;
@class POPBasicAnimation;
extern void POPAnimatorRenderTime(POPAnimator *animator, CFTimeInterval beginTime, CFTimeInterval time);
extern void POPAnimatorRenderTimes(POPAnimator *animator, CFTimeInterval beginTime, NSArray *times);
extern void POPAnimatorRenderDuration(POPAnimator *animator, CFAbsoluteTime beginTime, CFTimeInterval duration, CFTimeInterval step);
extern POPBasicAnimation *FBTestLinearPositionAnimation(CFTimeInterval beginTime = 0);
extern POP::Vector2r FBTestInterpolateLinear(POP::Vector2r start, POP::Vector2r end, CGFloat progress);

54
Clocker/pop/pop-tests/POPAnimationTestsExtras.mm

@ -0,0 +1,54 @@
/**
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 "POPAnimationTestsExtras.h"
#import <pop/POP.h>
#import <pop/POPAnimatorPrivate.h>
void POPAnimatorRenderTime(POPAnimator *animator, CFTimeInterval beginTime, CFTimeInterval time)
{
[animator renderTime:beginTime + time];
}
void POPAnimatorRenderTimes(POPAnimator *animator, CFTimeInterval beginTime, NSArray *times)
{
for (NSNumber *time in times) {
[animator renderTime:beginTime + time.doubleValue];
}
}
void POPAnimatorRenderDuration(POPAnimator *animator, CFTimeInterval beginTime, CFTimeInterval duration, CFTimeInterval step)
{
CFTimeInterval initialTime = animator.beginTime;
animator.beginTime = beginTime;
NSCAssert(step > 0, @"unexpected step %f", step);
CFTimeInterval time = 0;
while(time <= duration) {
[animator renderTime:beginTime + time];
time += step;
}
animator.beginTime = initialTime;
}
POPBasicAnimation *FBTestLinearPositionAnimation(CFTimeInterval beginTime)
{
POPBasicAnimation *anim = [POPBasicAnimation linearAnimation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPosition];
anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
anim.duration = 1;
anim.beginTime = beginTime;
return anim;
}
POP::Vector2r FBTestInterpolateLinear(POP::Vector2r start, POP::Vector2r end, CGFloat progress)
{
return start + ((end - start) * progress);
}

63
Clocker/pop/pop-tests/POPBaseAnimationTests.h

@ -0,0 +1,63 @@
/**
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 <cmath>
#import <XCTest/XCTest.h>
#import "POPCGUtils.h"
@class CALayer;
@class POPAnimator;
@class POPAnimatableProperty;
@class POPAnimation;
@class POPPropertyAnimation;
@interface POPBaseAnimationTests : XCTestCase
// two layers for test use
@property (strong, nonatomic) CALayer *layer1, *layer2;
// the animator to use for rendering
@property (strong, nonatomic) POPAnimator *animator;
// the time tests began
@property (assign, nonatomic) CFTimeInterval beginTime;
// radius animatable property
@property (strong, nonatomic) POPAnimatableProperty *radiusProperty;
- (void)testCopyingSucceedsForConcreteAnimation:(POPAnimation *)anim;
- (void)testCopyingSucceedsForConcretePropertyAnimation:(POPPropertyAnimation *)anim;
@end
// max frame count required for animations to converge
extern NSUInteger kPOPAnimationConvergenceMaxFrameCount;
// counts the number of events of value within epsilon, starting from end
extern NSUInteger POPAnimationCountLastEventValues(NSArray *events, NSNumber *value, float epsilon = 0);
// returns YES if array of value events contain specified value
extern BOOL POPAnimationEventsContainValue(NSArray *events, NSNumber *value);
// equality with epsilon
#define _EQL_(x, y, epsilon) (std::abs ((x) - (y)) < epsilon)
// color equality assert
#define POPAssertColorEqual(c1, c2) \
{ \
CGFloat v1[4], v2[4]; \
POPCGColorGetRGBAComponents(c1, v1); \
POPCGColorGetRGBAComponents(c2, v2); \
XCTAssertTrue(_EQL_(v1[0], v2[0], 1e-6) && _EQL_(v1[1], v2[1], 1e-6) && _EQL_(v1[2], v2[2], 1e-6) && _EQL_(v1[3], v2[3], 1e-6), @"not equal color:[r:%f g:%f b:%f a:%f] color:[r:%f g:%f b:%f a:%f]", v1[0], v1[1], v1[2], v1[3], v2[0], v2[1], v2[2], v2[3]); \
}
extern void configureConcreteAnimation(POPAnimation *anim);
extern void configureConcretePropertyAnimation(POPPropertyAnimation *anim);

147
Clocker/pop/pop-tests/POPBaseAnimationTests.mm

@ -0,0 +1,147 @@
/**
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 "POPBaseAnimationTests.h"
#import <QuartzCore/QuartzCore.h>
#import <OCMock/OCMock.h>
#import <pop/POP.h>
#import <pop/POPAnimatorPrivate.h>
#import "POPAnimatable.h"
#import "POPAnimationTestsExtras.h"
#import "POPAnimationInternal.h"
@implementation POPBaseAnimationTests
{
CALayer *_layer1;
CALayer *_layer2;
POPAnimator *_animator;
CFTimeInterval _beginTime;
POPAnimatableProperty *_radiusProperty;
id delegate;
}
@synthesize layer1 = _layer1;
@synthesize layer2 = _layer2;
@synthesize animator = _animator;
@synthesize beginTime = _beginTime;
@synthesize radiusProperty = _radiusProperty;
- (void)setUp
{
[super setUp];
_layer1 = [[CALayer alloc] init];
_layer2 = [[CALayer alloc] init];
_animator = [POPAnimator sharedAnimator];
_radiusProperty = [POPAnimatableProperty propertyWithName:@"radius" initializer:^(POPMutableAnimatableProperty *prop){
prop.readBlock = ^(POPAnimatable *obj, CGFloat values[]) {
values[0] = [obj radius];
};
prop.writeBlock = ^(POPAnimatable *obj, const CGFloat values[]) {
obj.radius = values[0];
};
prop.threshold = 0.01;
}];
_beginTime = CACurrentMediaTime();
_animator.beginTime = _beginTime;
}
- (void)testCopyingSucceedsForConcreteAnimation:(POPAnimation *)anim
{
POPAnimation *copy = [anim copy];
XCTAssertEqualObjects(copy.name, anim.name, @"expected equality; value1:%@ value2:%@", copy.name, anim.name);
XCTAssertEqual(copy.beginTime, anim.beginTime, @"expected equality; value1:%@ value2:%@", @(copy.beginTime), @(anim.beginTime));
XCTAssertEqualObjects(copy.delegate, anim.delegate, @"expected equality; value1:%@ value2:%@", copy.delegate, anim.delegate);
XCTAssertEqualObjects(copy.animationDidStartBlock, anim.animationDidStartBlock, @"expected equality; value1:%@ value2:%@", copy.animationDidStartBlock, anim.animationDidStartBlock);
XCTAssertEqualObjects(copy.animationDidReachToValueBlock, anim.animationDidReachToValueBlock, @"expected equality; value1:%@ value2:%@", copy.animationDidReachToValueBlock, anim.animationDidReachToValueBlock);
XCTAssertEqualObjects(copy.completionBlock, anim.completionBlock, @"expected equality; value1:%@ value2:%@", copy.completionBlock, anim.completionBlock);
XCTAssertEqualObjects(copy.animationDidApplyBlock, anim.animationDidApplyBlock, @"expected equality; value1:%@ value2:%@", copy.animationDidApplyBlock, anim.animationDidApplyBlock);
XCTAssertEqual(copy.removedOnCompletion, anim.removedOnCompletion, @"expected equality; value1:%@ value2:%@", @(copy.removedOnCompletion), @(anim.removedOnCompletion));
XCTAssertEqual(copy.autoreverses, anim.autoreverses, @"expected equality; value1:%@ value2:%@", @(copy.autoreverses), @(anim.autoreverses));
XCTAssertEqual(copy.repeatCount, anim.repeatCount, @"expected equality; value1:%@ value2:%@", @(copy.repeatCount), @(anim.repeatCount));
XCTAssertEqual(copy.repeatForever, anim.repeatForever, @"expected equality; value1:%@ value2:%@", @(copy.repeatForever), @(anim.repeatForever));
}
- (void)testCopyingSucceedsForConcretePropertyAnimation:(POPPropertyAnimation *)anim
{
[self testCopyingSucceedsForConcreteAnimation:anim];
POPPropertyAnimation *copy = [anim copy];
XCTAssertEqualObjects(copy.fromValue, anim.fromValue, @"expected equality; value1:%@ value2:%@", copy.fromValue, anim.fromValue);
XCTAssertEqualObjects(copy.toValue, anim.toValue, @"expected equality; value1:%@ value2:%@", copy.toValue, anim.toValue);
XCTAssertEqual(copy.roundingFactor, anim.roundingFactor, @"expected equality; value1:%@ value2:%@", @(copy.roundingFactor), @(anim.roundingFactor));
XCTAssertEqual(copy.clampMode, anim.clampMode, @"expected equality; value1:%@ value2:%@", @(copy.clampMode), @(anim.clampMode));
XCTAssertEqual(copy.additive, anim.additive, @"expected equality; value1:%@ value2:%@", @(copy.additive), @(anim.additive));
}
@end
NSUInteger kPOPAnimationConvergenceMaxFrameCount = 12; // 12 frames, ~200ms at 1/60fps, the user perseption threshold
NSUInteger POPAnimationCountLastEventValues(NSArray *events, NSNumber *value, float epsilon)
{
__block NSUInteger count = 0;
[events enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(POPAnimationValueEvent *event, NSUInteger idx, BOOL *ptrStop) {
BOOL match = 0 == epsilon ? [event.value isEqualToValue:value] : fabsf([event.value floatValue] - [value floatValue]) < epsilon;
if (!match) {
*ptrStop = YES;
} else {
count++;
}
}];
return count;
}
BOOL POPAnimationEventsContainValue(NSArray *events, NSNumber *value)
{
for (POPAnimationValueEvent *event in events) {
if ([event.value isEqual:value]) {
return YES;
}
}
return NO;
}
void configureConcreteAnimation(POPAnimation *anim)
{
static id delegate = [NSObject new];
anim.name = @"pop_animation_copy_test";
anim.beginTime = 1.234;
anim.delegate = delegate; // dummy delegate
anim.animationDidStartBlock = ^(POPAnimation *a){ NSLog(@"Animation Did Start"); };
anim.animationDidReachToValueBlock = ^(POPAnimation *a){ NSLog(@"Animation Did Reach To Value"); };
anim.completionBlock = ^(POPAnimation *a, BOOL finished){ NSLog(@"Animation Finished"); };
anim.animationDidApplyBlock = ^(POPAnimation *){ NSLog(@"Animation Applied"); };
anim.removedOnCompletion = NO; // not default
anim.autoreverses = YES; // not default
anim.repeatCount = 42;
anim.repeatForever = YES; // not default
}
void configureConcretePropertyAnimation(POPPropertyAnimation *anim)
{
configureConcreteAnimation(anim);
// Decay animations don't use fromValue, so setting it here causes issues.
if (![anim isMemberOfClass:[POPDecayAnimation class]]) {
anim.fromValue = @(12345);
}
anim.toValue = @(77888);
anim.roundingFactor = 0.257;
anim.clampMode = 87;
anim.additive = YES; // not default
}

162
Clocker/pop/pop-tests/POPBasicAnimationTests.mm

@ -0,0 +1,162 @@
/**
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 <XCTest/XCTest.h>
#import <OCMock/OCMock.h>
#import <pop/POPBasicAnimation.h>
#import "POPAnimatable.h"
#import "POPAnimationTestsExtras.h"
#import "POPBaseAnimationTests.h"
@interface POPBasicAnimationTests : POPBaseAnimationTests
@end
@implementation POPBasicAnimationTests
- (void)testGreaterThanOneControlPointC1Y
{
POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionX];
anim.fromValue = @0;
anim.toValue = @100;
anim.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.15f :1.5f :0.55f :1.0f];
anim.duration = 0.36;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:nil];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// verify write count
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
XCTAssertTrue(writeEvents.count > 10, @"expected more write events %@", tracer.allEvents);
// verify last written value is equal to animation to value
id lastValue = [(POPAnimationValueEvent *)writeEvents.lastObject value];
XCTAssertEqualObjects(lastValue, anim.toValue, @"expected more write events %@", tracer.allEvents);
// verify last written value is less than previous value
id prevLastValue = [(POPAnimationValueEvent *)writeEvents[writeEvents.count - 2] value];
XCTAssertTrue(NSOrderedDescending == [prevLastValue compare:lastValue], @"unexpected lastValue; prevLastValue:%@ events:%@", prevLastValue, tracer.allEvents);
}
- (void)testColorInterpolation
{
POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerBackgroundColor];
#if TARGET_OS_IPHONE
anim.fromValue = [UIColor whiteColor];
anim.toValue = [UIColor redColor];
#else
anim.fromValue = [NSColor whiteColor];
anim.toValue = [NSColor redColor];
#endif
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:nil];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// verify write events
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
XCTAssertTrue(writeEvents.count > 5, @"expected more write events %@", tracer.allEvents);
// assert final value
POPAssertColorEqual((__bridge CGColorRef)anim.toValue, layer.backgroundColor);
}
- (void)testZeroDurationAnimation
{
POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerBackgroundColor];
anim.duration = 0.0f;
#if TARGET_OS_IPHONE
anim.fromValue = [UIColor whiteColor];
anim.toValue = [UIColor redColor];
#else
anim.fromValue = [NSColor whiteColor];
anim.toValue = [NSColor redColor];
#endif
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:nil];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// verify write events
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
XCTAssertTrue(writeEvents.count == 1, @"expected one write event %@", tracer.allEvents);
NSArray *stopEvents = [tracer eventsWithType:kPOPAnimationEventDidStop];
XCTAssertTrue(stopEvents.count == 1, @"expected one stop event %@", tracer.allEvents);
// assert final value
POPAssertColorEqual((__bridge CGColorRef)anim.toValue, layer.backgroundColor);
}
#if TARGET_OS_IPHONE
- (void)testEdgeInsetsSupport
{
const UIEdgeInsets fromEdgeInsets = UIEdgeInsetsZero;
const UIEdgeInsets toEdgeInsets = UIEdgeInsetsMake(100, 200, 200, 400);
POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPScrollViewContentInset];
anim.fromValue = [NSValue valueWithUIEdgeInsets:fromEdgeInsets];
anim.toValue = [NSValue valueWithUIEdgeInsets:toEdgeInsets];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
id scrollView = [OCMockObject niceMockForClass:[UIScrollView class]];
[scrollView pop_addAnimation:anim forKey:nil];
// expect final value to be set
[[scrollView expect] setContentInset:toEdgeInsets];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
// verify scroll view
[scrollView verify];
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
UIEdgeInsets lastEdgeInsets = [lastEvent.value UIEdgeInsetsValue];
// verify last insets are to insets
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(lastEdgeInsets, toEdgeInsets), @"unexpected last edge insets value: %@", lastEvent);
}
#endif
@end

169
Clocker/pop/pop-tests/POPCustomAnimationTests.mm

@ -0,0 +1,169 @@
/**
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 <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import <pop/POPCustomAnimation.h>
#import "POPAnimatable.h"
#import "POPAnimationTestsExtras.h"
#import "POPBaseAnimationTests.h"
static const CGFloat epsilon = 0.0001f;
@interface POPCustomAnimationTests : POPBaseAnimationTests
@end
@implementation POPCustomAnimationTests
- (void)testCallbackFinished
{
static NSString * const key = @"key";
static CFTimeInterval const timeInterval = 0.1;
__block NSUInteger callbackCount = 0;
// animation
POPCustomAnimation *anim = [POPCustomAnimation animationWithBlock:^BOOL(id target, POPCustomAnimation *animation) {
if (0 != callbackCount) {
// validate elapsed time
XCTAssertEqualWithAccuracy(animation.elapsedTime, timeInterval, epsilon, @"expected elapsedTime:%f %@", timeInterval, animation);
}
// increment callback count
callbackCount++;
return callbackCount < 3;
}];
anim.beginTime = self.beginTime;
// delegate
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// expect start, progress & stop to all be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
[[delegate expect] pop_animationDidApply:anim];
anim.delegate = delegate;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// layer
id layer = [OCMockObject niceMockForClass:[CALayer class]];
[layer pop_addAnimation:anim forKey:key];
POPAnimatorRenderDuration(self.animator, self.beginTime + 0.1, 5, 0.1);
XCTAssertTrue(callbackCount == 3, @"unexpected callbackCount:%lu", (unsigned long)callbackCount);
NSArray *startEvents = [tracer eventsWithType:kPOPAnimationEventDidStart];
XCTAssertTrue(1 == startEvents.count, @"unexpected startEvents count %@", startEvents);
NSArray *stopEvents = [tracer eventsWithType:kPOPAnimationEventDidStop];
XCTAssertTrue(1 == stopEvents.count, @"unexpected stopEvents count %@", stopEvents);
[layer verify];
[delegate verify];
}
- (void)testCallbackCancelled
{
static NSString * const key = @"key";
static CFTimeInterval const timeInterval = 0.1;
__block NSUInteger callbackCount = 0;
// animation
POPCustomAnimation *anim = [POPCustomAnimation animationWithBlock:^BOOL(id target, POPCustomAnimation *animation) {
if (0 == callbackCount) {
// validate elapsed time acruel
XCTAssertEqualWithAccuracy(animation.elapsedTime, 0., epsilon, @"expected elapsedTime:%f %@", timeInterval, animation);
} else {
// validate elapsed time acruel
XCTAssertEqualWithAccuracy(animation.elapsedTime, timeInterval, epsilon, @"expected elapsedTime:%f %@", timeInterval, animation);
}
// increment callback count
callbackCount++;
if (callbackCount == 3) {
[target pop_removeAnimationForKey:key];
}
return callbackCount < 3;
}];
anim.beginTime = self.beginTime;
// delegate
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// expect start, progress & stop to all be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:NO];
[[delegate expect] pop_animationDidApply:anim];
anim.delegate = delegate;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// layer
id layer = [OCMockObject niceMockForClass:[CALayer class]];
[layer pop_addAnimation:anim forKey:key];
POPAnimatorRenderDuration(self.animator, self.beginTime + 0.1, 5, 0.1);
XCTAssertTrue(callbackCount == 3, @"unexpected callbackCount:%lu", (unsigned long)callbackCount);
NSArray *startEvents = [tracer eventsWithType:kPOPAnimationEventDidStart];
XCTAssertTrue(1 == startEvents.count, @"unexpected startEvents count %@", startEvents);
NSArray *stopEvents = [tracer eventsWithType:kPOPAnimationEventDidStop];
XCTAssertTrue(1 == stopEvents.count, @"unexpected stopEvents count %@", stopEvents);
[layer verify];
[delegate verify];
}
- (void)testAssociation
{
static NSString * const key = @"key";
__block id blockTarget = nil;
POPCustomAnimation *anim = [POPCustomAnimation animationWithBlock:^(id target, POPCustomAnimation *animation) {
blockTarget = target;
return YES;
}];
id layer = [OCMockObject niceMockForClass:[CALayer class]];
[layer pop_addAnimation:anim forKey:key];
// verify animation & key
XCTAssertTrue(anim == [layer pop_animationForKey:key], @"expected:%@ actual:%@", anim, [layer pop_animationForKey:key]);
XCTAssertTrue([[layer pop_animationKeys] containsObject:key], @"expected:%@ actual:%@", key, [layer pop_animationKeys]);
POPAnimatorRenderDuration(self.animator, self.beginTime, 1, 0.1);
XCTAssertEqualObjects(layer, blockTarget, @"expected:%@ actual:%@", layer, blockTarget);
// remove animations
[layer pop_removeAnimationForKey:key];
// verify animation & key
XCTAssertFalse(anim == [layer pop_animationForKey:key], @"expected:%@ actual:%@", (id)nil, [layer pop_animationForKey:key]);
XCTAssertFalse([[layer pop_animationKeys] containsObject:key], @"expected:%@ actual:%@", (id)nil, [layer pop_animationKeys]);
}
@end

544
Clocker/pop/pop-tests/POPDecayAnimationTests.mm

@ -0,0 +1,544 @@
/**
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/QuartzCore.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import <pop/POP.h>
#import <pop/POPAnimatorPrivate.h>
#import "POPAnimatable.h"
#import "POPAnimationTestsExtras.h"
#import "POPBaseAnimationTests.h"
@interface POPDecayAnimationTests : POPBaseAnimationTests
@end
@implementation POPDecayAnimationTests
static NSString *animationKey = @"key";
static const CGFloat epsilon = 0.0001f;
- (POPDecayAnimation *)_positionAnimation
{
POPDecayAnimation *anim = [POPDecayAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPosition];
anim.fromValue = [NSValue valueWithCGPoint:CGPointZero];
anim.velocity = [NSValue valueWithCGPoint:CGPointMake(7223.021, 7223.021)];
anim.deceleration = 0.998000;
return anim;
}
- (POPDecayAnimation *)_positionXAnimation
{
POPDecayAnimation *anim = [POPDecayAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @0.;
anim.velocity = @7223.021;
anim.deceleration = 0.998000;
return anim;
}
- (POPDecayAnimation *)_positionYAnimation
{
POPDecayAnimation *anim = self._positionXAnimation;
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionY];
return anim;
}
- (void)testConvergence
{
POPAnimatable *circle = [POPAnimatable new];
POPDecayAnimation *anim = self._positionXAnimation;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 10.0, 1.0/60.0);
[tracer stop];
// did reach to value
POPAnimationValueEvent *didReachToEvent = [[tracer eventsWithType:kPOPAnimationEventDidReachToValue] lastObject];
XCTAssertEqualObjects(didReachToEvent.value, anim.toValue, @"unexpected did reach to event: %@ anim:%@", didReachToEvent, anim);
// finished
POPAnimationValueEvent *stopEvent = [[tracer eventsWithType:kPOPAnimationEventDidStop] lastObject];
XCTAssertEqualObjects(stopEvent.value, @YES, @"unexpected stop event %@", stopEvent);
// all write values monotonically increasing
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
POPAnimationValueEvent *lastWriteEvent = nil;
for (POPAnimationValueEvent *writeEvent in writeEvents) {
if (lastWriteEvent) {
NSComparisonResult result = [lastWriteEvent.value compare:writeEvent.value];
XCTAssertTrue(NSOrderedAscending == result || NSOrderedSame == result, @"write event values not monotonically increasing current:%@ last:%@ all:%@", writeEvent, lastWriteEvent, writeEvents);
}
lastWriteEvent = writeEvent;
}
// convergence threshold
NSUInteger toValueFrameCount = POPAnimationCountLastEventValues(writeEvents, anim.toValue, anim.property.threshold);
XCTAssertTrue(toValueFrameCount <= kPOPAnimationConvergenceMaxFrameCount, @"unexpected convergence; toValueFrameCount: %lu", (unsigned long)toValueFrameCount);
}
- (void)testConvergenceNegativeVelocity
{
POPAnimatable *circle = [POPAnimatable new];
POPDecayAnimation *anim = self._positionXAnimation;
anim.velocity = @-7223.021;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 10.0, 1.0/60.0);
[tracer stop];
// did reach to value
POPAnimationValueEvent *didReachToEvent = [[tracer eventsWithType:kPOPAnimationEventDidReachToValue] lastObject];
XCTAssertEqualObjects(didReachToEvent.value, anim.toValue, @"unexpected did reach to event: %@ anim:%@", didReachToEvent, anim);
// finished
POPAnimationValueEvent *stopEvent = [[tracer eventsWithType:kPOPAnimationEventDidStop] lastObject];
XCTAssertEqualObjects(stopEvent.value, @YES, @"unexpected stop event %@", stopEvent);
// all write values monotonically increasing
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
POPAnimationValueEvent *lastWriteEvent = nil;
for (POPAnimationValueEvent *writeEvent in writeEvents) {
if (lastWriteEvent) {
NSComparisonResult result = [lastWriteEvent.value compare:writeEvent.value];
XCTAssertTrue(NSOrderedDescending == result || NSOrderedSame == result, @"write event values not monotonically decreasing current:%@ last:%@ all:%@", writeEvent, lastWriteEvent, writeEvents);
}
lastWriteEvent = writeEvent;
}
// convergence threshold
NSUInteger toValueFrameCount = POPAnimationCountLastEventValues(writeEvents, anim.toValue, anim.property.threshold);
XCTAssertTrue(toValueFrameCount <= kPOPAnimationConvergenceMaxFrameCount, @"unexpected convergence; toValueFrameCount: %lu", (unsigned long)toValueFrameCount);
}
- (void)test2DConvergence
{
POPDecayAnimation *animX = self._positionXAnimation;
POPDecayAnimation *animY = self._positionYAnimation;
XCTAssertEqual(animX.duration, animY.duration, @"unexpected durations animX:%@ animY:%@", animX, animY);
XCTAssertEqualObjects(animX.toValue, animY.toValue, @"unexpected toValue animX:%@ animY:%@", animX, animY);
POPDecayAnimation *anim = self._positionAnimation;
CFTimeInterval duration = anim.duration;
XCTAssertEqualWithAccuracy(animX.duration, duration, epsilon, @"unexpected durations animX:%@ anim:%@", animX, anim);
XCTAssertEqualObjects(animX.toValue, @([anim.toValue CGPointValue].x), @"unexpected toValue animX:%@ anim:%@", animX, anim);
POPAnimatable *circle = [POPAnimatable new];
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 10.0, 1.0/60.0);
[tracer stop];
// did reach to value
POPAnimationValueEvent *didReachToEvent = [[tracer eventsWithType:kPOPAnimationEventDidReachToValue] lastObject];
XCTAssertEqualObjects(didReachToEvent.value, anim.toValue, @"unexpected did reach to event: %@ anim:%@", didReachToEvent, anim);
// finished
POPAnimationValueEvent *stopEvent = [[tracer eventsWithType:kPOPAnimationEventDidStop] lastObject];
XCTAssertEqualObjects(stopEvent.value, @YES, @"unexpected stop event %@", stopEvent);
// increase X velocity
anim.velocity = [NSValue valueWithCGPoint:CGPointMake(7223.021 + 1000, 7223.021)];
XCTAssertTrue(anim.duration > duration, @"unexpected duration expected:%f anim:%@", duration, anim);
// increase Y velocity
anim.velocity = [NSValue valueWithCGPoint:CGPointMake(7223.021, 7223.021 + 1000)];
XCTAssertTrue(anim.duration > duration, @"unexpected duration expected:%f anim:%@", duration, anim);
}
- (void)testRemovedOnCompletionNoStartStopBasics
{
static NSString *animationKey = @"key";
CALayer *layer = self.layer1;
POPAnimation *anim = self._positionXAnimation;
POPAnimationTracer *tracer = anim.tracer;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// cleanup
[layer pop_removeAllAnimations];
// configure animation
anim.removedOnCompletion = NO;
anim.delegate = delegate;
__block BOOL completionBlock = NO;
__block BOOL completionBlockFinished = NO;
anim.completionBlock = ^(POPAnimation *a, BOOL finished) {
completionBlock = YES;
completionBlockFinished = finished;
};
// start tracer
[tracer start];
// expect start and stopped
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
[layer pop_addAnimation:anim forKey:animationKey];
POPAnimatorRenderDuration(self.animator, self.beginTime, 20.0, 1.0/60.0);
NSArray *allEvents = tracer.allEvents;
// verify delegate
[delegate verify];
XCTAssertTrue(completionBlock, @"completion block did not execute %@", allEvents);
XCTAssertTrue(completionBlockFinished, @"completion block did not finish %@", allEvents);
// assert animation has not been removed
XCTAssertTrue(anim == [layer pop_animationForKey:animationKey], @"expected animation on layer animations:%@", [layer pop_animationKeys]);
}
- (void)testRemovedOnCompletionNoContinuations
{
static NSString *animationKey = @"key";
static NSArray *velocities = @[@50.0, @100.0, @20.0, @80.0];
static NSArray *durations = @[@2.0, @0.5, @0.5, @2.0];
CALayer *layer = self.layer1;
POPDecayAnimation *anim = self._positionXAnimation;
POPAnimationTracer *tracer = anim.tracer;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// cleanup
[layer pop_removeAllAnimations];
// configure animation
anim.removedOnCompletion = NO;
anim.delegate = delegate;
// start tracer
[tracer start];
__block CFTimeInterval beginTime;
__block BOOL completionBlock = NO;
__block BOOL completionBlockFinished = NO;
[velocities enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *ptrStop) {
anim.velocity = obj;
if (0 == idx) {
[tracer reset];
// starts and stops
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
anim.completionBlock = ^(POPAnimation *a, BOOL finished) {
completionBlock = YES;
completionBlockFinished = finished;
};
[layer pop_addAnimation:anim forKey:animationKey];
beginTime = self.beginTime;
CFTimeInterval dt = [durations[idx] doubleValue];
POPAnimatorRenderDuration(self.animator, beginTime, dt, 1.0/60.0);
beginTime += dt;
NSArray *allEvents = tracer.allEvents;
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
// verify delegate
[delegate verify];
XCTAssertTrue(1 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
XCTAssertTrue(completionBlock, @"completion block did not execute %@", allEvents);
XCTAssertTrue(completionBlockFinished, @"completion block did not finish %@", allEvents);
} else if (velocities.count - 1 == idx) {
// continue stoped animation
[tracer reset];
completionBlock = NO;
completionBlockFinished = NO;
[[delegate expect] pop_animationDidStop:anim finished:YES];
CFTimeInterval dt = [durations[idx] doubleValue];
POPAnimatorRenderDuration(self.animator, beginTime, dt, 1.0/60.0);
beginTime += dt;
NSArray *allEvents = tracer.allEvents;
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
// verify delegate
[delegate verify];
XCTAssertTrue(1 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
XCTAssertTrue(completionBlock, @"completion block did not execute %@", allEvents);
XCTAssertTrue(completionBlockFinished, @"completion block did not finish %@", allEvents);
} else {
// continue stoped (idx = 1) or started animation
if (1 == idx) {
[[delegate expect] pop_animationDidStart:anim];
}
// reset state
[tracer reset];
completionBlock = NO;
completionBlockFinished = NO;
CFTimeInterval dt = [durations[idx] doubleValue];
POPAnimatorRenderDuration(self.animator, beginTime, dt, 1.0/60.0);
beginTime += dt;
NSArray *allEvents = tracer.allEvents;
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
// verify delegate
[delegate verify];
XCTAssertTrue(0 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
XCTAssertFalse(completionBlock, @"completion block did not execute %@ %@", anim, allEvents);
XCTAssertFalse(completionBlockFinished, @"completion block did not finish %@ %@", anim, allEvents);
}
// assert animation has not been removed
XCTAssertTrue(anim == [layer pop_animationForKey:animationKey], @"expected animation on layer animations:%@", [layer pop_animationKeys]);
}];
}
- (void)testNoOperationAnimation
{
const CGPoint initialValue = CGPointMake(100, 100);
CALayer *layer = self.layer1;
layer.position = initialValue;
[layer pop_removeAllAnimations];
POPDecayAnimation *anim = [POPDecayAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPosition];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// starts and stops
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[layer pop_addAnimation:anim forKey:animationKey];
POPAnimatorRenderDuration(self.animator, self.beginTime, 5, 1.0/60.0);
// verify delegate
[delegate verify];
// verify number values
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
for (POPAnimationValueEvent *writeEvent in writeEvents) {
XCTAssertEqualObjects(writeEvent.value, [NSValue valueWithCGPoint:initialValue], @"unexpected write event:%@ anim:%@", writeEvent, anim);
}
}
- (void)testContinuation
{
POPDecayAnimation *anim = [POPDecayAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @0.0;
anim.velocity = @1000.0;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
[[delegate expect] pop_animationDidStart:anim];
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = self.layer1;
[layer pop_addAnimation:anim forKey:animationKey];
// run animation, not till completion
POPAnimatorRenderDuration(self.animator, self.beginTime, 1, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
[tracer reset];
// verify start delegation
[delegate verify];
// update velocity of active animation
anim.velocity = @1000.0;
[[delegate expect] pop_animationDidStop:anim finished:YES];
// run animation some more
POPAnimatorRenderDuration(self.animator, self.beginTime + 1, 4, 1.0/60.0);
NSArray *moreWriteEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify stop delegation
[delegate verify];
// compare event values
POPAnimationValueEvent *firstEvent = [writeEvents firstObject];
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
POPAnimationValueEvent *firstMoreEvent = [moreWriteEvents firstObject];
XCTAssertTrue(NSOrderedAscending == [firstEvent.value compare:lastEvent.value]
&& NSOrderedAscending == [lastEvent.value compare:firstMoreEvent.value], @"write event values not monotonically increasing %@ %@ %@", firstEvent, lastEvent, firstMoreEvent);
}
- (void)testRectSupport
{
const CGRect fromRect = CGRectMake(0, 0, 0, 0);
POPDecayAnimation *anim = [POPDecayAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
anim.fromValue = [NSValue valueWithCGRect:fromRect];
anim.velocity = [NSValue valueWithCGRect:CGRectMake(100, 100, 1000, 1000)];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = self.layer1;
[layer pop_addAnimation:anim forKey:animationKey];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
CGRect lastRect = [lastEvent.value CGRectValue];
XCTAssertTrue(!CGRectEqualToRect(fromRect, lastRect), @"unexpected last rect value: %@", lastEvent);
XCTAssertTrue(lastRect.origin.x == lastRect.origin.y && lastRect.size.width == lastRect.size.height && lastRect.origin.x < lastRect.size.width, @"unexpected last rect value: %@", lastEvent);
}
#if TARGET_OS_IPHONE
- (void)testEdgeInsetsSupport
{
const UIEdgeInsets fromEdgeInsets = UIEdgeInsetsZero;
const UIEdgeInsets velocityEdgeInsets = UIEdgeInsetsMake(100, 100, 1000, 1000);
POPDecayAnimation *anim = [POPDecayAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPScrollViewContentInset];
anim.fromValue = [NSValue valueWithUIEdgeInsets:fromEdgeInsets];
anim.velocity = [NSValue valueWithUIEdgeInsets:velocityEdgeInsets];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
id scrollView = [OCMockObject niceMockForClass:[UIScrollView class]];
[scrollView pop_addAnimation:anim forKey:nil];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
UIEdgeInsets lastEdgeInsets = [lastEvent.value UIEdgeInsetsValue];
XCTAssertTrue(!UIEdgeInsetsEqualToEdgeInsets(fromEdgeInsets, lastEdgeInsets), @"unexpected last edge insets value: %@", lastEvent);
XCTAssertTrue(lastEdgeInsets.top == lastEdgeInsets.left && lastEdgeInsets.bottom == lastEdgeInsets.right && lastEdgeInsets.top < lastEdgeInsets.bottom, @"unexpected last edge insets value: %@", lastEvent);
}
#endif
- (void)testEndValueOnReuse
{
POPAnimatable *circle = [POPAnimatable new];
POPDecayAnimation *anim = self._positionXAnimation;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// read out to value
CGFloat toValue = [anim.toValue floatValue];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 5.0, 1.0/60.0);
NSArray *stopEvent = [tracer eventsWithType:kPOPAnimationEventDidStop];
XCTAssertTrue(1 == stopEvent.count, @"unexpected events:%@", tracer.allEvents);
CGFloat lastValue = [[(POPAnimationValueEvent *)tracer.writeEvents.lastObject value] floatValue];
XCTAssertEqualWithAccuracy(toValue, lastValue, 0.5, @"expected:%f actual event:%@", lastValue, tracer.writeEvents.lastObject);
// update animation
anim.fromValue = @([anim.toValue floatValue] - 100);
anim.velocity = @(5000.);
// and reuse
[tracer reset];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 5.0, 1.0/60.0);
// verify decayed passed initial toValue
lastValue = [[(POPAnimationValueEvent *)tracer.writeEvents.lastObject value] floatValue];
XCTAssertTrue(lastValue > toValue, @"unexpected last value:%f", lastValue);
}
- (void)testComputedProperties
{
POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPositionX];
// set velocity, test duration
anim.velocity = @(100);
CGFloat d1 = anim.duration;
XCTAssertTrue(d1 > 0, @"unexpected duration %@", anim);
// set velocity, test duration
anim.velocity = @(1000);
CGFloat d2 = anim.duration;
XCTAssertTrue(d2 > d1, @"unexpected duration %@", anim);
// set from value, test to value
anim.fromValue = @(0);
CGFloat p1 = [anim.toValue floatValue];
XCTAssertTrue(p1 > [anim.fromValue floatValue], @"unexpected to value %@", anim);
// set from value, test to value
anim.fromValue = @(10000);
CGFloat p2 = [anim.toValue floatValue];
XCTAssertTrue(p2 > [anim.fromValue floatValue] && p2 > p1, @"unexpected to value %@", anim);
}
- (void)testNSCopyingSupportPOPDecayAnimation
{
POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:@"test_prop_name"];
configureConcretePropertyAnimation(anim);
anim.velocity = @(1.8888);
anim.deceleration = -9.8;
POPDecayAnimation *copy = [anim copy];
XCTAssertEqualObjects(copy.velocity, anim.velocity, @"expected equality; value1:%@ value2:%@", copy.velocity, anim.velocity);
XCTAssertEqual(copy.deceleration, anim.deceleration, @"expected equality; value1:%@ value2:%@", @(copy.deceleration), @(anim.deceleration));
}
@end

92
Clocker/pop/pop-tests/POPEaseInEaseOutAnimationTests.mm

@ -0,0 +1,92 @@
/**
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 <OCMock/OCMock.h>
#import <QuartzCore/QuartzCore.h>
#import <XCTest/XCTest.h>
#import <pop/POP.h>
#import <pop/POPAnimatorPrivate.h>
#import "POPAnimatable.h"
#import "POPAnimationTestsExtras.h"
#import "POPBaseAnimationTests.h"
@interface POPEaseInEaseOutAnimationTests : POPBaseAnimationTests
@end
@implementation POPEaseInEaseOutAnimationTests
- (void)testCompletion
{
// animation
// the default from, to and bounciness values are used
POPBasicAnimation *anim = [POPBasicAnimation easeInEaseOutAnimation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerScaleXY];
anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)];
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(0.97, 0.97)];
// delegate
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// expect start, progress & stop to all be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
anim.delegate = delegate;
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @0.1, @0.2, @0.4]);
[delegate verify];
}
- (void)testRectSupport
{
const CGRect fromRect = CGRectMake(0, 0, 0, 0);
const CGRect toRect = CGRectMake(100, 200, 200, 400);
POPBasicAnimation *anim = [POPBasicAnimation easeInEaseOutAnimation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
anim.fromValue = [NSValue valueWithCGRect:fromRect];
anim.toValue = [NSValue valueWithCGRect:toRect];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 1, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
CGRect lastRect = [lastEvent.value CGRectValue];
// verify last rect is to rect
XCTAssertTrue(CGRectEqualToRect(lastRect, toRect), @"unexpected last rect value: %@", lastEvent);
}
@end

687
Clocker/pop/pop-tests/POPSpringAnimationTests.mm

@ -0,0 +1,687 @@
/**
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 <OCMock/OCMock.h>
#import <QuartzCore/QuartzCore.h>
#import <XCTest/XCTest.h>
#import <pop/POPAnimation.h>
#import <pop/POPAnimationPrivate.h>
#import <pop/POPAnimator.h>
#import <pop/POPAnimatorPrivate.h>
#import <pop/POPAnimationExtras.h>
#import "POPAnimatable.h"
#import "POPAnimationInternal.h"
#import "POPAnimationTestsExtras.h"
#import "POPBaseAnimationTests.h"
#import "POPCGUtils.h"
@interface POPSpringAnimationTests : POPBaseAnimationTests
@end
@implementation POPSpringAnimationTests
static NSString *animationKey = @"key";
- (POPSpringAnimation *)_positionAnimation
{
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.fromValue = @0.0;
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.springBounciness = 4.0;
return anim;
}
- (void)testCompletion
{
// animation
// the default from, to and bounciness values are used
NSArray *markers = @[@0.5, @0.75, @1.0];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPosition];
anim.progressMarkers = markers;
XCTAssertEqualObjects(markers, anim.progressMarkers, @"%@ shoudl equal %@", markers, anim.progressMarkers);
// delegate
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// expect start, progress & stop to all be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
anim.delegate = delegate;
// layer
id layer = [OCMockObject niceMockForClass:[CALayer class]];
// expect position to be called
CGPoint position = CGPointMake(100, 100);
position = [(CALayer *)[[layer stub] andReturnValue:OCMOCK_VALUE(position)] position];
[layer pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @0.1, @0.2]);
[layer verify];
[delegate verify];
}
- (void)testConvergence
{
POPAnimatable *circle = [POPAnimatable new];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @0.0;
anim.toValue = @100.0;
anim.velocity = @100.0;
anim.springBounciness = 0.5;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 1.0, 1.0/60.0);
[tracer stop];
// finished
POPAnimationValueEvent *stopEvent = [[tracer eventsWithType:kPOPAnimationEventDidStop] lastObject];
XCTAssertEqualObjects(stopEvent.value, @YES, @"unexpected stop event %@", stopEvent);
// convergence threshold
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
NSUInteger toValueFrameCount = POPAnimationCountLastEventValues(writeEvents, anim.toValue, anim.property.threshold);
XCTAssertTrue(toValueFrameCount < kPOPAnimationConvergenceMaxFrameCount, @"unexpected convergence; toValueFrameCount: %lu", (unsigned long)toValueFrameCount);
}
- (void)testConvergenceRounded
{
POPAnimatable *circle = [POPAnimatable new];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @0.0;
anim.toValue = @100.0;
anim.velocity = @100.0;
anim.springBounciness = 0.5;
anim.roundingFactor = 1.0;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 1.0, 1.0/60.0);
[tracer stop];
// finished
POPAnimationValueEvent *stopEvent = [[tracer eventsWithType:kPOPAnimationEventDidStop] lastObject];
XCTAssertEqualObjects(stopEvent.value, @YES, @"unexpected stop event %@", stopEvent);
// convergence threshold
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
NSUInteger toValueFrameCount = POPAnimationCountLastEventValues(writeEvents, anim.toValue);
XCTAssertTrue(toValueFrameCount < kPOPAnimationConvergenceMaxFrameCount, @"unexpected convergence; toValueFrameCount: %lu", (unsigned long)toValueFrameCount);
}
- (void)testConvergenceClampedRounded
{
POPAnimatable *circle = [POPAnimatable new];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @0.0;
anim.toValue = @100.0;
anim.velocity = @100.0;
anim.springBounciness = 0.5;
anim.roundingFactor = 1.0;
anim.clampMode = kPOPAnimationClampEnd;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 1.0, 1.0/60.0);
[tracer stop];
// finished
POPAnimationValueEvent *stopEvent = [[tracer eventsWithType:kPOPAnimationEventDidStop] lastObject];
XCTAssertEqualObjects(stopEvent.value, @YES, @"unexpected stop event %@", stopEvent);
// convergence threshold
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
NSUInteger toValueFrameCount = POPAnimationCountLastEventValues(writeEvents, anim.toValue);
XCTAssertTrue(toValueFrameCount < kPOPAnimationConvergenceMaxFrameCount, @"unexpected convergence; toValueFrameCount: %lu", (unsigned long)toValueFrameCount);
}
- (void)testRemovedOnCompletionNoStartStopBasics
{
CALayer *layer = self.layer1;
POPSpringAnimation *anim = self._positionAnimation;
POPAnimationTracer *tracer = anim.tracer;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// cleanup
[layer pop_removeAllAnimations];
// configure animation
anim.removedOnCompletion = NO;
anim.toValue = @5.0;
anim.delegate = delegate;
__block BOOL completionBlock = NO;
__block BOOL completionBlockFinished = NO;
anim.completionBlock = ^(POPAnimation *a, BOOL finished) {
completionBlock = YES;
completionBlockFinished = finished;
};
// start tracer
[tracer start];
// expect start and stopped
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
[layer pop_addAnimation:anim forKey:animationKey];
POPAnimatorRenderDuration(self.animator, self.beginTime, 20.0, 1.0/60.0);
NSArray *allEvents = tracer.allEvents;
// verify delegate
[delegate verify];
XCTAssertTrue(completionBlock, @"completion block did not execute %@", allEvents);
XCTAssertTrue(completionBlockFinished, @"completion block did not finish %@", allEvents);
// assert animation has not been removed
XCTAssertTrue(anim == [layer pop_animationForKey:animationKey], @"expected animation on layer animations:%@", [layer pop_animationKeys]);
}
- (void)testRemovedOnCompletionNoContinuations
{
static NSString *animationKey = @"key";
static NSArray *toValues = @[@50.0, @100.0, @20.0, @80.0];
static NSArray *durations = @[@2.0, @0.3, @0.4, @2.0];
CALayer *layer = self.layer1;
POPSpringAnimation *anim = self._positionAnimation;
POPAnimationTracer *tracer = anim.tracer;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// cleanup
[layer pop_removeAllAnimations];
// configure animation
anim.removedOnCompletion = NO;
anim.delegate = delegate;
// start tracer
[tracer start];
__block CFTimeInterval beginTime;
__block BOOL completionBlock = NO;
__block BOOL completionBlockFinished = NO;
[toValues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *ptrStop) {
anim.toValue = obj;
if (0 == idx) {
[tracer reset];
// starts and stops
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
anim.completionBlock = ^(POPAnimation *a, BOOL finished) {
completionBlock = YES;
completionBlockFinished = finished;
};
[layer pop_addAnimation:anim forKey:animationKey];
beginTime = self.beginTime;
CFTimeInterval dt = [durations[idx] doubleValue];
POPAnimatorRenderDuration(self.animator, beginTime, dt, 1.0/60.0);
beginTime += dt;
NSArray *allEvents = tracer.allEvents;
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
// verify delegate
[delegate verify];
XCTAssertTrue(1 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
XCTAssertTrue(completionBlock, @"completion block did not execute %@", allEvents);
XCTAssertTrue(completionBlockFinished, @"completion block did not finish %@", allEvents);
} else if (toValues.count - 1 == idx) {
// continue stoped animation
[tracer reset];
completionBlock = NO;
completionBlockFinished = NO;
[[delegate expect] pop_animationDidStop:anim finished:YES];
CFTimeInterval dt = [durations[idx] doubleValue];
POPAnimatorRenderDuration(self.animator, beginTime, dt, 1.0/60.0);
beginTime += dt;
NSArray *allEvents = tracer.allEvents;
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
// verify delegate
[delegate verify];
XCTAssertTrue(1 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
XCTAssertTrue(completionBlock, @"completion block did not execute %@", allEvents);
XCTAssertTrue(completionBlockFinished, @"completion block did not finish %@", allEvents);
} else {
// continue stoped (idx = 1) or started animation
if (1 == idx) {
[[delegate expect] pop_animationDidStart:anim];
}
// reset state
[tracer reset];
completionBlock = NO;
completionBlockFinished = NO;
CFTimeInterval dt = [durations[idx] doubleValue];
POPAnimatorRenderDuration(self.animator, beginTime, dt, 1.0/60.0);
beginTime += dt;
NSArray *allEvents = tracer.allEvents;
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
// verify delegate
[delegate verify];
XCTAssertTrue(1 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
XCTAssertFalse(completionBlock, @"completion block did not execute %@ %@", anim, allEvents);
XCTAssertFalse(completionBlockFinished, @"completion block did not finish %@ %@", anim, allEvents);
}
// assert animation has not been removed
XCTAssertTrue(anim == [layer pop_animationForKey:animationKey], @"expected animation on layer animations:%@", [layer pop_animationKeys]);
}];
}
- (void)testNoOperationAnimation
{
const CGPoint initialValue = CGPointMake(100, 100);
CALayer *layer = self.layer1;
layer.position = initialValue;
[layer pop_removeAllAnimations];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPosition];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// starts and stops
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[layer pop_addAnimation:anim forKey:animationKey];
POPAnimatorRenderDuration(self.animator, self.beginTime, 5, 1.0/60.0);
// verify delegate
[delegate verify];
// verify number values
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
for (POPAnimationValueEvent *writeEvent in writeEvents) {
XCTAssertEqualObjects(writeEvent.value, [NSValue valueWithCGPoint:initialValue], @"unexpected write event:%@ anim:%@", writeEvent, anim);
}
}
- (void)testLazyValueInitialization
{
CALayer *layer = self.layer1;
layer.position = CGPointZero;
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @100.0;
anim.beginTime = self.beginTime + 0.3;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// add animation, but do not start
[layer pop_addAnimation:anim forKey:animationKey];
POPAnimatorRenderDuration(self.animator, self.beginTime, 0.2, 1.0/60.0);
XCTAssertNotNil(anim.fromValue, @"unexpected from value %@", anim);
XCTAssertNil(anim.toValue, @"unexpected to value %@", anim);
// start animation
POPAnimatorRenderDuration(self.animator, self.beginTime + 0.2, 0.2, 1.0/60.0);
XCTAssertNotNil(anim.fromValue, @"unexpected from value %@", anim);
XCTAssertNotNil(anim.toValue, @"unexpected to value %@", anim);
// continue running animation
anim.fromValue = nil;
anim.toValue = @200.0;
POPAnimatorRenderDuration(self.animator, self.beginTime + 0.4, 0.2, 1.0/60.0);
XCTAssertNotNil(anim.fromValue, @"unexpected from value %@", anim);
XCTAssertNotNil(anim.toValue, @"unexpected to value %@", anim);
}
- (void)testLatentSpring
{
POPSpringAnimation *translationAnimation = [POPSpringAnimation animation];
translationAnimation.dynamicsTension = 990;
translationAnimation.dynamicsFriction = 230;
translationAnimation.dynamicsMass = 1.0;
translationAnimation.property = [POPAnimatableProperty propertyWithName:kPOPLayerOpacity];
translationAnimation.removedOnCompletion = NO;
[self pop_addAnimation:translationAnimation forKey:@"test"];
POPAnimatorRenderDuration(self.animator, self.beginTime + 0.4, 0.2, 1.0/60.0);
}
- (void)testRectSupport
{
const CGRect fromRect = CGRectMake(0, 0, 0, 0);
const CGRect toRect = CGRectMake(100, 200, 200, 400);
const CGRect velocityRect = CGRectMake(1000, 1000, 1000, 1000);
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
anim.fromValue = [NSValue valueWithCGRect:fromRect];
anim.toValue = [NSValue valueWithCGRect:toRect];
anim.velocity = [NSValue valueWithCGRect:velocityRect];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
CGRect lastRect = [lastEvent.value CGRectValue];
// verify last rect is to rect
XCTAssertTrue(CGRectEqualToRect(lastRect, toRect), @"unexpected last rect value: %@", lastEvent);
}
#if TARGET_OS_IPHONE
- (void)testEdgeInsetsSupport
{
const UIEdgeInsets fromEdgeInsets = UIEdgeInsetsZero;
const UIEdgeInsets toEdgeInsets = UIEdgeInsetsMake(100, 200, 200, 400);
const UIEdgeInsets velocityEdgeInsets = UIEdgeInsetsMake(1000, 1000, 1000, 1000);
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPScrollViewContentInset];
anim.fromValue = [NSValue valueWithUIEdgeInsets:fromEdgeInsets];
anim.toValue = [NSValue valueWithUIEdgeInsets:toEdgeInsets];
anim.velocity = [NSValue valueWithUIEdgeInsets:velocityEdgeInsets];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
id scrollView = [OCMockObject niceMockForClass:[UIScrollView class]];
[scrollView pop_addAnimation:anim forKey:nil];
// expect final value to be set
[[scrollView expect] setContentInset:toEdgeInsets];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
// verify scroll view
[scrollView verify];
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
UIEdgeInsets lastEdgeInsets = [lastEvent.value UIEdgeInsetsValue];
// verify last insets are to insets
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(lastEdgeInsets, toEdgeInsets), @"unexpected last edge insets value: %@", lastEvent);
}
#endif
- (void)testColorSupport
{
CGFloat fromValues[4] = {1, 1, 1, 1};
CGFloat toValues[4] = {0, 0, 0, 1};
CGColorRef fromColor = POPCGColorRGBACreate(fromValues);
CGColorRef toColor = POPCGColorRGBACreate(toValues);
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
anim.fromValue = (__bridge_transfer id)fromColor;
anim.toValue = (__bridge_transfer id)toColor;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
// expect some interpolation
XCTAssertTrue(writeEvents.count > 1, @"unexpected write events %@", writeEvents);
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
// verify last written color is to color
POPAssertColorEqual((__bridge CGColorRef)lastEvent.value, (__bridge CGColorRef)anim.toValue);
}
static BOOL _floatingPointEqual(CGFloat a, CGFloat b)
{
CGFloat epsilon = 0.0001;
return std::abs(a - b) < epsilon;
}
- (void)testBouncinessSpeedToTensionFrictionConversion
{
CGFloat sampleBounciness = 12.0;
CGFloat sampleSpeed = 5.0;
CGFloat tension, friction, mass;
[POPSpringAnimation convertBounciness:sampleBounciness speed:sampleSpeed toTension:&tension friction:&friction mass:&mass];
CGFloat outBounciness, outSpeed;
[POPSpringAnimation convertTension:tension friction:friction toBounciness:&outBounciness speed:&outSpeed];
XCTAssertTrue(_floatingPointEqual(sampleBounciness, outBounciness) && _floatingPointEqual(sampleSpeed, outSpeed), @"(bounciness, speed) conversion failed. Mapped (%f, %f) back to (%f, %f)", sampleBounciness, sampleSpeed, outBounciness, outSpeed);
}
- (void)testTensionFrictionToBouncinessSpeedConversion
{
CGFloat sampleTension = 240.0;
CGFloat sampleFriction = 25.0;
CGFloat bounciness, speed;
[POPSpringAnimation convertTension:sampleTension friction:sampleFriction toBounciness:&bounciness speed:&speed];
CGFloat outTension, outFriction, outMass;
[POPSpringAnimation convertBounciness:bounciness speed:speed toTension:&outTension friction:&outFriction mass:&outMass];
XCTAssertTrue(_floatingPointEqual(sampleTension, outTension) && _floatingPointEqual(sampleFriction, outFriction), @"(tension, friction) conversion failed. Mapped (%f, %f) back to (%f, %f)", sampleTension, sampleFriction, outTension, outFriction);
}
- (void)testRemovedOnCompletionNoContinuationValues
{
static CGFloat fromValue = 400.0;
static NSArray *toValues = @[@200.0, @400.0];
// configure animation
POPSpringAnimation *anim = self._positionAnimation;
anim.fromValue = [NSNumber numberWithFloat:fromValue];
anim.toValue = toValues[0];
anim.removedOnCompletion = NO;
// run animation, from 400 to 200
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// assert reached to value
XCTAssertTrue(layer.position.x == [anim.toValue floatValue], @"unexpected value:%@ %@", layer, anim);
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// update to value, animate from 200 to 400
anim.toValue = toValues[1];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// verify from 200 to 400
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
XCTAssertTrue(writeEvents.count > 5, @"unexpected frame count %@", writeEvents);
CGFloat firstValue = [[(POPAnimationValueEvent *)[writeEvents firstObject] value] floatValue];
CGFloat lastValue = [[(POPAnimationValueEvent *)[writeEvents lastObject] value] floatValue];
XCTAssertEqualWithAccuracy(((CGFloat)[toValues[0] floatValue]), firstValue, 10, @"unexpected first value %@", writeEvents);
XCTAssertEqualWithAccuracy(((CGFloat)[toValues[1] floatValue]), lastValue, 10, @"unexpected last value %@", writeEvents);
}
- (void)testNilColor
{
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBackgroundColor];
#if TARGET_OS_IPHONE
anim.toValue = (__bridge id)[UIColor redColor].CGColor;
#else
anim.toValue = (__bridge id)[NSColor redColor].CGColor;
#endif
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// run animation
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// verify valid from color exists
CGColorRef fromColor = (__bridge CGColorRef)anim.fromValue;
XCTAssertTrue(fromColor, @"unexpected value %p", fromColor);
// verify from color clear
#if TARGET_OS_IPHONE
POPAssertColorEqual(fromColor, [UIColor clearColor].CGColor);
#else
POPAssertColorEqual(fromColor, [NSColor clearColor].CGColor);
#endif
}
- (void)testExcessiveJumpInTime
{
POPSpringAnimation *anim = self._positionAnimation;
anim.toValue = @(1000.0);
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:animationKey];
// render with large time jump
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @0.01, @300]);
// verify start stop
[delegate verify];
// verify last write event value
POPAnimationValueEvent *writeEvent = [[tracer eventsWithType:kPOPAnimationEventPropertyWrite] lastObject];
XCTAssertEqualObjects(writeEvent.value, anim.toValue, @"unexpected last write event %@", writeEvent);
}
- (void)testEquivalentFromToValues
{
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition];
anim.fromValue = [NSValue valueWithCGPoint:CGPointZero];
anim.toValue = [NSValue valueWithCGPoint:CGPointZero];
anim.velocity = [NSValue valueWithCGPoint:CGPointMake(1000.0, 1000.0)];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// run animation
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// verify last write event value
POPAnimationValueEvent *writeEvent = [[tracer eventsWithType:kPOPAnimationEventPropertyWrite] lastObject];
XCTAssertEqualObjects(writeEvent.value, anim.toValue, @"unexpected last write event %@", writeEvent);
}
- (void)testNSCopyingSupportPOPSpringAnimation
{
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:@"asdf_asdf_asdf"];
configureConcretePropertyAnimation(anim);
anim.velocity = @(4321);
anim.springBounciness = 11.1;
anim.springSpeed = 12;
anim.dynamicsTension = 0.83;
anim.dynamicsFriction = 0.97;
anim.dynamicsMass = 100;
POPSpringAnimation *copy = [anim copy];
XCTAssertEqualObjects(copy.velocity, anim.velocity, @"expected equality; value1:%@ value2:%@", copy.velocity, anim.velocity);
XCTAssertEqual(copy.springBounciness, anim.springBounciness, @"expected equality; value1:%@ value2:%@", @(copy.springBounciness), @(anim.springBounciness));
XCTAssertEqual(copy.springSpeed, anim.springSpeed, @"expected equality; value1:%@ value2:%@", @(copy.springSpeed), @(anim.springSpeed));
XCTAssertEqual(copy.dynamicsTension, anim.dynamicsTension, @"expected equality; value1:%@ value2:%@", @(copy.dynamicsTension), @(anim.dynamicsTension));
XCTAssertEqual(copy.dynamicsFriction, anim.dynamicsFriction, @"expected equality; value1:%@ value2:%@", @(copy.dynamicsFriction), @(anim.dynamicsFriction));
XCTAssertEqual(copy.dynamicsMass, anim.dynamicsMass, @"expected equality; value1:%@ value2:%@", @(copy.dynamicsMass), @(anim.dynamicsMass));
}
@end

22
Clocker/pop/pop-tests/pop-tests-ios-Info.plist

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.facebook.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

22
Clocker/pop/pop-tests/pop-tests-osx-Info.plist

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.facebook.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

24
Clocker/pop/pop-tests/pop-tests-tvos-Info.plist

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.facebook.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

21
Clocker/pop/pop.podspec

@ -0,0 +1,21 @@
Pod::Spec.new do |spec|
spec.name = 'pop'
spec.version = '1.0.9'
spec.license = { :type => 'BSD' }
spec.homepage = 'https://github.com/facebook/pop'
spec.authors = { 'Kimon Tsinteris' => 'kimon@mac.com' }
spec.summary = 'Extensible animation framework for iOS and OS X.'
spec.source = { :git => 'https://github.com/facebook/pop.git', :tag => '1.0.9' }
spec.source_files = 'pop/**/*.{h,m,mm,cpp}'
spec.public_header_files = 'pop/{POP,POPAnimatableProperty,POPAnimation,POPAnimationEvent,POPAnimationExtras,POPAnimationTracer,POPAnimator,POPBasicAnimation,POPCustomAnimation,POPDecayAnimation,POPDefines,POPGeometry,POPLayerExtras,POPPropertyAnimation,POPSpringAnimation}.h'
spec.requires_arc = true
spec.social_media_url = 'https://twitter.com/fbOpenSource'
spec.library = 'c++'
spec.pod_target_xcconfig = {
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11',
'CLANG_CXX_LIBRARY' => 'libc++'
}
spec.ios.deployment_target = '6.0'
spec.osx.deployment_target = '10.7'
spec.tvos.deployment_target = '9.0'
end

2071
Clocker/pop/pop.xcodeproj/project.pbxproj

File diff suppressed because it is too large Load Diff

7
Clocker/pop/pop.xcodeproj/project.xcworkspace/contents.xcworkspacedata generated

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:pop.xcodeproj">
</FileRef>
</Workspace>

99
Clocker/pop/pop.xcodeproj/xcshareddata/xcschemes/pop-ios-framework.xcscheme

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0640"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0B6BE74719FFD3B900762101"
BuildableName = "pop.framework"
BlueprintName = "pop-ios-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF01ED218C92B7F009E0AD1"
BuildableName = "pop-tests.xctest"
BlueprintName = "pop-tests-ios"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0B6BE74719FFD3B900762101"
BuildableName = "pop.framework"
BlueprintName = "pop-ios-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0B6BE74719FFD3B900762101"
BuildableName = "pop.framework"
BlueprintName = "pop-ios-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0B6BE74719FFD3B900762101"
BuildableName = "pop.framework"
BlueprintName = "pop-ios-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

110
Clocker/pop/pop.xcodeproj/xcshareddata/xcschemes/pop-ios-static.xcscheme

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0640"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EC191217162FB53A00E0CC76"
BuildableName = "libpop.a"
BlueprintName = "pop-ios-static"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF01ED218C92B7F009E0AD1"
BuildableName = "pop-tests.xctest"
BlueprintName = "pop-tests-ios"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF01ED218C92B7F009E0AD1"
BuildableName = "pop-tests.xctest"
BlueprintName = "pop-tests-ios"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EC191217162FB53A00E0CC76"
BuildableName = "libpop.a"
BlueprintName = "pop-ios-static"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EC191217162FB53A00E0CC76"
BuildableName = "libpop.a"
BlueprintName = "pop-ios-static"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EC191217162FB53A00E0CC76"
BuildableName = "libpop.a"
BlueprintName = "pop-ios-static"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

78
Clocker/pop/pop.xcodeproj/xcshareddata/xcschemes/pop-osx-framework.xcscheme

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0640"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EC68857E18C7B60000C6194C"
BuildableName = "pop.framework"
BlueprintName = "pop-osx-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EC7E319818C93D6500B38170"
BuildableName = "pop-tests.xctest"
BlueprintName = "pop-tests-osx"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EC68857E18C7B60000C6194C"
BuildableName = "pop.framework"
BlueprintName = "pop-osx-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

99
Clocker/pop/pop.xcodeproj/xcshareddata/xcschemes/pop-tvos-framework.xcscheme

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0755AE4E1BEA15950094AB41"
BuildableName = "pop.framework"
BlueprintName = "pop-tvos-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0755AE8B1BEA19580094AB41"
BuildableName = "pop-tests.xctest"
BlueprintName = "pop-tests-tvos"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0755AE4E1BEA15950094AB41"
BuildableName = "pop.framework"
BlueprintName = "pop-tvos-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0755AE4E1BEA15950094AB41"
BuildableName = "pop.framework"
BlueprintName = "pop-tvos-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0755AE4E1BEA15950094AB41"
BuildableName = "pop.framework"
BlueprintName = "pop-tvos-framework"
ReferencedContainer = "container:pop.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

10
Clocker/pop/pop.xcworkspace/contents.xcworkspacedata generated

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:pop.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

29
Clocker/pop/pop/POP.h

@ -0,0 +1,29 @@
/**
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/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 */

67
Clocker/pop/pop/POPAction.h

@ -0,0 +1,67 @@
/**
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 */

251
Clocker/pop/pop/POPAnimatableProperty.h

@ -0,0 +1,251 @@
/**
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>
@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) void (^readBlock)(id obj, CGFloat values[]);
/**
@abstract Block used to write values from an array of floats into a property.
*/
@property (readonly, nonatomic, copy) void (^writeBlock)(id obj, const CGFloat values[]);
/**
@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) void (^readBlock)(id obj, CGFloat values[]);
/**
@abstract A read-write version of POPAnimatableProperty writeBlock property.
*/
@property (readwrite, nonatomic, copy) void (^writeBlock)(id obj, const CGFloat values[]);
/**
@abstract A read-write version of POPAnimatableProperty threshold property.
*/
@property (readwrite, nonatomic, assign) CGFloat threshold;
@end
/**
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

1307
Clocker/pop/pop/POPAnimatableProperty.mm

File diff suppressed because it is too large Load Diff

188
Clocker/pop/pop/POPAnimation.h

@ -0,0 +1,188 @@
/**
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.
@param 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

303
Clocker/pop/pop/POPAnimation.mm

@ -0,0 +1,303 @@
/**
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

69
Clocker/pop/pop/POPAnimationEvent.h

@ -0,0 +1,69 @@
/**
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

108
Clocker/pop/pop/POPAnimationEvent.mm

@ -0,0 +1,108 @@
/**
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

41
Clocker/pop/pop/POPAnimationEventInternal.h

@ -0,0 +1,41 @@
/**
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

43
Clocker/pop/pop/POPAnimationExtras.h

@ -0,0 +1,43 @@
/**
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();
@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

117
Clocker/pop/pop/POPAnimationExtras.mm

@ -0,0 +1,117 @@
/**
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

505
Clocker/pop/pop/POPAnimationInternal.h

@ -0,0 +1,505 @@
/**
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 "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

16
Clocker/pop/pop/POPAnimationPrivate.h

@ -0,0 +1,16 @@
/**
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)))

103
Clocker/pop/pop/POPAnimationRuntime.h

@ -0,0 +1,103 @@
/**
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 <Foundation/Foundation.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/write block typedefs for convenience.
*/
typedef void(^pop_animatable_read_block)(id obj, CGFloat *value);
typedef void(^pop_animatable_write_block)(id obj, const CGFloat *value);
/**
Read object value and return a Vector4r.
*/
NS_INLINE Vector4r read_values(pop_animatable_read_block 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";
}

329
Clocker/pop/pop/POPAnimationRuntime.mm

@ -0,0 +1,329 @@
/**
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;
}

60
Clocker/pop/pop/POPAnimationTracer.h

@ -0,0 +1,60 @@
/**
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

191
Clocker/pop/pop/POPAnimationTracer.mm

@ -0,0 +1,191 @@
/**
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;
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 *)self->_animation velocity]];
}
}
if (recordAnimation) {
event.animationDescription = [self->_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

96
Clocker/pop/pop/POPAnimationTracerInternal.h

@ -0,0 +1,96 @@
/**
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

47
Clocker/pop/pop/POPAnimator.h

@ -0,0 +1,47 @@
/**
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;
/**
@abstract The optional animator delegate.
*/
@property (weak, nonatomic) id<POPAnimatorDelegate> delegate;
@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

806
Clocker/pop/pop/POPAnimator.mm

@ -0,0 +1,806 @@
/**
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
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;
static BOOL _disableBackgroundThread = YES;
@interface POPAnimator ()
{
#if TARGET_OS_IPHONE
CADisplayLink *_displayLink;
#else
CVDisplayLinkRef _displayLink;
int32_t _enqueuedRender;
#endif
POPAnimatorItemList _list;
CFMutableDictionaryRef _dict;
NSMutableArray *_observers;
POPAnimatorItemList _pendingList;
CFRunLoopObserverRef _pendingListObserver;
CFTimeInterval _slowMotionStartTime;
CFTimeInterval _slowMotionLastTime;
CFTimeInterval _slowMotionAccumulator;
CFTimeInterval _beginTime;
OSSpinLock _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 (paused == CVDisplayLinkIsRunning(self->_displayLink)) {
FBLogAnimInfo(paused ? @"pausing display link" : @"unpausing display link");
if (paused) {
CVDisplayLinkStop(self->_displayLink);
} else {
CVDisplayLinkStart(self->_displayLink);
}
}
#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()) {
pop_animatable_write_block 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) {
pop_animatable_read_block 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 {
pop_animatable_read_block 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
OSSpinLockLock(&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
OSSpinLockUnlock(&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
OSSpinLockLock(&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
OSSpinLockUnlock(&self->_lock);
}
}
+ (id)sharedAnimator
{
static POPAnimator* _animator = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_animator = [[POPAnimator alloc] init];
});
return _animator;
}
+ (BOOL)disableBackgroundThread
{
return _disableBackgroundThread;
}
+ (void)setDisableBackgroundThread:(BOOL)flag
{
_disableBackgroundThread = flag;
}
#pragma mark - Lifecycle
- (id)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
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
CVDisplayLinkSetOutputCallback(_displayLink, displayLinkCallback, (__bridge void *)self);
#endif
_dict = POPDictionaryCreateMutableWeakPointerToStrongObject(5);
_lock = OS_SPINLOCK_INIT;
return self;
}
- (void)dealloc
{
#if TARGET_OS_IPHONE
[_displayLink invalidate];
#else
CVDisplayLinkStop(_displayLink);
CVDisplayLinkRelease(_displayLink);
#endif
[self _clearPendingListObserver];
}
#pragma mark - Utility
- (void)_processPendingList
{
// rendering pending animations
CFTimeInterval time = [self _currentRenderTime];
[self _renderTime:(0 != _beginTime) ? _beginTime : time items:_pendingList];
// lock
OSSpinLockLock(&_lock);
// clear list and observer
_pendingList.clear();
[self _clearPendingListObserver];
// unlock
OSSpinLockUnlock(&_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
OSSpinLockLock(&_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
OSSpinLockUnlock(&_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
OSSpinLockLock(&_lock);
// count active animations
const NSUInteger count = items.size();
if (0 == count) {
// unlock
OSSpinLockUnlock(&_lock);
} else {
// copy list into vector
std::vector<POPAnimatorItemRef> vector{ items.begin(), items.end() };
// unlock
OSSpinLockUnlock(&_lock);
for (auto item : vector) {
[self _renderTime:time item:item];
}
}
// notify observers
for (id observer in self.observers) {
[observer animatorDidAnimate:(id)self];
}
// lock
OSSpinLockLock(&_lock);
// update display link
updateDisplayLink(self);
// unlock
OSSpinLockUnlock(&_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
OSSpinLockLock(&_lock);
// get observers
NSArray *observers = 0 != _observers.count ? [_observers copy] : nil;
// unlock
OSSpinLockUnlock(&_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
OSSpinLockLock(&_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
OSSpinLockUnlock(&_lock);
if (existingAnim == anim) {
return;
}
[self removeAnimationForObject:obj key:key cleanupDict:NO];
// lock
OSSpinLockLock(&_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
OSSpinLockUnlock(&_lock);
// schedule runloop processing of pending animations
[self _scheduleProcessPendingList];
}
- (void)removeAllAnimationsForObject:(id)obj
{
// lock
OSSpinLockLock(&_lock);
NSArray *animations = [(__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj) allValues];
CFDictionaryRemoveValue(_dict, (__bridge void *)obj);
// unlock
OSSpinLockUnlock(&_lock);
if (0 == animations.count) {
return;
}
NSHashTable *animationSet = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:animations.count];
for (id animation in animations) {
[animationSet addObject:animation];
}
// lock
OSSpinLockLock(&_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
OSSpinLockUnlock(&_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
OSSpinLockLock(&_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
OSSpinLockUnlock(&_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
OSSpinLockLock(&_lock);
// get keys
NSArray *keys = [(__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj) allKeys];
// unlock
OSSpinLockUnlock(&_lock);
return keys;
}
- (id)animationForObject:(id)obj key:(NSString *)key
{
// lock
OSSpinLockLock(&_lock);
// lookup animation
NSDictionary *keyAnimationsDict = (__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj);
POPAnimation *animation = keyAnimationsDict[key];
// unlock
OSSpinLockUnlock(&_lock);
return animation;
}
- (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
OSSpinLockLock(&_lock);
if (!_observers) {
// use ordered collection for deterministic callout
_observers = [[NSMutableArray alloc] initWithCapacity:1];
}
[_observers addObject:observer];
updateDisplayLink(self);
// unlock
OSSpinLockUnlock(&_lock);
}
- (void)removeObserver:(id<POPAnimatorObserving>)observer
{
NSAssert(nil != observer, @"attempting to remove nil %@ observer", self);
if (nil == observer) {
return;
}
// lock
OSSpinLockLock(&_lock);
[_observers removeObject:observer];
updateDisplayLink(self);
// unlock
OSSpinLockUnlock(&_lock);
}
@end

68
Clocker/pop/pop/POPAnimatorPrivate.h

@ -0,0 +1,68 @@
/**
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;
#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

71
Clocker/pop/pop/POPBasicAnimation.h

@ -0,0 +1,71 @@
/**
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

106
Clocker/pop/pop/POPBasicAnimation.mm

@ -0,0 +1,106 @@
/**
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

97
Clocker/pop/pop/POPBasicAnimationInternal.h

@ -0,0 +1,97 @@
/**
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;

152
Clocker/pop/pop/POPCGUtils.h

@ -0,0 +1,152 @@
/**
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

150
Clocker/pop/pop/POPCGUtils.mm

@ -0,0 +1,150 @@
/**
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 = [color numberOfComponents];
CGFloat components[numberOfComponents];
CGColorSpaceRef colorSpace = [[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

46
Clocker/pop/pop/POPCustomAnimation.h

@ -0,0 +1,46 @@
/**
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

75
Clocker/pop/pop/POPCustomAnimation.mm

@ -0,0 +1,75 @@
/**
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

66
Clocker/pop/pop/POPDecayAnimation.h

@ -0,0 +1,66 @@
/**
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

203
Clocker/pop/pop/POPDecayAnimation.mm

@ -0,0 +1,203 @@
/**
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

127
Clocker/pop/pop/POPDecayAnimationInternal.h

@ -0,0 +1,127 @@
/**
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;

37
Clocker/pop/pop/POPDefines.h

@ -0,0 +1,37 @@
/**
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

73
Clocker/pop/pop/POPGeometry.h

@ -0,0 +1,73 @@
/**
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

94
Clocker/pop/pop/POPGeometry.mm

@ -0,0 +1,94 @@
/**
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 valueWithBytes:&vec4 objCType:@encode(SCNVector4)];
}
@end
#endif
#endif

196
Clocker/pop/pop/POPLayerExtras.h

@ -0,0 +1,196 @@
/**
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/QuartzCore.h>
#import <pop/POPDefines.h>
POP_EXTERN_C_BEGIN
#pragma mark - Scale
/**
@abstract Returns layer scale factor for the x axis.
*/
extern CGFloat POPLayerGetScaleX(CALayer *l);
/**
@abstract Set layer scale factor for the x axis.
*/
extern void POPLayerSetScaleX(CALayer *l, CGFloat f);
/**
@abstract Returns layer scale factor for the y axis.
*/
extern CGFloat POPLayerGetScaleY(CALayer *l);
/**
@abstract Set layer scale factor for the y axis.
*/
extern void POPLayerSetScaleY(CALayer *l, CGFloat f);
/**
@abstract Returns layer scale factor for the z axis.
*/
extern CGFloat POPLayerGetScaleZ(CALayer *l);
/**
@abstract Set layer scale factor for the z axis.
*/
extern void POPLayerSetScaleZ(CALayer *l, CGFloat f);
/**
@abstract Returns layer scale factors for x and y access as point.
*/
extern CGPoint POPLayerGetScaleXY(CALayer *l);
/**
@abstract Sets layer x and y scale factors given point.
*/
extern void POPLayerSetScaleXY(CALayer *l, CGPoint p);
#pragma mark - Translation
/**
@abstract Returns layer translation factor for the x axis.
*/
extern CGFloat POPLayerGetTranslationX(CALayer *l);
/**
@abstract Set layer translation factor for the x axis.
*/
extern void POPLayerSetTranslationX(CALayer *l, CGFloat f);
/**
@abstract Returns layer translation factor for the y axis.
*/
extern CGFloat POPLayerGetTranslationY(CALayer *l);
/**
@abstract Set layer translation factor for the y axis.
*/
extern void POPLayerSetTranslationY(CALayer *l, CGFloat f);
/**
@abstract Returns layer translation factor for the z axis.
*/
extern CGFloat POPLayerGetTranslationZ(CALayer *l);
/**
@abstract Set layer translation factor for the z axis.
*/
extern void POPLayerSetTranslationZ(CALayer *l, CGFloat f);
/**
@abstract Returns layer translation factors for x and y access as point.
*/
extern CGPoint POPLayerGetTranslationXY(CALayer *l);
/**
@abstract Sets layer x and y translation factors given point.
*/
extern void POPLayerSetTranslationXY(CALayer *l, CGPoint p);
#pragma mark - Rotation
/**
@abstract Returns layer rotation, in radians, in the X axis.
*/
extern CGFloat POPLayerGetRotationX(CALayer *l);
/**
@abstract Sets layer rotation, in radians, in the X axis.
*/
extern void POPLayerSetRotationX(CALayer *l, CGFloat f);
/**
@abstract Returns layer rotation, in radians, in the Y axis.
*/
extern CGFloat POPLayerGetRotationY(CALayer *l);
/**
@abstract Sets layer rotation, in radians, in the Y axis.
*/
extern void POPLayerSetRotationY(CALayer *l, CGFloat f);
/**
@abstract Returns layer rotation, in radians, in the Z axis.
*/
extern CGFloat POPLayerGetRotationZ(CALayer *l);
/**
@abstract Sets layer rotation, in radians, in the Z axis.
*/
extern void POPLayerSetRotationZ(CALayer *l, CGFloat f);
/**
@abstract Returns layer rotation, in radians, in the Z axis.
*/
extern CGFloat POPLayerGetRotation(CALayer *l);
/**
@abstract Sets layer rotation, in radians, in the Z axis.
*/
extern void POPLayerSetRotation(CALayer *l, CGFloat f);
#pragma mark - Sublayer Scale
/**
@abstract Returns sublayer scale factors for x and y access as point.
*/
extern CGPoint POPLayerGetSubScaleXY(CALayer *l);
/**
@abstract Sets sublayer x and y scale factors given point.
*/
extern void POPLayerSetSubScaleXY(CALayer *l, CGPoint p);
#pragma mark - Sublayer Translation
/**
@abstract Returns sublayer translation factor for the x axis.
*/
extern CGFloat POPLayerGetSubTranslationX(CALayer *l);
/**
@abstract Set sublayer translation factor for the x axis.
*/
extern void POPLayerSetSubTranslationX(CALayer *l, CGFloat f);
/**
@abstract Returns sublayer translation factor for the y axis.
*/
extern CGFloat POPLayerGetSubTranslationY(CALayer *l);
/**
@abstract Set sublayer translation factor for the y axis.
*/
extern void POPLayerSetSubTranslationY(CALayer *l, CGFloat f);
/**
@abstract Returns sublayer translation factor for the z axis.
*/
extern CGFloat POPLayerGetSubTranslationZ(CALayer *l);
/**
@abstract Set sublayer translation factor for the z axis.
*/
extern void POPLayerSetSubTranslationZ(CALayer *l, CGFloat f);
/**
@abstract Returns sublayer translation factors for x and y access as point.
*/
extern CGPoint POPLayerGetSubTranslationXY(CALayer *l);
/**
@abstract Sets sublayer x and y translation factors given point.
*/
extern void POPLayerSetSubTranslationXY(CALayer *l, CGPoint p);
POP_EXTERN_C_END

288
Clocker/pop/pop/POPLayerExtras.mm

@ -0,0 +1,288 @@
/**
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 "POPLayerExtras.h"
#include "TransformationMatrix.h"
using namespace WebCore;
#define DECOMPOSE_TRANSFORM(L) \
TransformationMatrix _m(L.transform); \
TransformationMatrix::DecomposedType _d; \
_m.decompose(_d);
#define RECOMPOSE_TRANSFORM(L) \
_m.recompose(_d); \
L.transform = _m.transform3d();
#define RECOMPOSE_ROT_TRANSFORM(L) \
_m.recompose(_d, true); \
L.transform = _m.transform3d();
#define DECOMPOSE_SUBLAYER_TRANSFORM(L) \
TransformationMatrix _m(L.sublayerTransform); \
TransformationMatrix::DecomposedType _d; \
_m.decompose(_d);
#define RECOMPOSE_SUBLAYER_TRANSFORM(L) \
_m.recompose(_d); \
L.sublayerTransform = _m.transform3d();
#pragma mark - Scale
NS_INLINE void ensureNonZeroValue(CGFloat &f)
{
if (f == 0) {
f = 1e-6;
}
}
NS_INLINE void ensureNonZeroValue(CGPoint &p)
{
if (p.x == 0 && p.y == 0) {
p.x = 1e-6;
p.y = 1e-6;
}
}
CGFloat POPLayerGetScaleX(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return _d.scaleX;
}
void POPLayerSetScaleX(CALayer *l, CGFloat f)
{
ensureNonZeroValue(f);
DECOMPOSE_TRANSFORM(l);
_d.scaleX = f;
RECOMPOSE_TRANSFORM(l);
}
CGFloat POPLayerGetScaleY(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return _d.scaleY;
}
void POPLayerSetScaleY(CALayer *l, CGFloat f)
{
ensureNonZeroValue(f);
DECOMPOSE_TRANSFORM(l);
_d.scaleY = f;
RECOMPOSE_TRANSFORM(l);
}
CGFloat POPLayerGetScaleZ(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return _d.scaleZ;
}
void POPLayerSetScaleZ(CALayer *l, CGFloat f)
{
ensureNonZeroValue(f);
DECOMPOSE_TRANSFORM(l);
_d.scaleZ = f;
RECOMPOSE_TRANSFORM(l);
}
CGPoint POPLayerGetScaleXY(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return CGPointMake(_d.scaleX, _d.scaleY);
}
void POPLayerSetScaleXY(CALayer *l, CGPoint p)
{
ensureNonZeroValue(p);
DECOMPOSE_TRANSFORM(l);
_d.scaleX = p.x;
_d.scaleY = p.y;
RECOMPOSE_TRANSFORM(l);
}
#pragma mark - Translation
CGFloat POPLayerGetTranslationX(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return _d.translateX;
}
void POPLayerSetTranslationX(CALayer *l, CGFloat f)
{
DECOMPOSE_TRANSFORM(l);
_d.translateX = f;
RECOMPOSE_TRANSFORM(l);
}
CGFloat POPLayerGetTranslationY(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return _d.translateY;
}
void POPLayerSetTranslationY(CALayer *l, CGFloat f)
{
DECOMPOSE_TRANSFORM(l);
_d.translateY = f;
RECOMPOSE_TRANSFORM(l);
}
CGFloat POPLayerGetTranslationZ(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return _d.translateZ;
}
void POPLayerSetTranslationZ(CALayer *l, CGFloat f)
{
DECOMPOSE_TRANSFORM(l);
_d.translateZ = f;
RECOMPOSE_TRANSFORM(l);
}
CGPoint POPLayerGetTranslationXY(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return CGPointMake(_d.translateX, _d.translateY);
}
void POPLayerSetTranslationXY(CALayer *l, CGPoint p)
{
DECOMPOSE_TRANSFORM(l);
_d.translateX = p.x;
_d.translateY = p.y;
RECOMPOSE_TRANSFORM(l);
}
#pragma mark - Rotation
CGFloat POPLayerGetRotationX(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return _d.rotateX;
}
void POPLayerSetRotationX(CALayer *l, CGFloat f)
{
DECOMPOSE_TRANSFORM(l);
_d.rotateX = f;
RECOMPOSE_ROT_TRANSFORM(l);
}
CGFloat POPLayerGetRotationY(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return _d.rotateY;
}
void POPLayerSetRotationY(CALayer *l, CGFloat f)
{
DECOMPOSE_TRANSFORM(l);
_d.rotateY = f;
RECOMPOSE_ROT_TRANSFORM(l);
}
CGFloat POPLayerGetRotationZ(CALayer *l)
{
DECOMPOSE_TRANSFORM(l);
return _d.rotateZ;
}
void POPLayerSetRotationZ(CALayer *l, CGFloat f)
{
DECOMPOSE_TRANSFORM(l);
_d.rotateZ = f;
RECOMPOSE_ROT_TRANSFORM(l);
}
CGFloat POPLayerGetRotation(CALayer *l)
{
return POPLayerGetRotationZ(l);
}
void POPLayerSetRotation(CALayer *l, CGFloat f)
{
POPLayerSetRotationZ(l, f);
}
#pragma mark - Sublayer Scale
CGPoint POPLayerGetSubScaleXY(CALayer *l)
{
DECOMPOSE_SUBLAYER_TRANSFORM(l);
return CGPointMake(_d.scaleX, _d.scaleY);
}
void POPLayerSetSubScaleXY(CALayer *l, CGPoint p)
{
ensureNonZeroValue(p);
DECOMPOSE_SUBLAYER_TRANSFORM(l);
_d.scaleX = p.x;
_d.scaleY = p.y;
RECOMPOSE_SUBLAYER_TRANSFORM(l);
}
#pragma mark - Sublayer Translation
extern CGFloat POPLayerGetSubTranslationX(CALayer *l)
{
DECOMPOSE_SUBLAYER_TRANSFORM(l);
return _d.translateX;
}
extern void POPLayerSetSubTranslationX(CALayer *l, CGFloat f)
{
DECOMPOSE_SUBLAYER_TRANSFORM(l);
_d.translateX = f;
RECOMPOSE_SUBLAYER_TRANSFORM(l);
}
extern CGFloat POPLayerGetSubTranslationY(CALayer *l)
{
DECOMPOSE_SUBLAYER_TRANSFORM(l);
return _d.translateY;
}
extern void POPLayerSetSubTranslationY(CALayer *l, CGFloat f)
{
DECOMPOSE_SUBLAYER_TRANSFORM(l);
_d.translateY = f;
RECOMPOSE_SUBLAYER_TRANSFORM(l);
}
extern CGFloat POPLayerGetSubTranslationZ(CALayer *l)
{
DECOMPOSE_SUBLAYER_TRANSFORM(l);
return _d.translateZ;
}
extern void POPLayerSetSubTranslationZ(CALayer *l, CGFloat f)
{
DECOMPOSE_SUBLAYER_TRANSFORM(l);
_d.translateZ = f;
RECOMPOSE_SUBLAYER_TRANSFORM(l);
}
extern CGPoint POPLayerGetSubTranslationXY(CALayer *l)
{
DECOMPOSE_SUBLAYER_TRANSFORM(l);
return CGPointMake(_d.translateX, _d.translateY);
}
extern void POPLayerSetSubTranslationXY(CALayer *l, CGPoint p)
{
DECOMPOSE_SUBLAYER_TRANSFORM(l);
_d.translateX = p.x;
_d.translateY = p.y;
RECOMPOSE_SUBLAYER_TRANSFORM(l);
}

56
Clocker/pop/pop/POPMath.h

@ -0,0 +1,56 @@
/**
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 <CoreGraphics/CoreGraphics.h>
#import "POPDefines.h"
#import "POPVector.h"
NS_INLINE CGFloat sqrtr(CGFloat f)
{
#if CGFLOAT_IS_DOUBLE
return sqrt(f);
#else
return sqrtf(f);
#endif
}
// round to nearest sub; pass 2.0 to round to every 0.5 (eg: retina pixels)
NS_INLINE CGFloat POPSubRound(CGFloat f, CGFloat sub)
{
return round(f * sub) / sub;
}
#define MIX(a, b, f) ((a) + (f) * ((b) - (a)))
// the longer the duration, the higher the necessary precision
#define SOLVE_EPS(dur) (1. / (1000. * (dur)))
#define _EQLF_(x, y, epsilon) (fabsf ((x) - (y)) < epsilon)
extern void POPInterpolateVector(NSUInteger count, CGFloat *dst, const CGFloat *from, const CGFloat *to, CGFloat f);
extern double POPTimingFunctionSolve(const double vec[4], double t, double eps);
// quadratic mapping of t [0, 1] to [start, end]
extern double POPQuadraticOutInterpolation(double t, double start, double end);
// normalize value to [0, 1] based on its range [startValue, endValue]
extern double POPNormalize(double value, double startValue, double endValue);
// project a normalized value [0, 1] to a given range [start, end]
extern double POPProjectNormal(double n, double start, double end);
// solve a quadratic equation of the form a * x^2 + b * x + c = 0
extern void POPQuadraticSolve(CGFloat a, CGFloat b, CGFloat c, CGFloat &x1, CGFloat &x2);
// for a given tension return the bouncy 3 friction that produces no bounce
extern double POPBouncy3NoBounce(double tension);

83
Clocker/pop/pop/POPMath.mm

@ -0,0 +1,83 @@
/**
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 "POPMath.h"
#import "POPAnimationPrivate.h"
#import "UnitBezier.h"
void POPInterpolateVector(NSUInteger count, CGFloat *dst, const CGFloat *from, const CGFloat *to, CGFloat f)
{
for (NSUInteger idx = 0; idx < count; idx++) {
dst[idx] = MIX(from[idx], to[idx], f);
}
}
double POPTimingFunctionSolve(const double vec[4], double t, double eps)
{
WebCore::UnitBezier bezier(vec[0], vec[1], vec[2], vec[3]);
return bezier.solve(t, eps);
}
double POPNormalize(double value, double startValue, double endValue)
{
return (value - startValue) / (endValue - startValue);
}
double POPProjectNormal(double n, double start, double end)
{
return start + (n * (end - start));
}
static double linear_interpolation(double t, double start, double end)
{
return t * end + (1.f - t) * start;
}
double POPQuadraticOutInterpolation(double t, double start, double end)
{
return linear_interpolation(2*t - t*t, start, end);
}
static double b3_friction1(double x)
{
return (0.0007 * pow(x, 3)) - (0.031 * pow(x, 2)) + 0.64 * x + 1.28;
}
static double b3_friction2(double x)
{
return (0.000044 * pow(x, 3)) - (0.006 * pow(x, 2)) + 0.36 * x + 2.;
}
static double b3_friction3(double x)
{
return (0.00000045 * pow(x, 3)) - (0.000332 * pow(x, 2)) + 0.1078 * x + 5.84;
}
double POPBouncy3NoBounce(double tension)
{
double friction = 0;
if (tension <= 18.) {
friction = b3_friction1(tension);
} else if (tension > 18 && tension <= 44) {
friction = b3_friction2(tension);
} else if (tension > 44) {
friction = b3_friction3(tension);
} else {
assert(false);
}
return friction;
}
void POPQuadraticSolve(CGFloat a, CGFloat b, CGFloat c, CGFloat &x1, CGFloat &x2)
{
CGFloat discriminant = sqrt(b * b - 4 * a * c);
x1 = (-b + discriminant) / (2 * a);
x2 = (-b - discriminant) / (2 * a);
}

65
Clocker/pop/pop/POPPropertyAnimation.h

@ -0,0 +1,65 @@
/**
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/POPAnimatableProperty.h>
#import <pop/POPAnimation.h>
/**
@abstract Flags for clamping animation values.
@discussion Animation values can optionally be clamped to avoid overshoot. kPOPAnimationClampStart ensures values are more than fromValue and kPOPAnimationClampEnd ensures values are less than toValue.
*/
typedef NS_OPTIONS(NSUInteger, POPAnimationClampFlags)
{
kPOPAnimationClampNone = 0,
kPOPAnimationClampStart = 1UL << 0,
kPOPAnimationClampEnd = 1UL << 1,
kPOPAnimationClampBoth = kPOPAnimationClampStart | kPOPAnimationClampEnd,
};
/**
@abstract The semi-concrete property animation subclass.
*/
@interface POPPropertyAnimation : POPAnimation
/**
@abstract The property to animate.
*/
@property (strong, nonatomic) POPAnimatableProperty *property;
/**
@abstract The value to animate from.
@discussion The value type should match the property. If unspecified, the value is initialized to the object's current value on animation start.
*/
@property (copy, nonatomic) id fromValue;
/**
@abstract The value to animate to.
@discussion The value type should match the property. If unspecified, the value is initialized to the object's current value on animation start.
*/
@property (copy, nonatomic) id toValue;
/**
@abstract The rounding factor applied to the current animated value.
@discussion Specify 1.0 to animate between integral values. Defaults to 0 meaning no rounding.
*/
@property (assign, nonatomic) CGFloat roundingFactor;
/**
@abstract The clamp mode applied to the current animated value.
@discussion See {@ref POPAnimationClampFlags} for possible values. Defaults to kPOPAnimationClampNone.
*/
@property (assign, nonatomic) NSUInteger clampMode;
/**
@abstract The flag indicating whether values should be "added" each frame, rather than set.
@discussion Addition may be type dependent. Defaults to NO.
*/
@property (assign, nonatomic, getter = isAdditive) BOOL additive;
@end

125
Clocker/pop/pop/POPPropertyAnimation.mm

@ -0,0 +1,125 @@
/**
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 "POPPropertyAnimationInternal.h"
@implementation POPPropertyAnimation
#pragma mark - Lifecycle
#undef __state
#define __state ((POPPropertyAnimationState *)_state)
- (void)_initState
{
_state = new POPPropertyAnimationState(self);
}
#pragma mark - Properties
DEFINE_RW_FLAG(POPPropertyAnimationState, additive, isAdditive, setAdditive:);
DEFINE_RW_PROPERTY(POPPropertyAnimationState, roundingFactor, setRoundingFactor:, CGFloat);
DEFINE_RW_PROPERTY(POPPropertyAnimationState, clampMode, setClampMode:, NSUInteger);
DEFINE_RW_PROPERTY_OBJ(POPPropertyAnimationState, property, setProperty:, POPAnimatableProperty*, ((POPPropertyAnimationState*)_state)->updatedDynamicsThreshold(););
DEFINE_RW_PROPERTY_OBJ_COPY(POPPropertyAnimationState, progressMarkers, setProgressMarkers:, NSArray*, ((POPPropertyAnimationState*)_state)->updatedProgressMarkers(););
- (id)fromValue
{
return POPBox(__state->fromVec, __state->valueType);
}
- (void)setFromValue:(id)aValue
{
POPPropertyAnimationState *s = __state;
VectorRef vec = POPUnbox(aValue, s->valueType, s->valueCount, YES);
if (!vec_equal(vec, s->fromVec)) {
s->fromVec = vec;
if (s->tracing) {
[s->tracer updateFromValue:aValue];
}
}
}
- (id)toValue
{
return POPBox(__state->toVec, __state->valueType);
}
- (void)setToValue:(id)aValue
{
POPPropertyAnimationState *s = __state;
VectorRef vec = POPUnbox(aValue, s->valueType, s->valueCount, YES);
if (!vec_equal(vec, s->toVec)) {
s->toVec = vec;
// invalidate to dependent state
s->didReachToValue = false;
s->distanceVec = NULL;
if (s->tracing) {
[s->tracer updateToValue:aValue];
}
// automatically unpause active animations
if (s->active && s->paused) {
s->setPaused(false);
}
}
}
- (id)currentValue
{
return POPBox(__state->currentValue(), __state->valueType);
}
#pragma mark - Utility
- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug
{
[s appendFormat:@"; from = %@; to = %@", describe(__state->fromVec), describe(__state->toVec)];
if (_state->active)
[s appendFormat:@"; currentValue = %@", describe(__state->currentValue())];
if (__state->velocityVec && 0 != __state->velocityVec->norm())
[s appendFormat:@"; velocity = %@", describe(__state->velocityVec)];
if (!self.removedOnCompletion)
[s appendFormat:@"; removedOnCompletion = %@", POPStringFromBOOL(self.removedOnCompletion)];
if (__state->progressMarkers)
[s appendFormat:@"; progressMarkers = [%@]", [__state->progressMarkers componentsJoinedByString:@", "]];
if (_state->active)
[s appendFormat:@"; progress = %f", __state->progress];
}
@end
@implementation POPPropertyAnimation (NSCopying)
- (instancetype)copyWithZone:(NSZone *)zone {
POPPropertyAnimation *copy = [super copyWithZone:zone];
if (copy) {
copy.property = [self.property copyWithZone:zone];
copy.fromValue = self.fromValue;
copy.toValue = self.toValue;
copy.roundingFactor = self.roundingFactor;
copy.clampMode = self.clampMode;
copy.additive = self.additive;
}
return copy;
}
@end

359
Clocker/pop/pop/POPPropertyAnimationInternal.h

@ -0,0 +1,359 @@
/**
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 "POPPropertyAnimation.h"
static void clampValue(CGFloat &value, CGFloat fromValue, CGFloat toValue, NSUInteger clamp)
{
BOOL increasing = (toValue > fromValue);
// Clamp start of animation.
if ((kPOPAnimationClampStart & clamp) &&
((increasing && (value < fromValue)) || (!increasing && (value > fromValue)))) {
value = fromValue;
}
// Clamp end of animation.
if ((kPOPAnimationClampEnd & clamp) &&
((increasing && (value > toValue)) || (!increasing && (value < toValue)))) {
value = toValue;
}
}
struct _POPPropertyAnimationState : _POPAnimationState
{
POPAnimatableProperty *property;
POPValueType valueType;
NSUInteger valueCount;
VectorRef fromVec;
VectorRef toVec;
VectorRef currentVec;
VectorRef previousVec;
VectorRef previous2Vec;
VectorRef velocityVec;
VectorRef originalVelocityVec;
VectorRef distanceVec;
CGFloat roundingFactor;
NSUInteger clampMode;
NSArray *progressMarkers;
POPProgressMarker *progressMarkerState;
NSUInteger progressMarkerCount;
NSUInteger nextProgressMarkerIdx;
CGFloat dynamicsThreshold;
_POPPropertyAnimationState(id __unsafe_unretained anim) : _POPAnimationState(anim),
property(nil),
valueType((POPValueType)0),
valueCount(0),
fromVec(nullptr),
toVec(nullptr),
currentVec(nullptr),
previousVec(nullptr),
previous2Vec(nullptr),
velocityVec(nullptr),
originalVelocityVec(nullptr),
distanceVec(nullptr),
roundingFactor(0),
clampMode(0),
progressMarkers(nil),
progressMarkerState(nil),
progressMarkerCount(0),
nextProgressMarkerIdx(0),
dynamicsThreshold(0)
{
type = kPOPAnimationBasic;
}
~_POPPropertyAnimationState()
{
if (progressMarkerState) {
free(progressMarkerState);
progressMarkerState = NULL;
}
}
bool canProgress() {
return hasValue();
}
bool shouldRound() {
return 0 != roundingFactor;
}
bool hasValue() {
return 0 != valueCount;
}
bool isDone() {
// inherit done
if (_POPAnimationState::isDone()) {
return true;
}
// consider an animation with no values done
if (!hasValue() && !isCustom()) {
return true;
}
return false;
}
// returns a copy of the currentVec, rounding if needed
VectorRef currentValue() {
VectorRef vec = VectorRef(Vector::new_vector(currentVec.get()));
if (shouldRound()) {
vec->subRound(1 / roundingFactor);
}
return vec;
}
void resetProgressMarkerState()
{
for (NSUInteger idx = 0; idx < progressMarkerCount; idx++)
progressMarkerState[idx].reached = false;
nextProgressMarkerIdx = 0;
}
void updatedProgressMarkers()
{
if (progressMarkerState) {
free(progressMarkerState);
progressMarkerState = NULL;
}
progressMarkerCount = progressMarkers.count;
if (0 != progressMarkerCount) {
progressMarkerState = (POPProgressMarker *)malloc(progressMarkerCount * sizeof(POPProgressMarker));
[progressMarkers enumerateObjectsUsingBlock:^(NSNumber *progressMarker, NSUInteger idx, BOOL *stop) {
progressMarkerState[idx].reached = false;
progressMarkerState[idx].progress = [progressMarker floatValue];
}];
}
nextProgressMarkerIdx = 0;
}
virtual void updatedDynamicsThreshold()
{
dynamicsThreshold = property.threshold;
}
void finalizeProgress()
{
progress = 1.0;
NSUInteger count = valueCount;
VectorRef outVec(Vector::new_vector(count, NULL));
if (outVec && toVec) {
*outVec = *toVec;
}
currentVec = outVec;
clampCurrentValue();
delegateProgress();
}
void computeProgress() {
if (!canProgress()) {
return;
}
static ComputeProgressFunctor<Vector4r> func;
Vector4r v = vector4(currentVec);
Vector4r f = vector4(fromVec);
Vector4r t = vector4(toVec);
progress = func(v, f, t);
}
void delegateProgress() {
if (!canProgress()) {
return;
}
if (delegateDidProgress && progressMarkerState) {
while (nextProgressMarkerIdx < progressMarkerCount) {
if (progress < progressMarkerState[nextProgressMarkerIdx].progress)
break;
if (!progressMarkerState[nextProgressMarkerIdx].reached) {
ActionEnabler enabler;
[delegate pop_animation:self didReachProgress:progressMarkerState[nextProgressMarkerIdx].progress];
progressMarkerState[nextProgressMarkerIdx].reached = true;
}
nextProgressMarkerIdx++;
}
}
if (!didReachToValue) {
bool didReachToValue = false;
if (0 == valueCount) {
didReachToValue = true;
} else {
Vector4r distance = toVec->vector4r();
distance -= currentVec->vector4r();
if (0 == distance.squaredNorm()) {
didReachToValue = true;
} else {
// components
if (distanceVec) {
didReachToValue = true;
const CGFloat *distanceValues = distanceVec->data();
for (NSUInteger idx = 0; idx < valueCount; idx++) {
didReachToValue &= (signbit(distance[idx]) != signbit(distanceValues[idx]));
}
}
}
}
if (didReachToValue) {
handleDidReachToValue();
}
}
}
void handleDidReachToValue() {
didReachToValue = true;
if (delegateDidReachToValue) {
ActionEnabler enabler;
[delegate pop_animationDidReachToValue:self];
}
POPAnimationDidReachToValueBlock block = animationDidReachToValueBlock;
if (block != NULL) {
ActionEnabler enabler;
block(self);
}
if (tracing) {
[tracer didReachToValue:POPBox(currentValue(), valueType, true)];
}
}
void readObjectValue(VectorRef *ptrVec, id obj)
{
// use current object value as from value
pop_animatable_read_block read = property.readBlock;
if (NULL != read) {
Vector4r vec = read_values(read, obj, valueCount);
*ptrVec = VectorRef(Vector::new_vector(valueCount, vec));
if (tracing) {
[tracer readPropertyValue:POPBox(*ptrVec, valueType, true)];
}
}
}
virtual void willRun(bool started, id obj) {
// ensure from value initialized
if (NULL == fromVec) {
readObjectValue(&fromVec, obj);
}
// ensure to value initialized
if (NULL == toVec) {
// compute decay to value
if (kPOPAnimationDecay == type) {
[self toValue];
} else {
// read to value
readObjectValue(&toVec, obj);
}
}
// handle one time value initialization on start
if (started) {
// initialize current vec
if (!currentVec) {
currentVec = VectorRef(Vector::new_vector(valueCount, NULL));
// initialize current value with from value
// only do this on initial creation to avoid overwriting current value
// on paused animation continuation
if (currentVec && fromVec) {
*currentVec = *fromVec;
}
}
// ensure velocity values
if (!velocityVec) {
velocityVec = VectorRef(Vector::new_vector(valueCount, NULL));
}
if (!originalVelocityVec) {
originalVelocityVec = VectorRef(Vector::new_vector(valueCount, NULL));
}
}
// ensure distance value initialized
// depends on current value set on one time start
if (NULL == distanceVec) {
// not yet started animations may not have current value
VectorRef fromVec2 = NULL != currentVec ? currentVec : fromVec;
if (fromVec2 && toVec) {
Vector4r distance = toVec->vector4r();
distance -= fromVec2->vector4r();
if (0 != distance.squaredNorm()) {
distanceVec = VectorRef(Vector::new_vector(valueCount, distance));
}
}
}
}
virtual void reset(bool all) {
_POPAnimationState::reset(all);
if (all) {
currentVec = NULL;
previousVec = NULL;
previous2Vec = NULL;
}
progress = 0;
resetProgressMarkerState();
didReachToValue = false;
distanceVec = NULL;
}
void clampCurrentValue(NSUInteger clamp)
{
if (kPOPAnimationClampNone == clamp)
return;
// Clamp all vector values
CGFloat *currentValues = currentVec->data();
const CGFloat *fromValues = fromVec->data();
const CGFloat *toValues = toVec->data();
for (NSUInteger idx = 0; idx < valueCount; idx++) {
clampValue(currentValues[idx], fromValues[idx], toValues[idx], clamp);
}
}
void clampCurrentValue()
{
clampCurrentValue(clampMode);
}
};
typedef struct _POPPropertyAnimationState POPPropertyAnimationState;
@interface POPPropertyAnimation ()
@end

67
Clocker/pop/pop/POPSpringAnimation.h

@ -0,0 +1,67 @@
/**
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 spring animation class.
@discussion Animation is achieved through modeling spring dynamics.
*/
@interface POPSpringAnimation : POPPropertyAnimation
/**
@abstract The designated initializer.
@returns An instance of a spring 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 spring 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.
*/
@property (copy, nonatomic) id velocity;
/**
@abstract The effective bounciness.
@discussion Use in conjunction with 'springSpeed' to change animation effect. Values are converted into corresponding dynamics constants. Higher values increase spring movement range resulting in more oscillations and springiness. Defined as a value in the range [0, 20]. Defaults to 4.
*/
@property (assign, nonatomic) CGFloat springBounciness;
/**
@abstract The effective speed.
@discussion Use in conjunction with 'springBounciness' to change animation effect. Values are converted into corresponding dynamics constants. Higher values increase the dampening power of the spring resulting in a faster initial velocity and more rapid bounce slowdown. Defined as a value in the range [0, 20]. Defaults to 12.
*/
@property (assign, nonatomic) CGFloat springSpeed;
/**
@abstract The tension used in the dynamics simulation.
@discussion Can be used over bounciness and speed for finer grain tweaking of animation effect.
*/
@property (assign, nonatomic) CGFloat dynamicsTension;
/**
@abstract The friction used in the dynamics simulation.
@discussion Can be used over bounciness and speed for finer grain tweaking of animation effect.
*/
@property (assign, nonatomic) CGFloat dynamicsFriction;
/**
@abstract The mass used in the dynamics simulation.
@discussion Can be used over bounciness and speed for finer grain tweaking of animation effect.
*/
@property (assign, nonatomic) CGFloat dynamicsMass;
@end

192
Clocker/pop/pop/POPSpringAnimation.mm

@ -0,0 +1,192 @@
/**
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 "POPSpringAnimationInternal.h"
@implementation POPSpringAnimation
#pragma mark - Lifecycle
#undef __state
#define __state ((POPSpringAnimationState *)_state)
+ (instancetype)animation
{
return [[self alloc] init];
}
+ (instancetype)animationWithPropertyNamed:(NSString *)aName
{
POPSpringAnimation *anim = [self animation];
anim.property = [POPAnimatableProperty propertyWithName:aName];
return anim;
}
- (void)_initState
{
_state = new POPSpringAnimationState(self);
}
- (id)init
{
self = [super _init];
if (nil != self) {
__state->solver = new SpringSolver4d(1, 1, 1);
__state->updatedDynamicsThreshold();
__state->updatedBouncinessAndSpeed();
}
return self;
}
- (void)dealloc
{
if (__state) {
delete __state->solver;
__state->solver = NULL;
}
}
#pragma mark - Properties
- (id)velocity
{
return POPBox(__state->velocityVec, __state->valueType);
}
- (void)setVelocity:(id)aValue
{
POPPropertyAnimationState *s = __state;
VectorRef vec = POPUnbox(aValue, s->valueType, s->valueCount, YES);
VectorRef origVec = POPUnbox(aValue, s->valueType, s->valueCount, YES);
if (!vec_equal(vec, s->velocityVec)) {
s->velocityVec = vec;
s->originalVelocityVec = origVec;
if (s->tracing) {
[s->tracer updateVelocity:aValue];
}
}
}
DEFINE_RW_PROPERTY(POPSpringAnimationState, dynamicsTension, setDynamicsTension:, CGFloat, [self _updatedDynamicsTension];);
DEFINE_RW_PROPERTY(POPSpringAnimationState, dynamicsFriction, setDynamicsFriction:, CGFloat, [self _updatedDynamicsFriction];);
DEFINE_RW_PROPERTY(POPSpringAnimationState, dynamicsMass, setDynamicsMass:, CGFloat, [self _updatedDynamicsMass];);
FB_PROPERTY_GET(POPSpringAnimationState, springSpeed, CGFloat);
- (void)setSpringSpeed:(CGFloat)aFloat
{
POPSpringAnimationState *s = __state;
if (s->userSpecifiedDynamics || aFloat != s->springSpeed) {
s->springSpeed = aFloat;
s->userSpecifiedDynamics = false;
s->updatedBouncinessAndSpeed();
if (s->tracing) {
[s->tracer updateSpeed:aFloat];
}
}
}
FB_PROPERTY_GET(POPSpringAnimationState, springBounciness, CGFloat);
- (void)setSpringBounciness:(CGFloat)aFloat
{
POPSpringAnimationState *s = __state;
if (s->userSpecifiedDynamics || aFloat != s->springBounciness) {
s->springBounciness = aFloat;
s->userSpecifiedDynamics = false;
s->updatedBouncinessAndSpeed();
if (s->tracing) {
[s->tracer updateBounciness:aFloat];
}
}
}
- (SpringSolver4d *)solver
{
return __state->solver;
}
- (void)setSolver:(SpringSolver4d *)aSolver
{
if (aSolver != __state->solver) {
if (__state->solver) {
delete(__state->solver);
}
__state->solver = aSolver;
}
}
#pragma mark - Utility
- (void)_updatedDynamicsTension
{
__state->userSpecifiedDynamics = true;
if(__state->tracing) {
[__state->tracer updateTension:__state->dynamicsTension];
}
__state->updatedDynamics();
}
- (void)_updatedDynamicsFriction
{
__state->userSpecifiedDynamics = true;
if(__state->tracing) {
[__state->tracer updateFriction:__state->dynamicsFriction];
}
__state->updatedDynamics();
}
- (void)_updatedDynamicsMass
{
__state->userSpecifiedDynamics = true;
if(__state->tracing) {
[__state->tracer updateMass:__state->dynamicsMass];
}
__state->updatedDynamics();
}
- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug
{
[super _appendDescription:s debug:debug];
if (debug) {
if (_state->userSpecifiedDynamics) {
[s appendFormat:@"; dynamics = (tension:%f, friction:%f, mass:%f)", __state->dynamicsTension, __state->dynamicsFriction, __state->dynamicsMass];
} else {
[s appendFormat:@"; bounciness = %f; speed = %f", __state->springBounciness, __state->springSpeed];
}
}
}
@end
@implementation POPSpringAnimation (NSCopying)
- (instancetype)copyWithZone:(NSZone *)zone {
POPSpringAnimation *copy = [super copyWithZone:zone];
if (copy) {
id velocity = POPBox(__state->originalVelocityVec, __state->valueType);
// If velocity never gets set, then POPBox will return nil, messing up __state->valueCount.
if (velocity) {
copy.velocity = velocity;
}
copy.springBounciness = self.springBounciness;
copy.springSpeed = self.springSpeed;
copy.dynamicsTension = self.dynamicsTension;
copy.dynamicsFriction = self.dynamicsFriction;
copy.dynamicsMass = self.dynamicsMass;
}
return copy;
}
@end

132
Clocker/pop/pop/POPSpringAnimationInternal.h

@ -0,0 +1,132 @@
/**
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 <cmath>
#import "POPAnimationExtras.h"
#import "POPPropertyAnimationInternal.h"
struct _POPSpringAnimationState : _POPPropertyAnimationState
{
SpringSolver4d *solver;
CGFloat springSpeed;
CGFloat springBounciness; // normalized springiness
CGFloat dynamicsTension; // tension
CGFloat dynamicsFriction; // friction
CGFloat dynamicsMass; // mass
_POPSpringAnimationState(id __unsafe_unretained anim) : _POPPropertyAnimationState(anim),
solver(nullptr),
springSpeed(12.),
springBounciness(4.),
dynamicsTension(0),
dynamicsFriction(0),
dynamicsMass(0)
{
type = kPOPAnimationSpring;
}
bool hasConverged()
{
NSUInteger count = valueCount;
if (shouldRound()) {
return vec_equal(previous2Vec, previousVec) && vec_equal(previousVec, toVec);
} else {
if (!previousVec || !previous2Vec)
return false;
CGFloat t = dynamicsThreshold / 5;
const CGFloat *toValues = toVec->data();
const CGFloat *previousValues = previousVec->data();
const CGFloat *previous2Values = previous2Vec->data();
for (NSUInteger idx = 0; idx < count; idx++) {
if ((std::abs(toValues[idx] - previousValues[idx]) >= t) || (std::abs(previous2Values[idx] - previousValues[idx]) >= t)) {
return false;
}
}
return true;
}
}
bool isDone() {
if (_POPPropertyAnimationState::isDone()) {
return true;
}
return solver->started() && (hasConverged() || solver->hasConverged());
}
void updatedDynamics()
{
if (NULL != solver) {
solver->setConstants(dynamicsTension, dynamicsFriction, dynamicsMass);
}
}
void updatedDynamicsThreshold()
{
_POPPropertyAnimationState::updatedDynamicsThreshold();
if (NULL != solver) {
solver->setThreshold(dynamicsThreshold);
}
}
void updatedBouncinessAndSpeed() {
[POPSpringAnimation convertBounciness:springBounciness speed:springSpeed toTension:&dynamicsTension friction:&dynamicsFriction mass:&dynamicsMass];
updatedDynamics();
}
bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) {
// advance past not yet initialized animations
if (NULL == currentVec) {
return false;
}
CFTimeInterval localTime = time - startTime;
Vector4d value = vector4d(currentVec);
Vector4d toValue = vector4d(toVec);
Vector4d velocity = vector4d(velocityVec);
SSState4d state;
state.p = toValue - value;
// the solver assumes a spring of size zero
// flip the velocity from user perspective to solver perspective
state.v = velocity * -1;
solver->advance(state, localTime, dt);
value = toValue - state.p;
// flip velocity back to user perspective
velocity = state.v * -1;
*currentVec = value;
if (velocityVec) {
*velocityVec = velocity;
}
clampCurrentValue();
return true;
}
virtual void reset(bool all) {
_POPPropertyAnimationState::reset(all);
if (solver) {
solver->setConstants(dynamicsTension, dynamicsFriction, dynamicsMass);
solver->reset();
}
}
};
typedef struct _POPSpringAnimationState POPSpringAnimationState;

190
Clocker/pop/pop/POPSpringSolver.h

@ -0,0 +1,190 @@
/**
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 "POPVector.h"
namespace POP {
template <typename T>
struct SSState
{
T p;
T v;
};
template <typename T>
struct SSDerivative
{
T dp;
T dv;
};
typedef SSState<Vector4d> SSState4d;
typedef SSDerivative<Vector4d> SSDerivative4d;
const CFTimeInterval solverDt = 0.001f;
const CFTimeInterval maxSolverDt = 30.0f;
/**
Templated spring solver class.
*/
template <typename T>
class SpringSolver
{
double _k; // stiffness
double _b; // dampening
double _m; // mass
double _tp; // threshold
double _tv; // threshold velocity
double _ta; // threshold acceleration
CFTimeInterval _accumulatedTime;
SSState<T> _lastState;
T _lastDv;
bool _started;
public:
SpringSolver(double k, double b, double m = 1) : _k(k), _b(b), _m(m), _started(false)
{
_accumulatedTime = 0;
_lastState.p = T::Zero();
_lastState.v = T::Zero();
_lastDv = T::Zero();
setThreshold(1.);
}
~SpringSolver()
{
}
bool started()
{
return _started;
}
void setConstants(double k, double b, double m)
{
_k = k;
_b = b;
_m = m;
}
void setThreshold(double t)
{
_tp = t / 2; // half a unit
_tv = 25.0 * t; // 5 units per second, squared for comparison
_ta = 625.0 * t * t; // 5 units per second squared, squared for comparison
}
T acceleration(const SSState<T> &state, double t)
{
return state.p*(-_k/_m) - state.v*(_b/_m);
}
SSDerivative<T> evaluate(const SSState<T> &initial, double t)
{
SSDerivative<T> output;
output.dp = initial.v;
output.dv = acceleration(initial, t);
return output;
}
SSDerivative<T> evaluate(const SSState<T> &initial, double t, double dt, const SSDerivative<T> &d)
{
SSState<T> state;
state.p = initial.p + d.dp*dt;
state.v = initial.v + d.dv*dt;
SSDerivative<T> output;
output.dp = state.v;
output.dv = acceleration(state, t+dt);
return output;
}
void integrate(SSState<T> &state, double t, double dt)
{
SSDerivative<T> a = evaluate(state, t);
SSDerivative<T> b = evaluate(state, t, dt*0.5, a);
SSDerivative<T> c = evaluate(state, t, dt*0.5, b);
SSDerivative<T> d = evaluate(state, t, dt, c);
T dpdt = (a.dp + (b.dp + c.dp)*2.0 + d.dp) * (1.0/6.0);
T dvdt = (a.dv + (b.dv + c.dv)*2.0 + d.dv) * (1.0/6.0);
state.p = state.p + dpdt*dt;
state.v = state.v + dvdt*dt;
_lastDv = dvdt;
}
SSState<T> interpolate(const SSState<T> &previous, const SSState<T> &current, double alpha)
{
SSState<T> state;
state.p = current.p*alpha + previous.p*(1-alpha);
state.v = current.v*alpha + previous.v*(1-alpha);
return state;
}
void advance(SSState<T> &state, double t, double dt)
{
_started = true;
if (dt > maxSolverDt) {
// excessive time step, force shut down
_lastDv = _lastState.v = _lastState.p = T::Zero();
} else {
_accumulatedTime += dt;
SSState<T> previousState = state, currentState = state;
while (_accumulatedTime >= solverDt) {
previousState = currentState;
this->integrate(currentState, t, solverDt);
t += solverDt;
_accumulatedTime -= solverDt;
}
CFTimeInterval alpha = _accumulatedTime / solverDt;
_lastState = state = this->interpolate(previousState, currentState, alpha);
}
}
bool hasConverged()
{
if (!_started) {
return false;
}
for (size_t idx = 0; idx < _lastState.p.size(); idx++) {
if (fabs(_lastState.p(idx)) >= _tp) {
return false;
}
}
return (_lastState.v.squaredNorm() < _tv) && (_lastDv.squaredNorm() < _ta);
}
void reset()
{
_accumulatedTime = 0;
_lastState.p = T::Zero();
_lastState.v = T::Zero();
_lastDv = T::Zero();
_started = false;
}
};
/**
Convenience spring solver type definitions.
*/
typedef SpringSolver<Vector2d> SpringSolver2d;
typedef SpringSolver<Vector3d> SpringSolver3d;
typedef SpringSolver<Vector4d> SpringSolver4d;
}

394
Clocker/pop/pop/POPVector.h

@ -0,0 +1,394 @@
/**
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__FBVector__
#define __POP__FBVector__
#include <iostream>
#include <vector>
#import <objc/NSObjCRuntime.h>
#import <CoreGraphics/CoreGraphics.h>
#import "POPDefines.h"
#if SCENEKIT_SDK_AVAILABLE
#import <SceneKit/SceneKit.h>
#endif
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#endif
#import "POPMath.h"
namespace POP {
/** Fixed two-size vector class */
template <typename T>
struct Vector2
{
private:
typedef T Vector2<T>::* const _data[2];
static const _data _v;
public:
T x;
T y;
// Zero vector
static const Vector2 Zero() { return Vector2(0); }
// Constructors
Vector2() {}
explicit Vector2(T v) { x = v; y = v; };
explicit Vector2(T x0, T y0) : x(x0), y(y0) {};
explicit Vector2(const CGPoint &p) : x(p.x), y (p.y) {}
explicit Vector2(const CGSize &s) : x(s.width), y (s.height) {}
// Copy constructor
template<typename U> explicit Vector2(const Vector2<U> &v) : x(v.x), y(v.y) {}
// Index operators
const T& operator[](size_t i) const { return this->*_v[i]; }
T& operator[](size_t i) { return this->*_v[i]; }
const T& operator()(size_t i) const { return this->*_v[i]; }
T& operator()(size_t i) { return this->*_v[i]; }
// Backing data
T * data() { return &(this->*_v[0]); }
const T * data() const { return &(this->*_v[0]); }
// Size
inline size_t size() const { return 2; }
// Assignment
Vector2 &operator= (T v) { x = v; y = v; return *this;}
template<typename U> Vector2 &operator= (const Vector2<U> &v) { x = v.x; y = v.y; return *this;}
// Negation
Vector2 operator- (void) const { return Vector2<T>(-x, -y); }
// Equality
bool operator== (T v) const { return (x == v && y == v); }
bool operator== (const Vector2 &v) const { return (x == v.x && y == v.y); }
// Inequality
bool operator!= (T v) const {return (x != v || y != v); }
bool operator!= (const Vector2 &v) const { return (x != v.x || y != v.y); }
// Scalar Math
Vector2 operator+ (T v) const { return Vector2(x + v, y + v); }
Vector2 operator- (T v) const { return Vector2(x - v, y - v); }
Vector2 operator* (T v) const { return Vector2(x * v, y * v); }
Vector2 operator/ (T v) const { return Vector2(x / v, y / v); }
Vector2 &operator+= (T v) { x += v; y += v; return *this; };
Vector2 &operator-= (T v) { x -= v; y -= v; return *this; };
Vector2 &operator*= (T v) { x *= v; y *= v; return *this; };
Vector2 &operator/= (T v) { x /= v; y /= v; return *this; };
// Vector Math
Vector2 operator+ (const Vector2 &v) const { return Vector2(x + v.x, y + v.y); }
Vector2 operator- (const Vector2 &v) const { return Vector2(x - v.x, y - v.y); }
Vector2 &operator+= (const Vector2 &v) { x += v.x; y += v.y; return *this; };
Vector2 &operator-= (const Vector2 &v) { x -= v.x; y -= v.y; return *this; };
// Norms
CGFloat norm() const { return sqrtr(squaredNorm()); }
CGFloat squaredNorm() const { return x * x + y * y; }
// Cast
template<typename U> Vector2<U> cast() const { return Vector2<U>(x, y); }
CGPoint cg_point() const { return CGPointMake(x, y); };
};
template<typename T>
const typename Vector2<T>::_data Vector2<T>::_v = { &Vector2<T>::x, &Vector2<T>::y };
/** Fixed three-size vector class */
template <typename T>
struct Vector3
{
private:
typedef T Vector3<T>::* const _data[3];
static const _data _v;
public:
T x;
T y;
T z;
// Zero vector
static const Vector3 Zero() { return Vector3(0); };
// Constructors
Vector3() {}
explicit Vector3(T v) : x(v), y(v), z(v) {};
explicit Vector3(T x0, T y0, T z0) : x(x0), y(y0), z(z0) {};
// Copy constructor
template<typename U> explicit Vector3(const Vector3<U> &v) : x(v.x), y(v.y), z(v.z) {}
// Index operators
const T& operator[](size_t i) const { return this->*_v[i]; }
T& operator[](size_t i) { return this->*_v[i]; }
const T& operator()(size_t i) const { return this->*_v[i]; }
T& operator()(size_t i) { return this->*_v[i]; }
// Backing data
T * data() { return &(this->*_v[0]); }
const T * data() const { return &(this->*_v[0]); }
// Size
inline size_t size() const { return 3; }
// Assignment
Vector3 &operator= (T v) { x = v; y = v; z = v; return *this;}
template<typename U> Vector3 &operator= (const Vector3<U> &v) { x = v.x; y = v.y; z = v.z; return *this;}
// Negation
Vector3 operator- (void) const { return Vector3<T>(-x, -y, -z); }
// Equality
bool operator== (T v) const { return (x == v && y == v && z = v); }
bool operator== (const Vector3 &v) const { return (x == v.x && y == v.y && z == v.z); }
// Inequality
bool operator!= (T v) const {return (x != v || y != v || z != v); }
bool operator!= (const Vector3 &v) const { return (x != v.x || y != v.y || z != v.z); }
// Scalar Math
Vector3 operator+ (T v) const { return Vector3(x + v, y + v, z + v); }
Vector3 operator- (T v) const { return Vector3(x - v, y - v, z - v); }
Vector3 operator* (T v) const { return Vector3(x * v, y * v, z * v); }
Vector3 operator/ (T v) const { return Vector3(x / v, y / v, z / v); }
Vector3 &operator+= (T v) { x += v; y += v; z += v; return *this; };
Vector3 &operator-= (T v) { x -= v; y -= v; z -= v; return *this; };
Vector3 &operator*= (T v) { x *= v; y *= v; z *= v; return *this; };
Vector3 &operator/= (T v) { x /= v; y /= v; z /= v; return *this; };
// Vector Math
Vector3 operator+ (const Vector3 &v) const { return Vector3(x + v.x, y + v.y, z + v.z); }
Vector3 operator- (const Vector3 &v) const { return Vector3(x - v.x, y - v.y, z - v.z); }
Vector3 &operator+= (const Vector3 &v) { x += v.x; y += v.y; z += v.z; return *this; };
Vector3 &operator-= (const Vector3 &v) { x -= v.x; y -= v.y; z -= v.z; return *this; };
// Norms
CGFloat norm() const { return sqrtr(squaredNorm()); }
CGFloat squaredNorm() const { return x * x + y * y + z * z; }
// Cast
template<typename U> Vector3<U> cast() const { return Vector3<U>(x, y, z); }
};
template<typename T>
const typename Vector3<T>::_data Vector3<T>::_v = { &Vector3<T>::x, &Vector3<T>::y, &Vector3<T>::z };
/** Fixed four-size vector class */
template <typename T>
struct Vector4
{
private:
typedef T Vector4<T>::* const _data[4];
static const _data _v;
public:
T x;
T y;
T z;
T w;
// Zero vector
static const Vector4 Zero() { return Vector4(0); };
// Constructors
Vector4() {}
explicit Vector4(T v) : x(v), y(v), z(v), w(v) {};
explicit Vector4(T x0, T y0, T z0, T w0) : x(x0), y(y0), z(z0), w(w0) {};
// Copy constructor
template<typename U> explicit Vector4(const Vector4<U> &v) : x(v.x), y(v.y), z(v.z), w(v.w) {}
// Index operators
const T& operator[](size_t i) const { return this->*_v[i]; }
T& operator[](size_t i) { return this->*_v[i]; }
const T& operator()(size_t i) const { return this->*_v[i]; }
T& operator()(size_t i) { return this->*_v[i]; }
// Backing data
T * data() { return &(this->*_v[0]); }
const T * data() const { return &(this->*_v[0]); }
// Size
inline size_t size() const { return 4; }
// Assignment
Vector4 &operator= (T v) { x = v; y = v; z = v; w = v; return *this;}
template<typename U> Vector4 &operator= (const Vector4<U> &v) { x = v.x; y = v.y; z = v.z; w = v.w; return *this;}
// Negation
Vector4 operator- (void) const { return Vector4<T>(-x, -y, -z, -w); }
// Equality
bool operator== (T v) const { return (x == v && y == v && z = v, w = v); }
bool operator== (const Vector4 &v) const { return (x == v.x && y == v.y && z == v.z && w == v.w); }
// Inequality
bool operator!= (T v) const {return (x != v || y != v || z != v || w != v); }
bool operator!= (const Vector4 &v) const { return (x != v.x || y != v.y || z != v.z || w != v.w); }
// Scalar Math
Vector4 operator+ (T v) const { return Vector4(x + v, y + v, z + v, w + v); }
Vector4 operator- (T v) const { return Vector4(x - v, y - v, z - v, w - v); }
Vector4 operator* (T v) const { return Vector4(x * v, y * v, z * v, w * v); }
Vector4 operator/ (T v) const { return Vector4(x / v, y / v, z / v, w / v); }
Vector4 &operator+= (T v) { x += v; y += v; z += v; w += v; return *this; };
Vector4 &operator-= (T v) { x -= v; y -= v; z -= v; w -= v; return *this; };
Vector4 &operator*= (T v) { x *= v; y *= v; z *= v; w *= v; return *this; };
Vector4 &operator/= (T v) { x /= v; y /= v; z /= v; w /= v; return *this; };
// Vector Math
Vector4 operator+ (const Vector4 &v) const { return Vector4(x + v.x, y + v.y, z + v.z, w + v.w); }
Vector4 operator- (const Vector4 &v) const { return Vector4(x - v.x, y - v.y, z - v.z, w - v.w); }
Vector4 &operator+= (const Vector4 &v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; };
Vector4 &operator-= (const Vector4 &v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; };
// Norms
CGFloat norm() const { return sqrtr(squaredNorm()); }
CGFloat squaredNorm() const { return x * x + y * y + z * z + w * w; }
// Cast
template<typename U> Vector4<U> cast() const { return Vector4<U>(x, y, z, w); }
};
template<typename T>
const typename Vector4<T>::_data Vector4<T>::_v = { &Vector4<T>::x, &Vector4<T>::y, &Vector4<T>::z, &Vector4<T>::w };
/** Convenience typedefs */
typedef Vector2<float> Vector2f;
typedef Vector2<double> Vector2d;
typedef Vector2<CGFloat> Vector2r;
typedef Vector3<float> Vector3f;
typedef Vector3<double> Vector3d;
typedef Vector3<CGFloat> Vector3r;
typedef Vector4<float> Vector4f;
typedef Vector4<double> Vector4d;
typedef Vector4<CGFloat> Vector4r;
/** Variable-sized vector class */
class Vector
{
size_t _count;
CGFloat *_values;
private:
Vector(size_t);
Vector(const Vector& other);
public:
~Vector();
// Creates a new vector instance of count with values. Initializing a vector of size 0 returns NULL.
static Vector *new_vector(NSUInteger count, const CGFloat *values);
// Creates a new vector given a pointer to another. Can return NULL.
static Vector *new_vector(const Vector * const other);
// Creates a variable size vector given a static vector and count.
static Vector *new_vector(NSUInteger count, Vector4r vec);
// Size of vector
NSUInteger size() const { return _count; }
// Returns array of values
CGFloat *data () { return _values; }
const CGFloat *data () const { return _values; };
// Vector2r support
Vector2r vector2r() const;
// Vector4r support
Vector4r vector4r() const;
// CGFloat support
static Vector *new_cg_float(CGFloat f);
// CGPoint support
CGPoint cg_point() const;
static Vector *new_cg_point(const CGPoint &p);
// CGSize support
CGSize cg_size() const;
static Vector *new_cg_size(const CGSize &s);
// CGRect support
CGRect cg_rect() const;
static Vector *new_cg_rect(const CGRect &r);
#if TARGET_OS_IPHONE
// UIEdgeInsets support
UIEdgeInsets ui_edge_insets() const;
static Vector *new_ui_edge_insets(const UIEdgeInsets &i);
#endif
// CGAffineTransform support
CGAffineTransform cg_affine_transform() const;
static Vector *new_cg_affine_transform(const CGAffineTransform &t);
// CGColorRef support
CGColorRef cg_color() const CF_RETURNS_RETAINED;
static Vector *new_cg_color(CGColorRef color);
#if SCENEKIT_SDK_AVAILABLE
// SCNVector3 support
SCNVector3 scn_vector3() const;
static Vector *new_scn_vector3(const SCNVector3 &vec3);
// SCNVector4 support
SCNVector4 scn_vector4() const;
static Vector *new_scn_vector4(const SCNVector4 &vec4);
#endif
// operator overloads
CGFloat &operator[](size_t i) const {
NSCAssert(size() > i, @"unexpected vector size:%lu", (unsigned long)size());
return _values[i];
}
// Returns the mathematical length
CGFloat norm() const;
CGFloat squaredNorm() const;
// Round to nearest sub
void subRound(CGFloat sub);
// Returns string description
NSString * toString() const;
// Operator overloads
template<typename U> Vector& operator= (const Vector4<U>& other) {
size_t count = MIN(_count, other.size());
for (size_t i = 0; i < count; i++) {
_values[i] = other[i];
}
return *this;
}
Vector& operator= (const Vector& other);
void swap(Vector &first, Vector &second);
bool operator==(const Vector &other) const;
bool operator!=(const Vector &other) const;
};
/** Convenience typedefs */
typedef std::shared_ptr<Vector> VectorRef;
typedef std::shared_ptr<const Vector> VectorConstRef;
}
#endif /* defined(__POP__FBVector__) */

334
Clocker/pop/pop/POPVector.mm

@ -0,0 +1,334 @@
/**
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 "POPVector.h"
#import "POPDefines.h"
#import "POPCGUtils.h"
namespace POP
{
Vector::Vector(const size_t count)
{
_count = count;
_values = 0 != count ? (CGFloat *)calloc(count, sizeof(CGFloat)) : NULL;
}
Vector::Vector(const Vector& other)
{
_count = other.size();
_values = 0 != _count ? (CGFloat *)calloc(_count, sizeof(CGFloat)) : NULL;
if (0 != _count) {
memcpy(_values, other.data(), _count * sizeof(CGFloat));
}
}
Vector::~Vector()
{
if (NULL != _values) {
free(_values);
_values = NULL;
}
_count = 0;
}
void Vector::swap(Vector &first, Vector &second)
{
using std::swap;
swap(first._count, second._count);
swap(first._values, second._values);
}
Vector& Vector::operator=(const Vector& other)
{
Vector temp(other);
swap(*this, temp);
return *this;
}
bool Vector::operator==(const Vector &other) const {
if (_count != other.size()) {
return false;
}
const CGFloat * const values = other.data();
for (NSUInteger idx = 0; idx < _count; idx++) {
if (_values[idx] != values[idx]) {
return false;
}
}
return true;
}
bool Vector::operator!=(const Vector &other) const {
if (_count == other.size()) {
return false;
}
const CGFloat * const values = other.data();
for (NSUInteger idx = 0; idx < _count; idx++) {
if (_values[idx] != values[idx]) {
return false;
}
}
return true;
}
Vector *Vector::new_vector(NSUInteger count, const CGFloat *values)
{
if (0 == count) {
return NULL;
}
Vector *v = new Vector(count);
if (NULL != values) {
memcpy(v->_values, values, count * sizeof(CGFloat));
}
return v;
}
Vector *Vector::new_vector(const Vector * const other)
{
if (NULL == other) {
return NULL;
}
return Vector::new_vector(other->size(), other->data());
}
Vector *Vector::new_vector(NSUInteger count, Vector4r vec)
{
if (0 == count) {
return NULL;
}
Vector *v = new Vector(count);
NSCAssert(count <= 4, @"unexpected count %lu", (unsigned long)count);
for (NSUInteger i = 0; i < MIN(count, (NSUInteger)4); i++) {
v->_values[i] = vec[i];
}
return v;
}
Vector4r Vector::vector4r() const
{
Vector4r v = Vector4r::Zero();
for (size_t i = 0; i < _count; i++) {
v(i) = _values[i];
}
return v;
}
Vector2r Vector::vector2r() const
{
Vector2r v = Vector2r::Zero();
if (_count > 0) v(0) = _values[0];
if (_count > 1) v(1) = _values[1];
return v;
}
Vector *Vector::new_cg_float(CGFloat f)
{
Vector *v = new Vector(1);
v->_values[0] = f;
return v;
}
CGPoint Vector::cg_point () const
{
Vector2r v = vector2r();
return CGPointMake(v(0), v(1));
}
Vector *Vector::new_cg_point(const CGPoint &p)
{
Vector *v = new Vector(2);
v->_values[0] = p.x;
v->_values[1] = p.y;
return v;
}
CGSize Vector::cg_size () const
{
Vector2r v = vector2r();
return CGSizeMake(v(0), v(1));
}
Vector *Vector::new_cg_size(const CGSize &s)
{
Vector *v = new Vector(2);
v->_values[0] = s.width;
v->_values[1] = s.height;
return v;
}
CGRect Vector::cg_rect() const
{
return _count < 4 ? CGRectZero : CGRectMake(_values[0], _values[1], _values[2], _values[3]);
}
Vector *Vector::new_cg_rect(const CGRect &r)
{
Vector *v = new Vector(4);
v->_values[0] = r.origin.x;
v->_values[1] = r.origin.y;
v->_values[2] = r.size.width;
v->_values[3] = r.size.height;
return v;
}
#if TARGET_OS_IPHONE
UIEdgeInsets Vector::ui_edge_insets() const
{
return _count < 4 ? UIEdgeInsetsZero : UIEdgeInsetsMake(_values[0], _values[1], _values[2], _values[3]);
}
Vector *Vector::new_ui_edge_insets(const UIEdgeInsets &i)
{
Vector *v = new Vector(4);
v->_values[0] = i.top;
v->_values[1] = i.left;
v->_values[2] = i.bottom;
v->_values[3] = i.right;
return v;
}
#endif
CGAffineTransform Vector::cg_affine_transform() const
{
if (_count < 6) {
return CGAffineTransformIdentity;
}
NSCAssert(size() >= 6, @"unexpected vector size:%lu", (unsigned long)size());
CGAffineTransform t;
t.a = _values[0];
t.b = _values[1];
t.c = _values[2];
t.d = _values[3];
t.tx = _values[4];
t.ty = _values[5];
return t;
}
Vector *Vector::new_cg_affine_transform(const CGAffineTransform &t)
{
Vector *v = new Vector(6);
v->_values[0] = t.a;
v->_values[1] = t.b;
v->_values[2] = t.c;
v->_values[3] = t.d;
v->_values[4] = t.tx;
v->_values[5] = t.ty;
return v;
}
CGColorRef Vector::cg_color() const
{
if (_count < 4) {
return NULL;
}
return POPCGColorRGBACreate(_values);
}
Vector *Vector::new_cg_color(CGColorRef color)
{
CGFloat rgba[4];
POPCGColorGetRGBAComponents(color, rgba);
return new_vector(4, rgba);
}
#if SCENEKIT_SDK_AVAILABLE
SCNVector3 Vector::scn_vector3() const
{
return _count < 3 ? SCNVector3Make(0.0, 0.0, 0.0) : SCNVector3Make(_values[0], _values[1], _values[2]);
}
Vector *Vector::new_scn_vector3(const SCNVector3 &vec3)
{
Vector *v = new Vector(3);
v->_values[0] = vec3.x;
v->_values[1] = vec3.y;
v->_values[2] = vec3.z;
return v;
}
SCNVector4 Vector::scn_vector4() const
{
return _count < 4 ? SCNVector4Make(0.0, 0.0, 0.0, 0.0) : SCNVector4Make(_values[0], _values[1], _values[2], _values[3]);
}
Vector *Vector::new_scn_vector4(const SCNVector4 &vec4)
{
Vector *v = new Vector(4);
v->_values[0] = vec4.x;
v->_values[1] = vec4.y;
v->_values[2] = vec4.z;
v->_values[3] = vec4.w;
return v;
}
#endif
void Vector::subRound(CGFloat sub)
{
for (NSUInteger idx = 0; idx < _count; idx++) {
_values[idx] = POPSubRound(_values[idx], sub);
}
}
CGFloat Vector::norm() const
{
return sqrtr(squaredNorm());
}
CGFloat Vector::squaredNorm() const
{
CGFloat d = 0;
for (NSUInteger idx = 0; idx < _count; idx++) {
d += (_values[idx] * _values[idx]);
}
return d;
}
NSString * Vector::toString() const
{
if (0 == _count)
return @"()";
if (1 == _count)
return [NSString stringWithFormat:@"%f", _values[0]];
if (2 == _count)
return [NSString stringWithFormat:@"(%.3f, %.3f)", _values[0], _values[1]];
NSMutableString *s = [NSMutableString stringWithCapacity:10];
for (NSUInteger idx = 0; idx < _count; idx++) {
if (0 == idx) {
[s appendFormat:@"[%.3f", _values[idx]];
} else if (idx == _count - 1) {
[s appendFormat:@", %.3f]", _values[idx]];
} else {
[s appendFormat:@", %.3f", _values[idx]];
}
}
return s;
}
}

56
Clocker/pop/pop/WebCore/FloatConversion.h

@ -0,0 +1,56 @@
/*
* Copyright (C) 2007 Apple 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:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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.
*/
#ifndef FloatConversion_h
#define FloatConversion_h
#include <CoreGraphics/CGBase.h>
namespace WebCore {
template<typename T>
float narrowPrecisionToFloat(T);
template<>
inline float narrowPrecisionToFloat(double number)
{
return static_cast<float>(number);
}
template<typename T>
CGFloat narrowPrecisionToCGFloat(T);
template<>
inline CGFloat narrowPrecisionToCGFloat(double number)
{
return static_cast<CGFloat>(number);
}
} // namespace WebCore
#endif // FloatConversion_h

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save