Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 27 additions & 4 deletions Sources/OpenRenderBox/Animation/ORBAnimation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,36 @@
#if !canImport(ObjectiveC)
public import Foundation

public class ORBAnimation: NSObject {
public private(set) var activeDuration: Double = 0
public class ORBAnimation: NSObject, NSCopying {
private var springTerms: [UInt32] = []

var isEmpty: Bool { springTerms.isEmpty }

public var activeDuration: Double {
isEmpty ? 0 : Double(Float(bitPattern: springTerms[1]))
}

public override init() {
super.init()
}

public func removeAll() {
_openRenderBoxUnimplementedFailure()
springTerms.removeAll()
}

public func copy(with zone: NSZone? = nil) -> Any {
let copy = ORBAnimation()
copy.springTerms = springTerms
return copy
}

public override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? ORBAnimation else { return false }
return springTerms == other.springTerms
}

public override var hash: Int {
springTerms.hashValue
}

public func evaluate(atTime time: Double) -> Float {
Expand Down Expand Up @@ -46,7 +67,9 @@ public class ORBAnimation: NSObject {
}

public func addSpringDuration(_ duration: Double, mass: Double, stiffness: Double, damping: Double, initialVelocity velocity: Double) {
_openRenderBoxUnimplementedFailure()
springTerms.append(velocity == 0 ? 9 : 10)
springTerms.append(contentsOf: [duration, mass, stiffness, damping].map { Float($0).bitPattern })
if velocity != 0 { springTerms.append(Float(velocity).bitPattern) }
}
}
#endif
186 changes: 186 additions & 0 deletions Sources/OpenRenderBox/Animation/ORBTransition.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
//
// ORBTransition.swift
// OpenRenderBox

#if !canImport(ObjectiveC)
public import Foundation
public import OpenRenderBoxCxx

private func transitionTime(_ value: Float) -> UInt8 {
guard !value.isNaN else { return 0 }
return UInt8(Float(0.5).addingProduct(min(max(value, 0), 1), 255))
}

public class ORBTransitionEffect: NSObject {
private var bits: UInt16 = 0x301
private var begin: UInt8 = 0
private var length: UInt8 = 255
private var arguments: [UInt32] = [0, 0]

public var type: ORBTransitionEffectType {
get { .init(rawValue: UInt32(bits & 127))! }
set { bits = (bits & ~127) | UInt16(newValue.rawValue & 127) }
}
public var flags: ORBTransitionEffectFlags {
get { .init(rawValue: UInt32(bits >> 14)) }
set { bits = (bits & 0x3fff) | UInt16((newValue.rawValue & 3) << 14) }
}
public var events: ORBTransitionEvents {
get { .init(rawValue: UInt32((bits >> 8) & 63)) }
set { bits = (bits & 0xc0ff) | UInt16((newValue.rawValue & 63) << 8) }
}
public var beginTime: Float {
get { bits & 128 != 0 ? 0 : Float(begin) * (1 / 255) }
set {
if bits & 128 != 0 {
bits &= ~128
length = 255
}
begin = transitionTime(newValue)
}
}
public var duration: Float {
get { bits & 128 != 0 ? 1 : Float(length) * (1 / 255) }
set {
if bits & 128 != 0 {
bits &= ~128
begin = 0
}
length = transitionTime(newValue)
}
}
public var animationIndex: UInt {
get { bits & 128 != 0 && begin == length ? UInt(begin) : 0 }
set {
insertAnimationIndex = newValue
removeAnimationIndex = newValue
}
}
public var insertAnimationIndex: UInt {
get { bits & 128 != 0 ? UInt(begin) : 0 }
set {
if bits & 128 == 0 {
bits |= 128
length = 0
}
begin = UInt8(truncatingIfNeeded: newValue)
}
}
public var removeAnimationIndex: UInt {
get { bits & 128 != 0 ? UInt(length) : 0 }
set {
if bits & 128 == 0 {
bits |= 128
begin = 0
}
length = UInt8(truncatingIfNeeded: newValue)
}
}

public func argumentValue(at index: UInt) -> Float {
Float(bitPattern: integerArgumentValue(at: index))
}
public func setArgumentValue(_ value: Float, at index: UInt) {
setIntegerArgumentValue(value.bitPattern, at: index)
}
public func integerArgumentValue(at index: UInt) -> UInt32 {
index < 2 ? arguments[Int(index)] : 0
}
public func setIntegerArgumentValue(_ value: UInt32, at index: UInt) {
if index < 2 { arguments[Int(index)] = value }
}

fileprivate func snapshot() -> ORBTransitionEffect {
let copy = ORBTransitionEffect()
copy.bits = bits
copy.begin = begin
copy.length = length
let counts = [0, 0, 1, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0]
let type = Int(type.rawValue)
let count = type < counts.count ? counts[type] : 2
for index in 0..<count { copy.arguments[index] = arguments[index] }
return copy
}

public override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? ORBTransitionEffect else { return false }
return bits == other.bits && begin == other.begin && length == other.length && arguments == other.arguments
}
public override var hash: Int {
var hash = UInt(bits & 0x3fff) | UInt((bits >> 2) & 0x3000) | (UInt(begin) << 16) | (UInt(length) << 24)
hash = hash &* 16_777_619 ^ UInt(arguments[0])
hash = hash &* 16_777_619 ^ UInt(arguments[1])
return Int(bitPattern: hash)
}
}

public class ORBTransition: NSObject, NSCopying {
public static let `default`: ORBTransition = {
let transition = ORBTransition()
transition.isDefault = true
return transition
}()

private var isDefault = false
private var storedMethod: ORBTransitionMethod = .empty
private var storedAnimation: ORBAnimation?
private var storedEffects: [ORBTransitionEffect] = []
private var storedDuration: UInt8 = 32

public var method: ORBTransitionMethod {
get { storedMethod }
set {
let method: ORBTransitionMethod = newValue.rawValue <= 8 ? newValue : .empty
if method != storedMethod {
precondition(!isDefault, "The default transition cannot be modified.")
storedMethod = method
}
}
}
public var animation: ORBAnimation? {
get { storedAnimation?.isEmpty == false ? storedAnimation?.copy() as? ORBAnimation : nil }
set {
precondition(!isDefault, "The default transition cannot be modified.")
storedAnimation = newValue?.copy() as? ORBAnimation
}
}
public var maxChanges: UInt32 = .max
public var isReplaceable: Bool = false
public var effects: [ORBTransitionEffect] { storedEffects.map { $0.snapshot() } }
public var addRemoveDuration: Float {
get { Float(storedDuration) * (1 / 255) }
set { storedDuration = transitionTime(newValue) }
}

public func addEffect(_ effect: ORBTransitionEffect?) {
precondition(!isDefault, "The default transition cannot be modified.")
if let effect { storedEffects.append(effect.snapshot()) }
}
public func copy(with zone: NSZone? = nil) -> Any {
let copy = ORBTransition()
copy.storedMethod = storedMethod
copy.storedAnimation = storedAnimation?.copy() as? ORBAnimation
copy.storedEffects = storedEffects
copy.maxChanges = maxChanges
copy.isReplaceable = isReplaceable
copy.storedDuration = storedDuration
return copy
}
public override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? ORBTransition else { return false }
return method == other.method && maxChanges == other.maxChanges
&& isReplaceable == other.isReplaceable && storedDuration == other.storedDuration
&& storedEffects == other.storedEffects && animation == other.animation
}
public override var hash: Int {
var hasher = Hasher()
hasher.combine(method.rawValue)
hasher.combine(maxChanges)
hasher.combine(isReplaceable)
hasher.combine(storedDuration)
hasher.combine(storedEffects)
hasher.combine(animation)
return hasher.finalize()
}
}
#endif
55 changes: 55 additions & 0 deletions Sources/OpenRenderBoxCxx/Animation/Animation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// Animation.cpp
// OpenRenderBox

#include <OpenRenderBoxCxx/Animation/Animation.hpp>
#include <cstring>

namespace ORB {

Animation::Animation(const Animation &other) {
*this = other;
}

Animation &Animation::operator=(const Animation &other) {
if (this != &other) {
_terms.reserve(other._terms.size());
_terms.clear();
for (uint32_t i = 0; i < other._terms.size(); ++i) {
_terms.push_back(other._terms[i]);
}
}
return *this;
}

float Animation::active_duration() const {
return _terms.empty() ? 0 : _terms[1].argument;
}

void Animation::add_spring(double duration, double mass, double stiffness, double damping, double velocity) {
bool has_velocity = velocity != 0;
_terms.reserve(_terms.size() + (has_velocity ? 6 : 5));
_terms.push_back(TermOrArg(uint32_t(has_velocity ? 10 : 9)));
_terms.push_back(TermOrArg(float(duration)));
_terms.push_back(TermOrArg(float(mass)));
_terms.push_back(TermOrArg(float(stiffness)));
_terms.push_back(TermOrArg(float(damping)));
if (has_velocity) _terms.push_back(TermOrArg(float(velocity)));
}

bool Animation::operator==(const Animation &other) const {
return _terms.size() == other._terms.size()
&& (_terms.empty() || std::memcmp(_terms.data(), other._terms.data(), _terms.size() * sizeof(TermOrArg)) == 0);
}

size_t Animation::hash() const {
size_t hash = 2166136261;
for (uint32_t i = 0; i < _terms.size(); ++i) {
uint32_t word;
std::memcpy(&word, &_terms[i], sizeof(word));
hash = hash * 16777619 ^ word;
}
return hash;
}

} /* ORB */
15 changes: 0 additions & 15 deletions Sources/OpenRenderBoxCxx/Animation/ORBAnimation.m

This file was deleted.

45 changes: 45 additions & 0 deletions Sources/OpenRenderBoxCxx/Animation/ORBAnimation.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// ORBAnimation.mm
// OpenRenderBox

#include <OpenRenderBoxObjC/Animation/ORBAnimation.h>

#if ORB_OBJC_FOUNDATION

#include "ORBAnimation_Private.h"

@implementation ORBAnimation {
ORB::Animation _animation;
}

- (id)copyWithZone:(NSZone *)zone {
ORBAnimation *copy = [ORBAnimation new];
copy->_animation = _animation;
return copy;
}

- (BOOL)isEqual:(id)object {
return [object isKindOfClass:[ORBAnimation class]]
&& _animation == ((ORBAnimation *)object)->_animation;
}

- (NSUInteger)hash { return _animation.hash(); }
- (BOOL)orb_isEmpty { return _animation.empty(); }
- (void)removeAll { _animation.clear(); }

- (double)activeDuration {
return _animation.active_duration();
}

- (void)addSpringDuration:(double)duration mass:(double)mass stiffness:(double)stiffness damping:(double)damping initialVelocity:(double)velocity {
_animation.add_spring(duration, mass, stiffness, damping, velocity);
}

- (const ORB::Animation &)orb_animation { return _animation; }
- (void)setORBAnimation:(const ORB::Animation &)animation { _animation = animation; }

// Other animation terms and evaluation require the animation engine.

@end

#endif /* ORB_OBJC_FOUNDATION */
16 changes: 16 additions & 0 deletions Sources/OpenRenderBoxCxx/Animation/ORBAnimation_Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// ORBAnimation_Private.h
// OpenRenderBox

#pragma once

#include <OpenRenderBoxObjC/Animation/ORBAnimation.h>

#if ORB_OBJC_FOUNDATION && defined(__cplusplus)
#include <OpenRenderBoxCxx/Animation/Animation.hpp>

@interface ORBAnimation ()
- (const ORB::Animation &)orb_animation __attribute__((objc_direct));
- (void)setORBAnimation:(const ORB::Animation &)animation __attribute__((objc_direct));
@end
#endif
Loading
Loading