1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-11-11 01:42:35 +03:00

Merge pull request #207 from exyte/task/swiftlint

Integrate SwiftLint in project
This commit is contained in:
Victor Sukochev 2017-10-19 15:44:31 +07:00 committed by GitHub
commit ff7c3e2df7
119 changed files with 6218 additions and 6089 deletions

115
.swiftlint.yml Normal file
View File

@ -0,0 +1,115 @@
included:
- Source
- Tests
excluded:
- Dependencies
disabled_rules:
- unused_optional_binding
- file_header
- no_extension_access_modifier
- number_separator
- object_literal
- pattern_matching_keywords
- private_over_fileprivate
- strict_fileprivate
- sorted_imports
- identifier_name # Disabled due to a lot of one symbol identifiers
# Temporary disabled
- explicit_type_interface
- line_length
- file_length
- type_body_length
- function_body_length
- cyclomatic_complexity
- function_parameter_count
- type_name
- force_unwrapping
- explicit_top_level_acl
- legacy_constructor
- todo
- implicitly_unwrapped_optional
- attributes
- force_cast
- extension_access_modifier
- large_tuple
- no_grouping_extension
opt_in_rules:
- block_based_kvo
- class_delegate_protocol
- closing_brace
- closure_end_indentation
- closure_parameter_position
- closure_spacing
- colon
- comma
- compiler_protocol_init
- conditional_returns_on_newline
- control_statement
- discarded_notification_center_observer
- discouraged_direct_init
- dynamic_inline
- empty_count
- empty_enum_arguments
- empty_parameters
- empty_parentheses_with_trailing_closure
- explicit_enum_raw_value
- explicit_init
- fatal_error_message
- first_where
- for_where
- force_try
- generic_type_name
- identifier_name
- implicit_getter
- implicit_return
- is_disjoint
- joined_default_parameter
- leading_whitespace
- legacy_cggeometry_functions
- legacy_constant
- legacy_nsgeometry_functions
- let_var_whitespace
- mark
- multiline_parameters
- multiple_closures_with_trailing_closure
- nesting
- nimble_operator
- notification_center_detachment
- opening_brace
- operator_usage_whitespace
- operator_whitespace
- overridden_super_call
- private_outlet
- private_unit_test
- prohibited_super_call
- protocol_property_accessors_order
- quick_discouraged_call
- redundant_discardable_let
- redundant_nil_coalescing
- redundant_optional_initialization
- redundant_string_enum_value
- redundant_void_return
- return_arrow_whitespace
- shorthand_operator
- single_test_class
- statement_position
- superfluous_disable_command
- switch_case_on_newline
- syntactic_sugar
- trailing_closure
- trailing_comma
- trailing_newline
- trailing_semicolon
- trailing_whitespace
- unneeded_parentheses_in_closure_argument
- unused_closure_parameter
- unused_enumerated
- valid_ibinspectable
- vertical_parameter_alignment
- vertical_whitespace
- void_return
- weak_delegate
- xctfail_message
force_cast: warning
shorthand_operator: warning
empty_count: warning

View File

@ -911,6 +911,7 @@
57614B731F83D15600875933 /* Headers */,
57614B741F83D15600875933 /* Resources */,
57614B751F83D15600875933 /* CopyFiles */,
665742FC1F988D93006612C6 /* ShellScript */,
);
buildRules = (
);
@ -930,6 +931,7 @@
57FCD2691D76EA4600CC0FB6 /* Headers */,
57FCD26A1D76EA4600CC0FB6 /* Resources */,
57D202181D78047000A90D4F /* CopyFiles */,
669D360B1F8F9B4C0048EF8B /* ShellScript */,
);
buildRules = (
);
@ -1040,6 +1042,35 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
665742FC1F988D93006612C6 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint autocorrect --format\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
};
669D360B1F8F9B4C0048EF8B /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint autocorrect --format\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
57614AFC1F83D15600875933 /* Sources */ = {
isa = PBXSourcesBuildPhase;

View File

@ -9,5 +9,5 @@
import Foundation
open class AnimatableVariable<T>: Variable<T> {
weak internal var node: Node?
weak internal var node: Node?
}

View File

@ -1,53 +1,53 @@
public enum AnimationState {
case initial
case running
case paused
case initial
case running
case paused
}
public class Animation {
internal init() {
}
public func play() {
}
public func stop() {
}
public func pause() {
}
public func state() -> AnimationState {
return .initial
}
public func easing(_ easing: Easing) -> Animation {
return self
}
public func delay(_ delay: Double) -> Animation {
return self
}
public func cycle(_ count: Double) -> Animation {
return self
}
public func cycle() -> Animation{
return self
}
public func reverse() -> Animation {
return self
}
public func autoreversed() -> Animation {
return self
}
@discardableResult public func onComplete(_: @escaping (() -> ())) -> Animation {
return self
}
internal init() {
}
public func play() {
}
public func stop() {
}
public func pause() {
}
public func state() -> AnimationState {
return .initial
}
public func easing(_ easing: Easing) -> Animation {
return self
}
public func delay(_ delay: Double) -> Animation {
return self
}
public func cycle(_ count: Double) -> Animation {
return self
}
public func cycle() -> Animation {
return self
}
public func reverse() -> Animation {
return self
}
public func autoreversed() -> Animation {
return self
}
@discardableResult public func onComplete(_: @escaping (() -> Void)) -> Animation {
return self
}
}

View File

@ -9,125 +9,125 @@
import Foundation
enum AnimationType {
case unknown
case unknown
case contents
case affineTransformation
case opacity
case sequence
case combine
case affineTransformation
case opacity
case sequence
case combine
case morphing
case shape
case empty
case empty
}
class BasicAnimation: Animation {
var nodeId: String?
var type = AnimationType.unknown
let ID: String
var next: BasicAnimation?
var removeFunc: (() -> ())?
var nodeId: String?
var type = AnimationType.unknown
let ID: String
var next: BasicAnimation?
var removeFunc: (() -> Void)?
var delayed = false
var manualStop = false
var paused = false
var pausedProgress = 0.0
var progress = 0.0
var repeatCount = 0.0
var progress = 0.0
var repeatCount = 0.0
var cycled = false
var delay = 0.0
var autoreverses = false
var onProgressUpdate: ((Double) -> ())?
var easing = Easing.ease
var completion: (() -> ())?
var delay = 0.0
var autoreverses = false
var onProgressUpdate: ((Double) -> Void)?
var easing = Easing.ease
var completion: (() -> Void)?
override init() {
ID = UUID().uuidString
super.init()
}
override init() {
ID = UUID().uuidString
super.init()
}
override open func delay(_ delay: Double) -> Animation {
self.delay += delay
return self
}
override open func cycle(_ count: Double) -> Animation {
self.repeatCount = count
return self
}
override open func easing(_ easing: Easing) -> Animation {
self.easing = easing
return self
}
override open func autoreversed() -> Animation {
self.autoreverses = true
return self
}
override open func cycle() -> Animation {
self.cycled = true
override open func cycle(_ count: Double) -> Animation {
self.repeatCount = count
return self
}
override open func onComplete(_ f: @escaping (() -> ())) -> Animation {
self.completion = f
return self
}
override open func easing(_ easing: Easing) -> Animation {
self.easing = easing
return self
}
override open func play() {
override open func autoreversed() -> Animation {
self.autoreverses = true
return self
}
override open func cycle() -> Animation {
self.cycled = true
return self
}
override open func onComplete(_ f: @escaping (() -> Void)) -> Animation {
self.completion = f
return self
}
override open func play() {
manualStop = false
paused = false
animationProducer.addAnimation(self)
}
override open func stop() {
animationProducer.addAnimation(self)
}
override open func stop() {
manualStop = true
paused = false
if delay > 0.0 {
animationProducer.removeDelayed(animation: self)
}
removeFunc?()
}
removeFunc?()
}
override open func pause() {
paused = true
manualStop = false
if delay > 0.0 {
animationProducer.removeDelayed(animation: self)
}
removeFunc?()
}
override func state() -> AnimationState {
if delayed {
return .running
}
if self.progress == 0.0 {
return .initial
}
if paused || manualStop || progress == 1.0 {
return .paused
}
return .running
}
override open func reverse() -> Animation {
return self
}
override open func reverse() -> Animation {
return self
}
func getDuration() -> Double { return 0 }
func getDuration() -> Double { return 0 }
}
// MARK: - Hashable
@ -135,8 +135,8 @@ extension BasicAnimation: Hashable {
public var hashValue: Int {
return ID.hashValue
}
public static func ==(lhs: BasicAnimation, rhs: BasicAnimation) -> Bool {
public static func == (lhs: BasicAnimation, rhs: BasicAnimation) -> Bool {
return lhs.ID == rhs.ID
}
}
@ -148,84 +148,83 @@ extension BasicAnimation {
}
}
// Animated property list https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreAnimation_guide/AnimatableProperties/AnimatableProperties.html
internal class AnimationImpl<T: Interpolable>: BasicAnimation {
var variable: AnimatableVariable<T>
var variable: AnimatableVariable<T>
let initialValue: T
let timeFactory: (() -> ((Double) -> T))
let duration: Double
let logicalFps: UInt
let duration: Double
let logicalFps: UInt
private var vFunc: ((Double) -> T)?
init(observableValue: AnimatableVariable<T>, valueFunc: @escaping (Double) -> T, animationDuration: Double, delay: Double = 0.0, fps: UInt = 30) {
self.variable = observableValue
init(observableValue: AnimatableVariable<T>, valueFunc: @escaping (Double) -> T, animationDuration: Double, delay: Double = 0.0, fps: UInt = 30) {
self.variable = observableValue
self.initialValue = observableValue.value
self.duration = animationDuration
self.duration = animationDuration
self.timeFactory = { return valueFunc }
self.vFunc = .none
self.logicalFps = fps
self.vFunc = .none
self.logicalFps = fps
super.init()
super.init()
self.delay = delay
}
self.delay = delay
}
init(observableValue: AnimatableVariable<T>, factory: @escaping (() -> ((Double) -> T)), animationDuration: Double, delay: Double = 0.0, fps: UInt = 30) {
self.variable = observableValue
self.initialValue = observableValue.value
self.duration = animationDuration
self.timeFactory = factory
self.logicalFps = fps
super.init()
self.delay = delay
}
convenience init(observableValue: AnimatableVariable<T>, startValue: T, finalValue: T, animationDuration: Double) {
let interpolationFunc = { (t: Double) -> T in
return startValue.interpolate(finalValue, progress: t)
}
convenience init(observableValue: AnimatableVariable<T>, startValue: T, finalValue: T, animationDuration: Double) {
let interpolationFunc = { (t: Double) -> T in
return startValue.interpolate(finalValue, progress: t)
}
self.init(observableValue: observableValue, valueFunc: interpolationFunc, animationDuration: animationDuration)
}
self.init(observableValue: observableValue, valueFunc: interpolationFunc, animationDuration: animationDuration)
}
convenience init(observableValue: AnimatableVariable<T>, finalValue: T, animationDuration: Double) {
self.init(observableValue: observableValue, startValue: observableValue.value, finalValue: finalValue, animationDuration: animationDuration)
}
convenience init(observableValue: AnimatableVariable<T>, finalValue: T, animationDuration: Double) {
self.init(observableValue: observableValue, startValue: observableValue.value, finalValue: finalValue, animationDuration: animationDuration)
}
override open func play() {
if manualStop {
variable.value = getVFunc()(0.0)
}
if paused {
variable.value = getVFunc()(pausedProgress)
}
super.play()
super.play()
}
open override func getDuration() -> Double {
open override func getDuration() -> Double {
var totalDuration = autoreverses ? duration * 2.0 : duration
totalDuration = totalDuration * (1.0 - pausedProgress)
return totalDuration
}
return totalDuration
}
open func getVFunc() -> ((Double) -> T) {
if let vFunc = vFunc {
return vFunc
}
var timeFunc = { (t: Double) -> Double in
return t
}
if autoreverses {
let original = timeFunc
timeFunc = { (t: Double) -> Double in
@ -236,11 +235,11 @@ internal class AnimationImpl<T: Interpolable>: BasicAnimation {
}
}
}
vFunc = { (t: Double) -> T in
return self.timeFactory()(timeFunc(t))
}
return vFunc!
}
@ -248,27 +247,29 @@ internal class AnimationImpl<T: Interpolable>: BasicAnimation {
// For sequence completion
class EmptyAnimation: BasicAnimation {
required init(completion: @escaping (() -> ())) {
super.init()
required init(completion: @escaping (() -> Void)) {
super.init()
self.completion = completion
self.type = .empty
}
self.completion = completion
self.type = .empty
}
}
// MARK: - Animation Description
open class AnimationDescription <T> {
open let valueFunc: (Double) -> T
open var duration = 0.0
open var delay = 0.0
public init(valueFunc: @escaping (Double) -> T, duration: Double = 1.0, delay: Double = 0.0) {
self.valueFunc = valueFunc
self.duration = duration
self.delay = delay
}
open func t(_ duration: Double, delay: Double = 0.0) -> AnimationDescription<T> {
return AnimationDescription(valueFunc: valueFunc, duration: duration, delay: delay)
}
open let valueFunc: (Double) -> T
open var duration = 0.0
open var delay = 0.0
public init(valueFunc: @escaping (Double) -> T, duration: Double = 1.0, delay: Double = 0.0) {
self.valueFunc = valueFunc
self.duration = duration
self.delay = delay
}
open func t(_ duration: Double, delay: Double = 0.0) -> AnimationDescription<T> {
return AnimationDescription(valueFunc: valueFunc, duration: duration, delay: delay)
}
}

View File

@ -10,313 +10,312 @@ let animationProducer = AnimationProducer()
class AnimationProducer {
var storedAnimations = [Node: BasicAnimation]()
var storedAnimations = [Node: BasicAnimation]()
var delayedAnimations = [BasicAnimation: Timer]()
var displayLink: MDisplayLinkProtocol?
struct ContentAnimationDesc {
let animation: ContentsAnimation
let layer: CALayer
weak var cache: AnimationCache?
let startDate: Date
let finishDate: Date
let completion: (()->())?
let completion: (() -> Void)?
}
var contentsAnimations = [ContentAnimationDesc]()
func addAnimation(_ animation: BasicAnimation, withoutDelay: Bool = false) {
// Delay - launching timer
if animation.delay > 0.0 && !withoutDelay {
func addAnimation(_ animation: BasicAnimation, withoutDelay: Bool = false) {
let timer = Timer.schedule(delay: animation.delay, handler: { [weak self] _ in
self?.addAnimation(animation, withoutDelay: true)
// Delay - launching timer
if animation.delay > 0.0 && !withoutDelay {
let timer = Timer.schedule(delay: animation.delay) { [weak self] _ in
self?.addAnimation(animation, withoutDelay: true)
_ = self?.delayedAnimations.removeValue(forKey: animation)
animation.delayed = false
})
}
animation.delayed = true
delayedAnimations[animation] = timer
return
}
return
}
// Empty - executing completion
if animation.type == .empty {
executeCompletion(animation)
return
}
if animation.type == .empty {
executeCompletion(animation)
return
}
// Cycle - attaching "re-add animation" logic
if animation.cycled {
if animation.manualStop {
return
}
let reAdd = EmptyAnimation {
self.addAnimation(animation)
}
if let nextAnimation = animation.next {
nextAnimation.next = reAdd
} else {
animation.next = reAdd
}
}
// General case
guard let nodeId = animation.nodeId, let node = Node.nodeBy(id: nodeId) else {
return
}
guard let nodeId = animation.nodeId, let node = Node.nodeBy(id: nodeId) else {
return
}
guard let macawView = nodesMap.getView(node) else {
storedAnimations[node] = animation
return
}
guard let macawView = nodesMap.getView(node) else {
storedAnimations[node] = animation
return
}
guard let layer = macawView.mLayer else {
return
}
guard let cache = macawView.animationCache else {
return
}
guard let cache = macawView.animationCache else {
return
}
// swiftlint:disable superfluous_disable_command switch_case_alignment
switch animation.type {
case .unknown:
return
case .affineTransformation:
addTransformAnimation(animation, sceneLayer: layer, animationCache: cache, completion: {
if let next = animation.next {
self.addAnimation(next)
}
})
switch animation.type {
case .unknown:
return
case .affineTransformation:
addTransformAnimation(animation, sceneLayer: layer, animationCache: cache, completion: {
if let next = animation.next {
self.addAnimation(next)
}
})
case .opacity:
addOpacityAnimation(animation, sceneLayer: layer, animationCache: cache, completion: {
if let next = animation.next {
self.addAnimation(next)
}
})
case .sequence:
addAnimationSequence(animation)
case .combine:
addCombineAnimation(animation)
case .opacity:
addOpacityAnimation(animation, sceneLayer: layer, animationCache: cache, completion: {
if let next = animation.next {
self.addAnimation(next)
}
})
case .sequence:
addAnimationSequence(animation)
case .combine:
addCombineAnimation(animation)
case .contents:
addContentsAnimation(animation, cache: cache, completion: {
addContentsAnimation(animation, cache: cache) {
if let next = animation.next {
self.addAnimation(next)
}
})
}
case .morphing:
addMorphingAnimation(animation, sceneLayer: layer, animationCache: cache, completion: {
addMorphingAnimation(animation, sceneLayer: layer, animationCache: cache) {
if let next = animation.next {
self.addAnimation(next)
}
})
}
case .shape:
addShapeAnimation(animation, sceneLayer: layer, animationCache: cache, completion: {
addShapeAnimation(animation, sceneLayer: layer, animationCache: cache) {
if let next = animation.next {
self.addAnimation(next)
}
})
case .empty:
executeCompletion(animation)
}
}
}
case .empty:
executeCompletion(animation)
}
// swiftlint:enable superfluous_disable_command switch_case_alignment
}
func removeDelayed(animation: BasicAnimation) {
guard let timer = delayedAnimations[animation] else {
return
}
timer.invalidate()
animation.delayed = false
delayedAnimations.removeValue(forKey: animation)
}
// MARK: - Sequence animation
fileprivate func addAnimationSequence(_ animationSequnce: Animation) {
guard let sequence = animationSequnce as? AnimationSequence else {
return
}
// Generating sequence
var sequenceAnimations = [BasicAnimation]()
fileprivate func addAnimationSequence(_ animationSequnce: Animation) {
guard let sequence = animationSequnce as? AnimationSequence else {
return
}
// Generating sequence
var sequenceAnimations = [BasicAnimation]()
var cycleAnimations = sequence.animations
if sequence.autoreverses {
cycleAnimations.append(contentsOf: sequence.animations.reversed())
}
if sequence.repeatCount > 0.0001 {
for _ in 0..<Int(sequence.repeatCount) {
sequenceAnimations.append(contentsOf: cycleAnimations)
}
} else {
sequenceAnimations.append(contentsOf: cycleAnimations)
}
// Connecting animations
for i in 0..<(sequenceAnimations.count - 1) {
let animation = sequenceAnimations[i]
animation.next = sequenceAnimations[i + 1]
}
if sequence.repeatCount > 0.0001 {
for _ in 0..<Int(sequence.repeatCount) {
sequenceAnimations.append(contentsOf: cycleAnimations)
}
} else {
sequenceAnimations.append(contentsOf: cycleAnimations)
}
// Completion
if let completion = sequence.completion {
let completionAnimation = EmptyAnimation(completion: completion)
// Connecting animations
for i in 0..<(sequenceAnimations.count - 1) {
let animation = sequenceAnimations[i]
animation.next = sequenceAnimations[i + 1]
}
if let next = sequence.next {
completionAnimation.next = next
}
// Completion
if let completion = sequence.completion {
let completionAnimation = EmptyAnimation(completion: completion)
sequenceAnimations.last?.next = completionAnimation
} else {
if let next = sequence.next {
sequenceAnimations.last?.next = next
}
}
if let next = sequence.next {
completionAnimation.next = next
}
// Launching
if let firstAnimation = sequence.animations.first {
self.addAnimation(firstAnimation)
}
}
sequenceAnimations.last?.next = completionAnimation
} else {
if let next = sequence.next {
sequenceAnimations.last?.next = next
}
}
// Launching
if let firstAnimation = sequence.animations.first {
self.addAnimation(firstAnimation)
}
}
// MARK: - Combine animation
fileprivate func addCombineAnimation(_ combineAnimation: Animation) {
guard let combine = combineAnimation as? CombineAnimation else {
return
}
fileprivate func addCombineAnimation(_ combineAnimation: Animation) {
guard let combine = combineAnimation as? CombineAnimation else {
return
}
// Reversing
if combine.autoreverses {
combine.animations.forEach { animation in
animation.autoreverses = true
}
}
// Reversing
if combine.autoreverses {
combine.animations.forEach { animation in
animation.autoreverses = true
}
}
// repeat count
if combine.repeatCount > 0.00001 {
var sequence = [Animation]()
// repeat count
if combine.repeatCount > 0.00001 {
var sequence = [Animation]()
for _ in 0..<Int(combine.repeatCount) {
sequence.append(combine)
}
for _ in 0..<Int(combine.repeatCount) {
sequence.append(combine)
}
combine.repeatCount = 0.0
addAnimationSequence(sequence.sequence())
return
}
combine.repeatCount = 0.0
addAnimationSequence(sequence.sequence())
return
}
// Looking for longest animation
var longestAnimation: BasicAnimation?
combine.animations.forEach { animation in
guard let longest = longestAnimation else {
longestAnimation = animation
return
}
// Looking for longest animation
var longestAnimation: BasicAnimation?
combine.animations.forEach { animation in
guard let longest = longestAnimation else {
longestAnimation = animation
return
}
if longest.getDuration() < animation.getDuration() {
longestAnimation = animation
}
}
if longest.getDuration() < animation.getDuration() {
longestAnimation = animation
}
}
// Attaching completion empty animation and potential next animation
if let completion = combine.completion {
let completionAnimation = EmptyAnimation(completion: completion)
if let next = combine.next {
completionAnimation.next = next
}
// Attaching completion empty animation and potential next animation
if let completion = combine.completion {
let completionAnimation = EmptyAnimation(completion: completion)
if let next = combine.next {
completionAnimation.next = next
}
longestAnimation?.next = completionAnimation
longestAnimation?.next = completionAnimation
} else {
if let next = combine.next {
longestAnimation?.next = next
}
} else {
if let next = combine.next {
longestAnimation?.next = next
}
}
}
combine.removeFunc = {
combine.animations.forEach { animation in
animation.removeFunc?()
}
}
combine.removeFunc = {
combine.animations.forEach { animation in
animation.removeFunc?()
}
}
// Launching
combine.animations.forEach { animation in
self.addAnimation(animation)
}
}
// Launching
combine.animations.forEach { animation in
self.addAnimation(animation)
}
}
// MARK: - Empty Animation
fileprivate func executeCompletion(_ emptyAnimation: BasicAnimation) {
emptyAnimation.completion?()
}
fileprivate func executeCompletion(_ emptyAnimation: BasicAnimation) {
emptyAnimation.completion?()
}
// MARK: - Stored animation
func addStoredAnimations(_ node: Node) {
if let animation = storedAnimations[node] {
addAnimation(animation)
storedAnimations.removeValue(forKey: node)
}
func addStoredAnimations(_ node: Node) {
if let animation = storedAnimations[node] {
addAnimation(animation)
storedAnimations.removeValue(forKey: node)
}
guard let group = node as? Group else {
return
}
guard let group = node as? Group else {
return
}
group.contents.forEach { child in
addStoredAnimations(child)
}
}
group.contents.forEach { child in
addStoredAnimations(child)
}
}
// MARK: - Contents animation
func addContentsAnimation(_ animation: BasicAnimation, cache: AnimationCache?, completion: @escaping (() -> ())) {
func addContentsAnimation(_ animation: BasicAnimation, cache: AnimationCache?, completion: @escaping (() -> Void)) {
guard let contentsAnimation = animation as? ContentsAnimation else {
return
}
guard let nodeId = animation.nodeId, let node = Node.nodeBy(id: nodeId) else {
return
}
if animation.autoreverses {
animation.autoreverses = false
addAnimation([animation, animation.reverse()].sequence() as! BasicAnimation)
return
}
if animation.repeatCount > 0.0001 {
animation.repeatCount = 0.0
var animSequence = [Animation]()
for _ in 0...Int(animation.repeatCount) {
animSequence.append(animation)
}
addAnimation(animSequence.sequence() as! BasicAnimation)
return
}
let startDate = Date(timeInterval: contentsAnimation.delay, since: Date())
var unionBounds: Rect? = .none
if let startBounds = contentsAnimation.getVFunc()(0.0).group().bounds(),
let endBounds = contentsAnimation.getVFunc()(1.0).group().bounds() {
let endBounds = contentsAnimation.getVFunc()(1.0).group().bounds() {
unionBounds = startBounds.union(rect: endBounds)
}
guard let layer = cache?.layerForNode(node, animation: contentsAnimation, customBounds: unionBounds) else {
return
}
let animationDesc = ContentAnimationDesc(
animation: contentsAnimation,
layer: layer,
@ -325,9 +324,9 @@ class AnimationProducer {
finishDate: Date(timeInterval: contentsAnimation.duration, since: startDate),
completion: completion
)
contentsAnimations.append(animationDesc)
if displayLink == nil {
displayLink = MDisplayLink()
displayLink?.startUpdates { [weak self] in
@ -337,43 +336,43 @@ class AnimationProducer {
}
}
}
@objc func updateContentAnimations() {
if contentsAnimations.count == 0 {
if contentsAnimations.isEmpty {
displayLink?.invalidate()
displayLink = .none
}
let currentDate = Date()
var animationsToRemove = [Animation]()
let count = contentsAnimations.count
for (index, animationDesc) in contentsAnimations.reversed().enumerated() {
let animation = animationDesc.animation
guard let nodeId = animation.nodeId, let group = Node.nodeBy(id: nodeId) as? Group else {
continue
}
defer {
animationDesc.layer.setNeedsDisplay()
animationDesc.layer.displayIfNeeded()
}
let progress = currentDate.timeIntervalSince(animationDesc.startDate) / animation.duration + animation.pausedProgress
// Completion
if progress >= 1.0 {
// Final update
group.contents = animation.getVFunc()(1.0)
animation.onProgressUpdate?(1.0)
animation.pausedProgress = 0.0
// Finishing animation
if !animation.cycled {
animation.completion?()
}
contentsAnimations.remove(at: count - 1 - index)
animationDesc.cache?.freeLayer(group)
animationDesc.completion?()
@ -383,14 +382,14 @@ class AnimationProducer {
let t = progressForTimingFunction(animation.easing, progress: progress)
group.contents = animation.getVFunc()(t)
animation.onProgressUpdate?(progress)
// Manual stop
if animation.manualStop || animation.paused {
defer {
contentsAnimations.remove(at: count - 1 - index)
animationDesc.cache?.freeLayer(group)
}
if animation.manualStop {
animation.pausedProgress = 0.0
group.contents = animation.getVFunc()(0)

View File

@ -1,103 +1,103 @@
import Foundation
class AnimationUtils {
class func absolutePosition(_ node: Node) -> Transform {
return AnimationUtils.absoluteTransform(node, pos: node.place)
}
class func absoluteTransform(_ node: Node, pos: Transform) -> Transform {
var transform = pos
var parent = nodesMap.parents(node).first
while parent != .none {
transform = GeomUtils.concat(t1: parent!.place, t2: transform)
parent = nodesMap.parents(parent!).first
class func absolutePosition(_ node: Node) -> Transform {
return AnimationUtils.absoluteTransform(node, pos: node.place)
}
return transform
}
class func absoluteClip(node: Node) -> Locus? {
if let _ = node.clip {
return node.clip
}
var parent = nodesMap.parents(node).first
while parent != .none {
if let _ = parent?.clip {
return parent?.clip
}
parent = nodesMap.parents(parent!).first
}
return .none
}
private static var indexCache = [Node: Int]()
class func absoluteIndex(_ node: Node, useCache: Bool = false) -> Int {
if useCache {
if let cachedIndex = indexCache[node] {
return cachedIndex
}
} else {
indexCache.removeAll()
}
func childrenTotalCount(_ node: Node) -> Int{
guard let group = node as? Group else {
return 1
}
var count = 1
for child in group.contents {
count += childrenTotalCount(child)
}
return count
}
var zIndex = 0
var parent = nodesMap.parents(node).first
var currentNode = node
while parent != .none {
if let group = parent as? Group {
let localIndex = group.contents.index(of: currentNode) ?? group.contents.count
for i in 0..<localIndex {
zIndex += childrenTotalCount(group.contents[i])
class func absoluteTransform(_ node: Node, pos: Transform) -> Transform {
var transform = pos
var parent = nodesMap.parents(node).first
while parent != .none {
transform = GeomUtils.concat(t1: parent!.place, t2: transform)
parent = nodesMap.parents(parent!).first
}
}
zIndex += 1
currentNode = parent!
parent = nodesMap.parents(parent!).first
return transform
}
if useCache {
indexCache[node] = zIndex
class func absoluteClip(node: Node) -> Locus? {
if let _ = node.clip {
return node.clip
}
var parent = nodesMap.parents(node).first
while parent != .none {
if let _ = parent?.clip {
return parent?.clip
}
parent = nodesMap.parents(parent!).first
}
return .none
}
return zIndex
}
class func animatedNodes(root: Node, animationCache: AnimationCache) -> [Node] {
if animationCache.isAnimating(root) {
return [root]
private static var indexCache = [Node: Int]()
class func absoluteIndex(_ node: Node, useCache: Bool = false) -> Int {
if useCache {
if let cachedIndex = indexCache[node] {
return cachedIndex
}
} else {
indexCache.removeAll()
}
func childrenTotalCount(_ node: Node) -> Int {
guard let group = node as? Group else {
return 1
}
var count = 1
for child in group.contents {
count += childrenTotalCount(child)
}
return count
}
var zIndex = 0
var parent = nodesMap.parents(node).first
var currentNode = node
while parent != .none {
if let group = parent as? Group {
let localIndex = group.contents.index(of: currentNode) ?? group.contents.count
for i in 0..<localIndex {
zIndex += childrenTotalCount(group.contents[i])
}
}
zIndex += 1
currentNode = parent!
parent = nodesMap.parents(parent!).first
}
if useCache {
indexCache[node] = zIndex
}
return zIndex
}
guard let rootGroup = root as? Group else {
return []
class func animatedNodes(root: Node, animationCache: AnimationCache) -> [Node] {
if animationCache.isAnimating(root) {
return [root]
}
guard let rootGroup = root as? Group else {
return []
}
var result = [Node]()
rootGroup.contents.forEach { child in
let childAnimatedNodes = animatedNodes(root: child, animationCache: animationCache)
result.append(contentsOf: childAnimatedNodes)
}
return result
}
var result = [Node]()
rootGroup.contents.forEach { child in
let childAnimatedNodes = animatedNodes(root: child, animationCache: animationCache)
result.append(contentsOf: childAnimatedNodes)
}
return result
}
}

View File

@ -7,9 +7,9 @@
//
public enum Easing {
case ease
case linear
case easeIn
case easeOut
case easeInOut
case ease
case linear
case easeIn
case easeOut
case easeInOut
}

View File

@ -1,42 +1,42 @@
import Foundation
#if os(iOS)
import UIKit
import UIKit
#endif
public extension Rect {
convenience init(cgRect: CGRect) {
self.init(
x: Double(cgRect.origin.x),
y: Double(cgRect.origin.y),
w: Double(cgRect.size.width),
h: Double(cgRect.size.height))
}
func cgRect() -> CGRect {
return CGRect(x: self.x, y: self.y, width: self.w, height: self.h)
}
func applyTransform(_ transform: Transform) -> Rect {
// TODO: Rewrite using math
let cgTransform = RenderUtils.mapTransform(transform)
return Rect(cgRect: self.cgRect().applying(cgTransform))
}
public func description() -> String {
return "x: \(self.x) y:\(self.y) w:\(self.w) h:\(self.h)"
}
convenience init(cgRect: CGRect) {
self.init(
x: Double(cgRect.origin.x),
y: Double(cgRect.origin.y),
w: Double(cgRect.size.width),
h: Double(cgRect.size.height))
}
func cgRect() -> CGRect {
return CGRect(x: self.x, y: self.y, width: self.w, height: self.h)
}
func applyTransform(_ transform: Transform) -> Rect {
// TODO: Rewrite using math
let cgTransform = RenderUtils.mapTransform(transform)
return Rect(cgRect: self.cgRect().applying(cgTransform))
}
public func description() -> String {
return "x: \(self.x) y:\(self.y) w:\(self.w) h:\(self.h)"
}
}
public extension Point {
public func cgPoint() -> CGPoint {
return CGPoint(x: self.x, y: self.y)
}
public func description() -> String {
return "x: \(self.x) y:\(self.y)"
}
public func cgPoint() -> CGPoint {
return CGPoint(x: self.x, y: self.y)
}
public func description() -> String {
return "x: \(self.x) y:\(self.y)"
}
}

View File

@ -1,24 +1,26 @@
import Foundation
// swiftlint:disable trailing_closure
public func >> (a: Double, b: Double) -> OpacityAnimationDescription {
return OpacityAnimationDescription(valueFunc: { t in
return a.interpolate(b, progress: t)
})
return OpacityAnimationDescription(valueFunc: { t in
return a.interpolate(b, progress: t)
})
}
public func >> (a: Transform, b: Transform) -> TransformAnimationDescription {
return TransformAnimationDescription(valueFunc: { t in
return a.interpolate(b, progress: t)
})
return TransformAnimationDescription(valueFunc: { t in
return a.interpolate(b, progress: t)
})
}
public func >> (a: Locus, b: Locus) -> MorphingAnimationDescription {
return MorphingAnimationDescription(valueFunc: { t in
// return a.interpolate(b, progress: t)
if t == 0.0 {
return a
}
return b
})
return MorphingAnimationDescription(valueFunc: { t in
// return a.interpolate(b, progress: t)
if t == 0.0 {
return a
}
return b
})
}
// swiftlint:enable trailing_closure

View File

@ -1,9 +1,9 @@
public protocol ContentsInterpolation: Interpolable {
}
extension Array: ContentsInterpolation {
public func interpolate(_ endValue: Array, progress: Double) -> Array {
return self
}
public func interpolate(_ endValue: Array, progress: Double) -> Array {
return self
}
}

View File

@ -1,9 +1,9 @@
public protocol DoubleInterpolation: Interpolable {
}
extension Double: DoubleInterpolation {
public func interpolate(_ endValue: Double, progress: Double) -> Double {
return self + (endValue - self) * progress
}
public func interpolate(_ endValue: Double, progress: Double) -> Double {
return self + (endValue - self) * progress
}
}

View File

@ -7,7 +7,7 @@
//
public protocol FillInterpolation: Interpolable {
}
extension Fill: FillInterpolation {

View File

@ -1,3 +1,3 @@
public protocol Interpolable {
func interpolate(_ endValue: Self, progress: Double) -> Self
func interpolate(_ endValue: Self, progress: Double) -> Self
}

View File

@ -1,9 +1,9 @@
public protocol LocusInterpolation: Interpolable {
}
extension Locus: LocusInterpolation {
public func interpolate(_ endValue: Locus, progress: Double) -> Self {
return self
}
public func interpolate(_ endValue: Locus, progress: Double) -> Self {
return self
}
}

View File

@ -7,11 +7,11 @@
//
public protocol ShapeInterpolation: Interpolable {
}
extension Shape: ShapeInterpolation {
public func interpolate(_ endValue: Shape, progress: Double) -> Self {
return self
}
public func interpolate(_ endValue: Shape, progress: Double) -> Self {
return self
}
}

View File

@ -7,7 +7,7 @@
//
public protocol StrokeInterpolation: Interpolable {
}
extension Stroke: StrokeInterpolation {

View File

@ -1,15 +1,14 @@
public protocol TransformInterpolation: Interpolable {
}
extension Transform: TransformInterpolation {
public func interpolate(_ endValue: Transform, progress: Double) -> Transform {
return Transform(m11: self.m11.interpolate(endValue.m11, progress: progress),
m12: self.m12.interpolate(endValue.m12, progress: progress),
m21: self.m21.interpolate(endValue.m21, progress: progress),
m22: self.m22.interpolate(endValue.m22, progress: progress),
dx: self.dx.interpolate(endValue.dx, progress: progress),
dy: self.dy.interpolate(endValue.dy, progress: progress))
}
public func interpolate(_ endValue: Transform, progress: Double) -> Transform {
return Transform(m11: self.m11.interpolate(endValue.m11, progress: progress),
m12: self.m12.interpolate(endValue.m12, progress: progress),
m21: self.m21.interpolate(endValue.m21, progress: progress),
m22: self.m22.interpolate(endValue.m22, progress: progress),
dx: self.dx.interpolate(endValue.dx, progress: progress),
dy: self.dy.interpolate(endValue.dy, progress: progress))
}
}

View File

@ -1,19 +1,18 @@
func solveEquation(a: Double, b: Double, c: Double) -> (s1: Double?, s2: Double?) {
let epsilon: Double = 0.000000001
if (abs(a) < epsilon) {
if (abs(b) < epsilon) {
if abs(a) < epsilon {
if abs(b) < epsilon {
return (.none, .none)
}
let s = -c / b
if (0.0 < s && s < 1.0) {
if 0.0 < s && s < 1.0 {
return (s, .none)
}
return (.none, .none)
}
let b2ac = b * b - 4.0 * c * a
if (b2ac < 0.0) {
if b2ac < 0.0 {
return (.none, .none)
}
let sqrtb2ac = b2ac.squareRoot()
@ -24,7 +23,7 @@ func solveEquation(a: Double, b: Double, c: Double) -> (s1: Double?, s2: Double?
}
let s2 = (-b - sqrtb2ac) / (2.0 * a)
var r2: Double? = .none
if ((epsilon < s2) && (1-s2 > epsilon)) {
if (epsilon < s2) && (1-s2 > epsilon) {
r2 = s2
}
return (r1, r2)
@ -35,17 +34,17 @@ func boundsWithDerivative(p0: Point, p1: Point, p2: Point, p3: Point) -> Rect? {
let bx = 6 * p0.x - 12 * p1.x + 6 * p2.x
let cx = 3 * p1.x - 3 * p0.x
let sx = solveEquation(a: ax, b: bx, c: cx)
let ay = -1.0 * p0.y + 3.0 * p1.y - 3.0 * p2.y + 1.0 * p3.y
let by = 3.0 * p0.y - 6.0 * p1.y + 3.0 * p2.y
let cy = 3 * p1.y - 3 * p0.y
let sy = solveEquation(a: ay, b: by, c: cy)
let solutions = [sx.s1, sx.s2, sy.s1, sy.s2].flatMap { $0 }
var minX:Double? = .none
var minY:Double? = .none
var maxX:Double? = .none
var maxY:Double? = .none
var minX: Double? = .none
var minY: Double? = .none
var maxX: Double? = .none
var maxY: Double? = .none
for s in solutions {
let p = BezierFunc2D(s, p0: p0, p1: p1, p2: p2, p3: p3)
if let mx = minX {
@ -55,7 +54,7 @@ func boundsWithDerivative(p0: Point, p1: Point, p2: Point, p3: Point) -> Rect? {
} else {
minX = p.x
}
if let my = minY {
if my > p.y {
minY = p.y
@ -63,7 +62,7 @@ func boundsWithDerivative(p0: Point, p1: Point, p2: Point, p3: Point) -> Rect? {
} else {
minY = p.y
}
if let mx = maxX {
if mx < p.x {
maxX = p.x
@ -71,7 +70,7 @@ func boundsWithDerivative(p0: Point, p1: Point, p2: Point, p3: Point) -> Rect? {
} else {
maxY = p.x
}
if let my = maxY {
if my < p.y {
maxY = p.y
@ -80,41 +79,41 @@ func boundsWithDerivative(p0: Point, p1: Point, p2: Point, p3: Point) -> Rect? {
maxY = p.y
}
}
if let minX = minX, let maxX = maxX, let minY = minY, let maxY = maxY {
return Rect(x: minX, y: minY, w: maxX - minX, h: maxY - minY)
}
return .none
}
func boundsForFunc(_ func2d: func2D) -> Rect {
var p = func2d(0.0)
var minX = p.x
var minY = p.y
var maxX = minX
var maxY = minY
for t in stride(from: 0.0, to: 1.0, by: 0.01) {
p = func2d(t)
if minX > p.x {
minX = p.x
var p = func2d(0.0)
var minX = p.x
var minY = p.y
var maxX = minX
var maxY = minY
for t in stride(from: 0.0, to: 1.0, by: 0.01) {
p = func2d(t)
if minX > p.x {
minX = p.x
}
if minY > p.y {
minY = p.y
}
if maxX < p.x {
maxX = p.x
}
if maxY < p.y {
maxY = p.y
}
}
if minY > p.y {
minY = p.y
}
if maxX < p.x {
maxX = p.x
}
if maxY < p.y {
maxY = p.y
}
}
return Rect(x: minX, y: minY, w: maxX - minX, h: maxY - minY)
return Rect(x: minX, y: minY, w: maxX - minX, h: maxY - minY)
}

View File

@ -1,119 +1,118 @@
func pathBounds(_ path: Path) -> Rect? {
guard let firstSegment = path.segments.first else {
return .none
}
let firstSegmentInfo = pathSegmenInfo(firstSegment, currentPoint: .none, currentBezierPoint: .none)
var bounds = firstSegmentInfo.0
var currentPoint = firstSegmentInfo.1 ?? Point.origin
var cubicBezierPoint: Point?
for segment in path.segments {
let segmentInfo = pathSegmenInfo(segment, currentPoint: currentPoint, currentBezierPoint: cubicBezierPoint)
if let segmentBounds = segmentInfo.0 {
if segment.isAbsolute() {
bounds = bounds?.union(rect: segmentBounds)
} else {
bounds = bounds?.union(rect: segmentBounds.move(offset: currentPoint))
}
guard let firstSegment = path.segments.first else {
return .none
}
if let segmentLastPoint = segmentInfo.1 {
if segment.isAbsolute() {
currentPoint = segmentLastPoint
} else {
currentPoint = segmentLastPoint.add(currentPoint)
}
let firstSegmentInfo = pathSegmenInfo(firstSegment, currentPoint: .none, currentBezierPoint: .none)
var bounds = firstSegmentInfo.0
var currentPoint = firstSegmentInfo.1 ?? Point.origin
var cubicBezierPoint: Point?
for segment in path.segments {
let segmentInfo = pathSegmenInfo(segment, currentPoint: currentPoint, currentBezierPoint: cubicBezierPoint)
if let segmentBounds = segmentInfo.0 {
if segment.isAbsolute() {
bounds = bounds?.union(rect: segmentBounds)
} else {
bounds = bounds?.union(rect: segmentBounds.move(offset: currentPoint))
}
}
if let segmentLastPoint = segmentInfo.1 {
if segment.isAbsolute() {
currentPoint = segmentLastPoint
} else {
currentPoint = segmentLastPoint.add(currentPoint)
}
}
if let segmentBezierPoint = segmentInfo.2 {
if segment.isAbsolute() {
cubicBezierPoint = segmentBezierPoint
} else {
cubicBezierPoint = segmentBezierPoint.add(currentPoint)
}
}
}
if let segmentBezierPoint = segmentInfo.2 {
if segment.isAbsolute() {
cubicBezierPoint = segmentBezierPoint
} else {
cubicBezierPoint = segmentBezierPoint.add(currentPoint)
}
}
}
return bounds
return bounds
}
func pathSegmenInfo(_ segment: PathSegment, currentPoint: Point?, currentBezierPoint: Point?)
-> (Rect?, Point?, Point?) { // Bounds, last point, last bezier point TODO: Replace as struct
let data = segment.data
switch segment.type {
case .m, .M:
let point = Point(x: data[0], y: data[1])
return (Rect(x: point.x, y: point.y, w: 0.0, h: 0.0), point, .none)
case .c, .C:
return (cubicBounds(data, currentPoint: currentPoint), Point(x: data[4], y: data[5]), Point(x: data[2], y: data[3]))
case .s, .S:
guard let currentPoint = currentPoint else {
return (.none, .none, .none)
}
var p2 = currentPoint
if let bezierPoint = currentBezierPoint {
p2 = Point(
x: 2.0 * currentPoint.x - bezierPoint.x,
y: 2.0 * currentPoint.y - bezierPoint.y)
}
return (sCubicBounds(data, currentPoint: currentPoint, currentBezierPoint: currentBezierPoint),
Point(x: data[2], y: data[3]),
Point(x: p2.x, y: p2.y))
case .H:
if let p = currentPoint {
if (data[0] > p.x) {
return (Rect(x: p.x, y: p.y, w: data[0] - p.x, h: 0.0), Point(x: data[0], y: p.y), .none)
} else {
return (Rect(x: data[0], y: p.y, w: p.x - data[0], h: 0.0), Point(x: data[0], y: p.y), .none)
-> (Rect?, Point?, Point?) { // Bounds, last point, last bezier point TODO: Replace as struct
let data = segment.data
switch segment.type {
case .m, .M:
let point = Point(x: data[0], y: data[1])
return (Rect(x: point.x, y: point.y, w: 0.0, h: 0.0), point, .none)
case .c, .C:
return (cubicBounds(data, currentPoint: currentPoint), Point(x: data[4], y: data[5]), Point(x: data[2], y: data[3]))
case .s, .S:
guard let currentPoint = currentPoint else {
return (.none, .none, .none)
}
var p2 = currentPoint
if let bezierPoint = currentBezierPoint {
p2 = Point(
x: 2.0 * currentPoint.x - bezierPoint.x,
y: 2.0 * currentPoint.y - bezierPoint.y)
}
return (sCubicBounds(data, currentPoint: currentPoint, currentBezierPoint: currentBezierPoint),
Point(x: data[2], y: data[3]),
Point(x: p2.x, y: p2.y))
case .H:
if let p = currentPoint {
if data[0] > p.x {
return (Rect(x: p.x, y: p.y, w: data[0] - p.x, h: 0.0), Point(x: data[0], y: p.y), .none)
} else {
return (Rect(x: data[0], y: p.y, w: p.x - data[0], h: 0.0), Point(x: data[0], y: p.y), .none)
}
}
return (Rect(x: 0.0, y: 0.0, w: data[0], h: 0.0), Point(x: data[0], y: 0.0), .none)
case .h:
return (Rect(x: 0.0, y: 0.0, w: data[0], h: 0.0), Point(x: data[0], y: 0.0), .none)
case .V:
if let p = currentPoint {
if data[0] > p.y {
return (Rect(x: p.x, y: p.y, w: 0.0, h: data[0] - p.y), Point(x: p.x, y: data[0]), .none)
} else {
return (Rect(x: p.x, y: data[0], w: 0.0, h: p.y - data[0]), Point(x: p.x, y: data[0]), .none)
}
}
return (Rect(x: 0.0, y: 0.0, w: 0.0, h: data[0]), Point(x: 0.0, y: data[0]), .none)
case .v:
return (Rect(x: 0.0, y: 0.0, w: 0.0, h: data[0]), Point(x: 0.0, y: data[0]), .none)
case .l, .L:
return (Rect(x: data[0], y: data[1], w: 0.0, h: 0.0), Point(x: data[0], y: data[1]), .none)
default:
return (.none, .none, .none)
}
}
return (Rect(x: 0.0, y: 0.0, w: data[0], h: 0.0), Point(x: data[0], y: 0.0), .none)
case .h:
return (Rect(x: 0.0, y: 0.0, w: data[0], h: 0.0), Point(x: data[0], y: 0.0), .none)
case .V:
if let p = currentPoint {
if (data[0] > p.y) {
return (Rect(x: p.x, y: p.y, w: 0.0, h: data[0] - p.y), Point(x: p.x, y: data[0]), .none)
} else {
return (Rect(x: p.x, y: data[0], w: 0.0, h: p.y - data[0]), Point(x: p.x, y: data[0]), .none)
}
}
return (Rect(x: 0.0, y: 0.0, w: 0.0, h: data[0]), Point(x: 0.0, y: data[0]), .none)
case .v:
return (Rect(x: 0.0, y: 0.0, w: 0.0, h: data[0]), Point(x: 0.0, y: data[0]), .none)
case .l, .L:
return (Rect(x: data[0], y: data[1], w: 0.0, h: 0.0), Point(x: data[0], y: data[1]), .none)
default:
return (.none, .none, .none)
}
}
private func cubicBounds(_ data: [Double], currentPoint: Point?) -> Rect {
let p0 = Point(x: 0, y: 0)
let p1 = Point(x: data[0], y: data[1])
let p2 = Point(x: data[2], y: data[3])
let p3 = Point(x: data[4], y: data[5])
return boundsWithDerivative(p0: p0, p1: p1, p2: p2, p3: p3) ?? Rect(x: 0, y: 0, w: 0, h:0)
let p0 = Point(x: 0, y: 0)
let p1 = Point(x: data[0], y: data[1])
let p2 = Point(x: data[2], y: data[3])
let p3 = Point(x: data[4], y: data[5])
return boundsWithDerivative(p0: p0, p1: p1, p2: p2, p3: p3) ?? Rect(x: 0, y: 0, w: 0, h: 0)
}
private func sCubicBounds(_ data: [Double], currentPoint: Point, currentBezierPoint: Point?) -> Rect {
let p0 = Point(x: 0, y: 0)
let p1 = Point(x: data[0], y: data[1])
let p3 = Point(x: data[2], y: data[3])
var p2 = currentPoint
if let bezierPoint = currentBezierPoint {
p2 = Point(
x: 2.0 * currentPoint.x - bezierPoint.x,
y: 2.0 * currentPoint.y - bezierPoint.y)
}
return boundsWithDerivative(p0: p0, p1: p1, p2: p2, p3: p3) ?? Rect(x: 0, y: 0, w: 0, h:0)
let p0 = Point(x: 0, y: 0)
let p1 = Point(x: data[0], y: data[1])
let p3 = Point(x: data[2], y: data[3])
var p2 = currentPoint
if let bezierPoint = currentBezierPoint {
p2 = Point(
x: 2.0 * currentPoint.x - bezierPoint.x,
y: 2.0 * currentPoint.y - bezierPoint.y)
}
return boundsWithDerivative(p0: p0, p1: p1, p2: p2, p3: p3) ?? Rect(x: 0, y: 0, w: 0, h: 0)
}

View File

@ -3,12 +3,12 @@ import Foundation
typealias func2D = ((_ t: Double) -> (Point))
func BezierFunc2D(_ t: Double, p0: Point, p1: Point, p2: Point, p3: Point) -> Point {
return Point(
x: polynom3(t, p0: p0.x, p1: p1.x, p2: p2.x, p3: p3.x),
y: polynom3(t, p0: p0.y, p1: p1.y, p2: p2.y, p3: p3.y))
return Point(
x: polynom3(t, p0: p0.x, p1: p1.x, p2: p2.x, p3: p3.x),
y: polynom3(t, p0: p0.y, p1: p1.y, p2: p2.y, p3: p3.y))
}
func polynom3(_ t: Double, p0: Double, p1: Double, p2: Double, p3: Double) -> Double {
let t1 = 1.0 - t
return pow(t1, 3.0) * p0 + 3.0 * t * pow(t1, 2.0) * p1 + 3.0 * t * t * t1 * p2 + pow(t, 3.0) * p3
let t1 = 1.0 - t
return pow(t1, 3.0) * p0 + 3.0 * t * pow(t1, 2.0) * p1 + 3.0 * t * t * t1 * p2 + pow(t, 3.0) * p3
}

View File

@ -1,62 +1,61 @@
import Foundation
internal class AnimationSequence: BasicAnimation {
let animations: [BasicAnimation]
let animations: [BasicAnimation]
required init(animations: [BasicAnimation], delay: Double = 0.0) {
self.animations = animations
required init(animations: [BasicAnimation], delay: Double = 0.0) {
self.animations = animations
super.init()
super.init()
self.type = .sequence
self.nodeId = animations.first?.nodeId
self.delay = delay
}
self.type = .sequence
self.nodeId = animations.first?.nodeId
self.delay = delay
}
override func getDuration() -> Double {
let originalDuration = animations.map { $0.getDuration() } .reduce(0) { $0 + $1 }
override func getDuration() -> Double {
let originalDuration = animations.map({ $0.getDuration() }).reduce(0, { $0 + $1 })
if autoreverses {
return originalDuration * 2.0
}
return originalDuration
}
open override func stop() {
return originalDuration
}
open override func stop() {
super.stop()
guard let active = (animations.filter { $0.isActive() }).first else {
return
}
active.stop()
}
}
open override func pause() {
super.pause()
guard let active = (animations.filter { $0.isActive() }).first else {
return
}
active.pause()
}
open override func play() {
guard let active = (animations.filter { $0.isActive() }).first else {
super.play()
return
}
manualStop = false
paused = false
active.play()
}
open override func state() -> AnimationState {
for animation in animations {
let state = animation.state()
@ -64,31 +63,31 @@ internal class AnimationSequence: BasicAnimation {
return state
}
}
return .initial
}
open override func reverse() -> Animation {
var reversedAnimations = [BasicAnimation]()
animations.forEach { animation in
reversedAnimations.append(animation.reverse() as! BasicAnimation)
}
open override func reverse() -> Animation {
var reversedAnimations = [BasicAnimation]()
animations.forEach { animation in
reversedAnimations.append(animation.reverse() as! BasicAnimation)
}
let reversedSequence = reversedAnimations.reversed().sequence(delay: self.delay) as! BasicAnimation
reversedSequence.completion = completion
reversedSequence.progress = progress
let reversedSequence = reversedAnimations.reversed().sequence(delay: self.delay) as! BasicAnimation
reversedSequence.completion = completion
reversedSequence.progress = progress
return reversedSequence
}
return reversedSequence
}
}
public extension Sequence where Iterator.Element: Animation {
public func sequence(delay: Double = 0.0) -> Animation {
public func sequence(delay: Double = 0.0) -> Animation {
var sequence = [BasicAnimation]()
self.forEach { animation in
sequence.append(animation as! BasicAnimation)
}
return AnimationSequence(animations: sequence, delay: delay)
}
var sequence = [BasicAnimation]()
self.forEach { animation in
sequence.append(animation as! BasicAnimation)
}
return AnimationSequence(animations: sequence, delay: delay)
}
}

View File

@ -1,66 +1,65 @@
import Foundation
internal class CombineAnimation: BasicAnimation {
let animations: [BasicAnimation]
let animations: [BasicAnimation]
required init(animations: [BasicAnimation], delay: Double = 0.0, node: Node? = .none) {
self.animations = animations
self.animations = animations
super.init()
super.init()
self.type = .combine
self.nodeId = nodeId ?? animations.first?.nodeId
self.delay = delay
}
self.type = .combine
self.nodeId = nodeId ?? animations.first?.nodeId
self.delay = delay
}
override func getDuration() -> Double {
if let maxElement = animations.map({ $0.getDuration() }).max() {
return maxElement
}
override func getDuration() -> Double {
if let maxElement = animations.map({ $0.getDuration() }).max() {
return maxElement
}
return 0.0
}
return 0.0
}
open override func reverse() -> Animation {
var reversedAnimations = [BasicAnimation]()
animations.forEach { animation in
reversedAnimations.append(animation.reverse() as! BasicAnimation)
}
open override func reverse() -> Animation {
var reversedAnimations = [BasicAnimation]()
animations.forEach { animation in
reversedAnimations.append(animation.reverse() as! BasicAnimation)
}
let combineReversed = reversedAnimations.combine(delay: self.delay) as! BasicAnimation
combineReversed.completion = completion
combineReversed.progress = progress
let combineReversed = reversedAnimations.combine(delay: self.delay) as! BasicAnimation
combineReversed.completion = completion
combineReversed.progress = progress
return combineReversed
}
return combineReversed
}
open override func play() {
animations.forEach { animation in
animation.paused = false
animation.manualStop = false
}
super.play()
}
open override func stop() {
open override func stop() {
super.stop()
animations.forEach { animation in
animation.stop()
}
}
animations.forEach { animation in
animation.stop()
}
}
open override func pause() {
super.pause()
animations.forEach { animation in
animation.pause()
}
}
open override func state() -> AnimationState {
var result = AnimationState.initial
for animation in animations {
@ -68,12 +67,12 @@ internal class CombineAnimation: BasicAnimation {
if state == .running {
return .running
}
if state != .initial {
result = state
}
}
return result
}
}
@ -81,10 +80,10 @@ internal class CombineAnimation: BasicAnimation {
public extension Sequence where Iterator.Element: Animation {
public func combine(delay: Double = 0.0, node: Node? = .none) -> Animation {
var toCombine = [BasicAnimation]()
self.forEach { animation in
toCombine.append(animation as! BasicAnimation)
}
var toCombine = [BasicAnimation]()
self.forEach { animation in
toCombine.append(animation as! BasicAnimation)
}
return CombineAnimation(animations: toCombine, delay: delay, node: node)
}
}
}

View File

@ -1,28 +1,26 @@
internal class ContentsAnimation: AnimationImpl<[Node]> {
init(animatedGroup: Group, valueFunc: @escaping (Double) -> [Node], animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedGroup.contentsVar, valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
type = .contents
nodeId = animatedGroup.id
if autostart {
self.play()
}
}
init(animatedGroup: Group, factory: @escaping (() -> ((Double) -> [Node])), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedGroup.contentsVar, factory: factory, animationDuration: animationDuration, delay: delay, fps: fps)
type = .contents
nodeId = animatedGroup.id
if autostart {
self.play()
}
}
open override func reverse() -> Animation {
let factory = { () -> (Double) -> [Node] in
let original = self.timeFactory()
@ -30,36 +28,36 @@ internal class ContentsAnimation: AnimationImpl<[Node]> {
return original(1.0 - t)
}
}
let node = Node.nodeBy(id: nodeId!)
let reversedAnimation = ContentsAnimation(animatedGroup: node as! Group,
factory: factory, animationDuration: duration, fps: logicalFps)
factory: factory, animationDuration: duration, fps: logicalFps)
reversedAnimation.progress = progress
reversedAnimation.completion = completion
return reversedAnimation
}
}
public extension AnimatableVariable where T: ContentsInterpolation {
public func animation(_ f: @escaping (Double) -> [Node]) -> Animation {
let group = node! as! Group
return ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: 1.0, delay: 0.0, autostart: false)
}
public func animation(_ f: @escaping ((Double) -> [Node]), during: Double = 1.0, delay: Double = 0.0) -> Animation {
let group = node! as! Group
return ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: during, delay: delay, autostart: false)
}
public func animate(_ f: @escaping (Double) -> [Node]) {
let group = node! as! Group
let _ = ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: 1.0, delay: 0.0, autostart: true)
_ = ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: 1.0, delay: 0.0, autostart: true)
}
public func animate(_ f: @escaping ((Double) -> [Node]), during: Double = 1.0, delay: Double = 0.0) {
let group = node! as! Group
let _ = ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: during, delay: delay, autostart: true)
_ = ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: during, delay: delay, autostart: true)
}
}

View File

@ -1,35 +1,34 @@
class MorphingAnimation: AnimationImpl<Locus> {
class MorphingAnimation: AnimationImpl<Locus> {
convenience init(animatedNode: Shape, startValue: Locus, finalValue: Locus, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
let interpolationFunc = { (t: Double) -> Locus in
return finalValue
}
self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps)
}
init(animatedNode: Shape, valueFunc: @escaping (Double) -> Locus, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.formVar, valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
type = .morphing
nodeId = animatedNode.id
if autostart {
self.play()
}
}
init(animatedNode: Shape, factory: @escaping (() -> ((Double) -> Locus)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.formVar, factory: factory, animationDuration: animationDuration, delay: delay, fps: fps)
type = .morphing
nodeId = animatedNode.id
if autostart {
self.play()
}
}
// Pause state not available for discreet animation
override public func pause() {
stop()
@ -40,40 +39,39 @@ public typealias MorphingAnimationDescription = AnimationDescription<Locus>
public extension AnimatableVariable where T: LocusInterpolation {
public func animate(_ desc: MorphingAnimationDescription) {
let _ = MorphingAnimation(animatedNode: node as! Shape, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: true)
_ = MorphingAnimation(animatedNode: node as! Shape, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: true)
}
public func animation(_ desc: MorphingAnimationDescription) -> Animation {
return MorphingAnimation(animatedNode: node as! Shape, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: false)
return MorphingAnimation(animatedNode: node as! Shape, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: false)
}
public func animate(from: Locus? =
nil, to: Locus, during: Double = 1.0, delay: Double = 0.0) {
public func animate(from: Locus? = nil, to: Locus, during: Double = 1.0, delay: Double = 0.0) {
self.animate(((from ?? (node as! Shape).form) >> to).t(during, delay: delay))
}
public func animation(from: Locus? = nil, to: Locus, during: Double = 1.0, delay: Double = 0.0) -> Animation {
if let safeFrom = from {
return self.animation((safeFrom >> to).t(during, delay: delay))
}
let origin = (node as! Shape).form
let factory = { () -> (Double) -> Locus in
return { (t: Double) in
if t == 0.0 {
return origin
}
return to }
}
return MorphingAnimation(animatedNode: self.node as! Shape, factory: factory, animationDuration: during, delay: delay)
}
public func animation(_ f: @escaping (Double) -> Locus, during: Double, delay: Double = 0.0) -> Animation {
return MorphingAnimation(animatedNode: node as! Shape, valueFunc: f, animationDuration: during, delay: delay)
}
}
// MARK: - Group
@ -84,43 +82,43 @@ public extension AnimatableVariable where T: ContentsInterpolation {
if let passedFromNode = from {
fromNode = passedFromNode
}
// Shapes on same hierarhy level
let fromShapes = fromNode.contents.flatMap{$0 as? Shape}
let toShapes = to.flatMap{$0 as? Shape}
let fromShapes = fromNode.contents.flatMap { $0 as? Shape }
let toShapes = to.flatMap { $0 as? Shape }
let minPathsNumber = min(fromShapes.count, toShapes.count)
var animations = [Animation]()
for i in 0..<minPathsNumber {
let fromShape = fromShapes[i]
let toShape = toShapes[i]
let animation = ShapeAnimation(animatedNode: fromShape, finalValue: toShape, animationDuration: during, delay: delay)
animations.append(animation)
}
if fromShapes.count > minPathsNumber {
for i in minPathsNumber..<fromShapes.count {
let shapeToHide = fromShapes[i]
let animation = shapeToHide.opacityVar.animation(to: 0.0, during:during, delay: delay)
let animation = shapeToHide.opacityVar.animation(to: 0.0, during: during, delay: delay)
animations.append(animation)
}
}
if toShapes.count > minPathsNumber {
for i in minPathsNumber..<toShapes.count {
let shapeToShow = toShapes[i]
shapeToShow.opacity = 0.0
fromNode.contents.append(shapeToShow)
let animation = shapeToShow.opacityVar.animation(to: 1.0, during:during, delay: delay)
let animation = shapeToShow.opacityVar.animation(to: 1.0, during: during, delay: delay)
animations.append(animation)
}
}
// Groups on same hierahy level
let fromGroups = fromNode.contents.flatMap{$0 as? Group}
let toGroups = to.flatMap{$0 as? Group}
let fromGroups = fromNode.contents.flatMap { $0 as? Group }
let toGroups = to.flatMap { $0 as? Group }
let minGroupsNumber = min(fromGroups.count, toGroups.count)
for i in 0..<minGroupsNumber {
let fromGroup = fromGroups[i]
@ -128,48 +126,47 @@ public extension AnimatableVariable where T: ContentsInterpolation {
let groupAnimation = fromGroup.contentsVar.animation(to: toGroup.contents, during: during, delay: delay)
animations.append(groupAnimation)
}
for i in minGroupsNumber..<fromGroups.count {
let groupToHide = fromGroups[i]
let animation = groupToHide.opacityVar.animation(to: 0.0, during:during, delay: delay)
let animation = groupToHide.opacityVar.animation(to: 0.0, during: during, delay: delay)
animations.append(animation)
}
for i in minGroupsNumber..<toGroups.count {
let groupToShow = toGroups[i]
groupToShow.opacity = 0.0
fromNode.contents.append(groupToShow)
let animation = groupToShow.opacityVar.animation(to: 1.0, during:during, delay: delay)
let animation = groupToShow.opacityVar.animation(to: 1.0, during: during, delay: delay)
animations.append(animation)
}
// Rest nodes
let fromNodes = fromNode.contents.filter {
return !($0 is Group || $0 is Shape)
!($0 is Group || $0 is Shape)
}
let toNodes = to.filter {
return !($0 is Group || $0 is Shape)
!($0 is Group || $0 is Shape)
}
fromNodes.forEach { node in
let animation = node.opacityVar.animation(to: 0.0, during:during, delay: delay)
let animation = node.opacityVar.animation(to: 0.0, during: during, delay: delay)
animations.append(animation)
}
toNodes.forEach { node in
node.opacity = 0.0
fromNode.contents.append(node)
let animation = node.opacityVar.animation(to: 1.0, during:during, delay: delay)
let animation = node.opacityVar.animation(to: 1.0, during: during, delay: delay)
animations.append(animation)
}
return animations.combine(node: fromNode)
}
public func animate(from: Group? = nil, to: [Node], during: Double = 1.0, delay: Double = 0.0) {
animation(from: from, to: to, during: during, delay: delay).play()
}

View File

@ -1,36 +1,35 @@
internal class OpacityAnimation: AnimationImpl<Double> {
convenience init(animatedNode: Node, startValue: Double, finalValue: Double, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
convenience init(animatedNode: Node, startValue: Double, finalValue: Double, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
let interpolationFunc = { (t: Double) -> Double in
return startValue.interpolate(finalValue, progress: t)
}
let interpolationFunc = { (t: Double) -> Double in
return startValue.interpolate(finalValue, progress: t)
}
self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps)
}
self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps)
}
init(animatedNode: Node, valueFunc: @escaping (Double) -> Double, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.opacityVar, valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
type = .opacity
nodeId = animatedNode.id
if autostart {
self.play()
}
}
init(animatedNode: Node, factory: @escaping (() -> ((Double) -> Double)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.opacityVar, factory: factory, animationDuration: animationDuration, delay: delay, fps: fps)
init(animatedNode: Node, valueFunc: @escaping (Double) -> Double, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.opacityVar, valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
type = .opacity
nodeId = animatedNode.id
if autostart {
self.play()
}
}
open override func reverse() -> Animation {
init(animatedNode: Node, factory: @escaping (() -> ((Double) -> Double)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.opacityVar, factory: factory, animationDuration: animationDuration, delay: delay, fps: fps)
type = .opacity
nodeId = animatedNode.id
if autostart {
self.play()
}
}
open override func reverse() -> Animation {
let factory = { () -> (Double) -> Double in
let original = self.timeFactory()
return { (t: Double) -> Double in
@ -39,43 +38,43 @@ internal class OpacityAnimation: AnimationImpl<Double> {
}
let node = Node.nodeBy(id: nodeId!)
let reversedAnimation = OpacityAnimation(animatedNode: node!,
factory: factory, animationDuration: duration, fps: logicalFps)
reversedAnimation.progress = progress
reversedAnimation.completion = completion
let reversedAnimation = OpacityAnimation(animatedNode: node!,
factory: factory, animationDuration: duration, fps: logicalFps)
reversedAnimation.progress = progress
reversedAnimation.completion = completion
return reversedAnimation
}
return reversedAnimation
}
}
public typealias OpacityAnimationDescription = AnimationDescription<Double>
public extension AnimatableVariable where T: DoubleInterpolation {
public func animate(_ desc: OpacityAnimationDescription) {
let _ = OpacityAnimation(animatedNode: node!, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: true)
}
public extension AnimatableVariable where T: DoubleInterpolation {
public func animate(_ desc: OpacityAnimationDescription) {
_ = OpacityAnimation(animatedNode: node!, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: true)
}
public func animation(_ desc: OpacityAnimationDescription) -> Animation {
return OpacityAnimation(animatedNode: node!, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: false)
}
return OpacityAnimation(animatedNode: node!, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: false)
}
public func animate(from: Double? = nil, to: Double, during: Double = 1.0, delay: Double = 0.0) {
self.animate(((from ?? node!.opacity) >> to).t(during, delay: delay))
}
public func animate(from: Double? = nil, to: Double, during: Double = 1.0, delay: Double = 0.0) {
self.animate(((from ?? node!.opacity) >> to).t(during, delay: delay))
}
public func animation(from: Double? = nil, to: Double, during: Double = 1.0, delay: Double = 0.0) -> Animation {
if let safeFrom = from {
return self.animation((safeFrom >> to).t(during, delay: delay))
}
let origin = node!.opacity
let factory = { () -> (Double) -> Double in
let factory = { () -> (Double) -> Double in
return { (t: Double) in return origin.interpolate(to, progress: t) }
}
return OpacityAnimation(animatedNode: self.node!, factory: factory, animationDuration: during, delay: delay)
}
public func animation(_ f: @escaping ((Double) -> Double), during: Double = 1.0, delay: Double = 0.0) -> Animation {
return OpacityAnimation(animatedNode: node!, valueFunc: f, animationDuration: during, delay: delay)
}
public func animation(_ f: @escaping ((Double) -> Double), during: Double = 1.0, delay: Double = 0.0) -> Animation {
return OpacityAnimation(animatedNode: node!, valueFunc: f, animationDuration: during, delay: delay)
}
}

View File

@ -8,12 +8,12 @@
class ShapeAnimation: AnimationImpl<Shape> {
convenience init(animatedNode: Shape, finalValue: Shape, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
let nodeId = animatedNode.id
let interpolationFunc = { (t: Double) -> Shape in
if t == 0 {
let initialNode = Node.nodeBy(id: nodeId) as! Shape
return Shape(form: initialNode.form,
fill: initialNode.fill,
stroke: initialNode.stroke,
@ -25,33 +25,33 @@ class ShapeAnimation: AnimationImpl<Shape> {
visible: initialNode.visible,
tag: initialNode.tag)
}
return finalValue
}
self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps)
}
init(animatedNode: Shape, valueFunc: @escaping (Double) -> Shape, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: AnimatableVariable<Shape>(animatedNode), valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
type = .shape
nodeId = animatedNode.id
if autostart {
self.play()
}
}
init(animatedNode: Shape, factory: @escaping (() -> ((Double) -> Shape)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: AnimatableVariable<Shape>(animatedNode), factory: factory, animationDuration: animationDuration, delay: delay, fps: fps)
type = .shape
nodeId = animatedNode.id
if autostart {
self.play()
}
}
// Pause state not available for discreet animation
override public func pause() {
stop()
@ -59,9 +59,9 @@ class ShapeAnimation: AnimationImpl<Shape> {
}
public extension AnimatableVariable {
public func animate<T:Stroke>(from: T? = nil, to: T, during: Double = 1.0, delay: Double = 0.0) {
public func animate<T: Stroke>(from: T? = nil, to: T, during: Double = 1.0, delay: Double = 0.0) {
let shape = node as! Shape
var safeFrom = from
if safeFrom == nil {
if let shapeStroke = shape.stroke as? T {
@ -70,18 +70,18 @@ public extension AnimatableVariable {
safeFrom = Stroke(width: 1.0) as? T
}
}
shape.stroke = safeFrom
let finalShape = SceneUtils.shapeCopy(from: shape)
finalShape.stroke = to
let _ = ShapeAnimation(animatedNode: shape, finalValue: finalShape, animationDuration: during, delay: delay, autostart: true)
_ = ShapeAnimation(animatedNode: shape, finalValue: finalShape, animationDuration: during, delay: delay, autostart: true)
}
public func animation<T:Stroke>(from: T? = nil, to: T, during: Double = 1.0, delay: Double = 0.0) -> Animation {
public func animation<T: Stroke>(from: T? = nil, to: T, during: Double = 1.0, delay: Double = 0.0) -> Animation {
let shape = node as! Shape
var safeFrom = from
if safeFrom == nil {
if let shapeStroke = shape.stroke as? T {
@ -90,20 +90,20 @@ public extension AnimatableVariable {
safeFrom = Stroke(width: 1.0) as? T
}
}
shape.stroke = safeFrom
let finalShape = SceneUtils.shapeCopy(from: shape)
finalShape.stroke = to
return ShapeAnimation(animatedNode: shape, finalValue: finalShape, animationDuration: during, delay: delay, autostart: false)
}
}
public extension AnimatableVariable {
public func animate<T:Fill>(from: T? = nil, to: T, during: Double = 1.0, delay: Double = 0.0) {
public func animate<T: Fill>(from: T? = nil, to: T, during: Double = 1.0, delay: Double = 0.0) {
let shape = node as! Shape
var safeFrom = from
if safeFrom == nil {
if let shapeFill = shape.fill as? T {
@ -112,18 +112,18 @@ public extension AnimatableVariable {
safeFrom = Color.clear as? T
}
}
shape.fill = safeFrom
let finalShape = SceneUtils.shapeCopy(from: shape)
finalShape.fill = to
let _ = ShapeAnimation(animatedNode: shape, finalValue: finalShape, animationDuration: during, delay: delay, autostart: true)
_ = ShapeAnimation(animatedNode: shape, finalValue: finalShape, animationDuration: during, delay: delay, autostart: true)
}
public func animation<T:Fill>(from: T? = nil, to: T, during: Double = 1.0, delay: Double = 0.0) -> Animation {
public func animation<T: Fill>(from: T? = nil, to: T, during: Double = 1.0, delay: Double = 0.0) -> Animation {
let shape = node as! Shape
var safeFrom = from
if safeFrom == nil {
if let shapeFill = shape.fill as? T {
@ -132,12 +132,12 @@ public extension AnimatableVariable {
safeFrom = Color.clear as? T
}
}
shape.fill = safeFrom
let finalShape = SceneUtils.shapeCopy(from: shape)
finalShape.fill = to
return ShapeAnimation(animatedNode: shape, finalValue: finalShape, animationDuration: during, delay: delay, autostart: false)
}
}

View File

@ -2,36 +2,36 @@ import Foundation
internal class TransformAnimation: AnimationImpl<Transform> {
convenience init(animatedNode: Node, startValue: Transform, finalValue: Transform, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
convenience init(animatedNode: Node, startValue: Transform, finalValue: Transform, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
let interpolationFunc = { (t: Double) -> Transform in
return startValue.interpolate(finalValue, progress: t)
}
let interpolationFunc = { (t: Double) -> Transform in
return startValue.interpolate(finalValue, progress: t)
}
self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps)
}
self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps)
}
init(animatedNode: Node, valueFunc: @escaping (Double) -> Transform, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.placeVar, valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
type = .affineTransformation
nodeId = animatedNode.id
if autostart {
self.play()
}
}
init(animatedNode: Node, factory: @escaping (() -> ((Double) -> Transform)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.placeVar, factory: factory, animationDuration: animationDuration, delay: delay, fps: fps)
init(animatedNode: Node, valueFunc: @escaping (Double) -> Transform, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.placeVar, valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
type = .affineTransformation
nodeId = animatedNode.id
if autostart {
self.play()
}
}
open override func reverse() -> Animation {
init(animatedNode: Node, factory: @escaping (() -> ((Double) -> Transform)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
super.init(observableValue: animatedNode.placeVar, factory: factory, animationDuration: animationDuration, delay: delay, fps: fps)
type = .affineTransformation
nodeId = animatedNode.id
if autostart {
self.play()
}
}
open override func reverse() -> Animation {
let factory = { () -> (Double) -> Transform in
let original = self.timeFactory()
@ -41,61 +41,60 @@ internal class TransformAnimation: AnimationImpl<Transform> {
}
let node = Node.nodeBy(id: nodeId!)
let reversedAnimation = TransformAnimation(animatedNode: node!,
factory: factory, animationDuration: duration, fps: logicalFps)
reversedAnimation.progress = progress
reversedAnimation.completion = completion
let reversedAnimation = TransformAnimation(animatedNode: node!,
factory: factory, animationDuration: duration, fps: logicalFps)
reversedAnimation.progress = progress
reversedAnimation.completion = completion
return reversedAnimation
}
return reversedAnimation
}
}
public typealias TransformAnimationDescription = AnimationDescription<Transform>
public extension AnimatableVariable where T: TransformInterpolation {
public func animate(_ desc: TransformAnimationDescription) {
let _ = TransformAnimation(animatedNode: node!, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: true)
}
public func animate(_ desc: TransformAnimationDescription) {
_ = TransformAnimation(animatedNode: node!, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: true)
}
public func animation(_ desc: TransformAnimationDescription) -> Animation {
return TransformAnimation(animatedNode: node!, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: false)
}
public func animation(_ desc: TransformAnimationDescription) -> Animation {
return TransformAnimation(animatedNode: node!, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: false)
}
public func animate(from: Transform? = nil, to: Transform, during: Double = 1.0, delay: Double = 0.0) {
self.animate(((from ?? node!.place) >> to).t(during, delay: delay))
}
public func animate(from: Transform? = nil, to: Transform, during: Double = 1.0, delay: Double = 0.0) {
self.animate(((from ?? node!.place) >> to).t(during, delay: delay))
}
public func animate(angle: Double, x: Double? = .none, y: Double? = .none, during: Double = 1.0, delay: Double = 0.0) {
let animation = self.animation(angle: angle, x: x, y: y, during: during, delay: delay)
animation.play()
}
public func animation(from: Transform? = nil, to: Transform, during: Double = 1.0, delay: Double = 0.0) -> Animation {
public func animation(from: Transform? = nil, to: Transform, during: Double = 1.0, delay: Double = 0.0) -> Animation {
if let safeFrom = from {
return self.animation((safeFrom >> to).t(during, delay: delay))
}
let origin = node!.place
let factory = { () -> (Double) -> Transform in
return { (t: Double) in return origin.interpolate(to, progress: t) }
}
return TransformAnimation(animatedNode: self.node!, factory: factory, animationDuration: during, delay: delay)
}
}
public func animation(_ f: @escaping ((Double) -> Transform), during: Double = 1.0, delay: Double = 0.0) -> Animation {
return TransformAnimation(animatedNode: node!, valueFunc: f, animationDuration: during, delay: delay)
}
public func animation(_ f: @escaping ((Double) -> Transform), during: Double = 1.0, delay: Double = 0.0) -> Animation {
return TransformAnimation(animatedNode: node!, valueFunc: f, animationDuration: during, delay: delay)
}
public func animation(angle: Double, x: Double? = .none, y: Double? = .none, during: Double = 1.0, delay: Double = 0.0) -> Animation {
let origin = node!.place
let bounds = node!.bounds()!
let factory = { () -> (Double) -> Transform in
return { t in
let asin = sin(angle * t); let acos = cos(angle * t)
let rotation = Transform(
let rotation = Transform(
m11: acos, m12: -asin,
m21: asin, m22: acos,
dx: 0.0, dy: 0.0
@ -105,15 +104,15 @@ public extension AnimatableVariable where T: TransformInterpolation {
dx: x ?? bounds.w / 2.0,
dy: y ?? bounds.h / 2.0
)
let t1 = GeomUtils.concat(t1: move, t2: rotation)
let t2 = GeomUtils.concat(t1: t1, t2: move.invert()!)
let result = GeomUtils.concat(t1: origin, t2: t2)
return result
}
}
return TransformAnimation(animatedNode: self.node!, factory: factory, animationDuration: during, delay: delay)
}

View File

@ -1,196 +1,194 @@
import Foundation
#if os(iOS)
import UIKit
import UIKit
#elseif os(OSX)
import AppKit
import AppKit
#endif
class AnimationCache {
class CachedLayer {
let layer: ShapeLayer
let animation: Animation
var linksCounter = 1
required init(layer: ShapeLayer, animation: Animation) {
self.layer = layer
self.animation = animation
}
}
let sceneLayer: CALayer
var layerCache = [Node: CachedLayer]()
required init(sceneLayer: CALayer) {
self.sceneLayer = sceneLayer
}
func layerForNode(_ node: Node, animation: Animation, customBounds: Rect? = .none, shouldRenderContent: Bool = true) -> ShapeLayer {
guard let cachedLayer = layerCache[node] else {
let layer = ShapeLayer()
layer.shouldRenderContent = shouldRenderContent
layer.animationCache = self
// Use to debug animation layers
// layer.backgroundColor = MColor.green.cgColor
// layer.borderWidth = 1.0
// layer.borderColor = MColor.blue.cgColor
let calculatedBounds = customBounds ?? node.bounds()
if let shapeBounds = calculatedBounds {
let cgRect = shapeBounds.cgRect()
let origFrame = CGRect(x: 0.0, y: 0.0,
width: round(cgRect.width),
height: round(cgRect.height))
layer.bounds = origFrame
layer.anchorPoint = CGPoint(
x: -1.0 * cgRect.origin.x / cgRect.width,
y: -1.0 * cgRect.origin.y / cgRect.height
)
layer.zPosition = CGFloat(AnimationUtils.absoluteIndex(node))
layer.renderTransform = CGAffineTransform(translationX: -1.0 * cgRect.origin.x, y: -1.0 * cgRect.origin.y)
let nodeTransform = RenderUtils.mapTransform(AnimationUtils.absolutePosition(node))
layer.transform = CATransform3DMakeAffineTransform(nodeTransform)
// Clip
if let clip = AnimationUtils.absoluteClip(node: node) {
let maskLayer = CAShapeLayer()
let origPath = RenderUtils.toBezierPath(clip).cgPath
var offsetTransform = CGAffineTransform(translationX: -1.0 * cgRect.origin.x, y: -1.0 * cgRect.origin.y)
let clipPath = origPath.mutableCopy(using: &offsetTransform)
maskLayer.path = clipPath
layer.mask = maskLayer
class CachedLayer {
let layer: ShapeLayer
let animation: Animation
var linksCounter = 1
required init(layer: ShapeLayer, animation: Animation) {
self.layer = layer
self.animation = animation
}
}
layer.opacity = Float(node.opacity)
layer.node = node
layer.contentsScale = calculateAnimationScale(animation: animation)
layer.setNeedsDisplay()
sceneLayer.addSublayer(layer)
layerCache[node] = CachedLayer(layer: layer, animation: animation)
sceneLayer.setNeedsDisplay()
return layer
}
cachedLayer.linksCounter += 1
return cachedLayer.layer
}
private func calculateAnimationScale(animation: Animation) -> CGFloat {
guard let defaultScale = MMainScreen()?.mScale else {
return 1.0
let sceneLayer: CALayer
var layerCache = [Node: CachedLayer]()
required init(sceneLayer: CALayer) {
self.sceneLayer = sceneLayer
}
guard let transformAnimation = animation as? TransformAnimation else {
return defaultScale
func layerForNode(_ node: Node, animation: Animation, customBounds: Rect? = .none, shouldRenderContent: Bool = true) -> ShapeLayer {
guard let cachedLayer = layerCache[node] else {
let layer = ShapeLayer()
layer.shouldRenderContent = shouldRenderContent
layer.animationCache = self
// Use to debug animation layers
// layer.backgroundColor = MColor.green.cgColor
// layer.borderWidth = 1.0
// layer.borderColor = MColor.blue.cgColor
let calculatedBounds = customBounds ?? node.bounds()
if let shapeBounds = calculatedBounds {
let cgRect = shapeBounds.cgRect()
let origFrame = CGRect(x: 0.0, y: 0.0,
width: round(cgRect.width),
height: round(cgRect.height))
layer.bounds = origFrame
layer.anchorPoint = CGPoint(
x: -1.0 * cgRect.origin.x / cgRect.width,
y: -1.0 * cgRect.origin.y / cgRect.height
)
layer.zPosition = CGFloat(AnimationUtils.absoluteIndex(node))
layer.renderTransform = CGAffineTransform(translationX: -1.0 * cgRect.origin.x, y: -1.0 * cgRect.origin.y)
let nodeTransform = RenderUtils.mapTransform(AnimationUtils.absolutePosition(node))
layer.transform = CATransform3DMakeAffineTransform(nodeTransform)
// Clip
if let clip = AnimationUtils.absoluteClip(node: node) {
let maskLayer = CAShapeLayer()
let origPath = RenderUtils.toBezierPath(clip).cgPath
var offsetTransform = CGAffineTransform(translationX: -1.0 * cgRect.origin.x, y: -1.0 * cgRect.origin.y)
let clipPath = origPath.mutableCopy(using: &offsetTransform)
maskLayer.path = clipPath
layer.mask = maskLayer
}
}
layer.opacity = Float(node.opacity)
layer.node = node
layer.contentsScale = calculateAnimationScale(animation: animation)
layer.setNeedsDisplay()
sceneLayer.addSublayer(layer)
layerCache[node] = CachedLayer(layer: layer, animation: animation)
sceneLayer.setNeedsDisplay()
return layer
}
cachedLayer.linksCounter += 1
return cachedLayer.layer
}
let animFunc = transformAnimation.getVFunc()
let origBounds = Rect(x: 0.0, y: 0.0, w: 1.0, h: 1.0)
let startTransform = animFunc(0.0)
let startBounds = origBounds.applyTransform(startTransform)
var startArea = startBounds.w * startBounds.h
// zero scale protection
if startArea == 0.0 {
startArea = 0.1
private func calculateAnimationScale(animation: Animation) -> CGFloat {
guard let defaultScale = MMainScreen()?.mScale else {
return 1.0
}
guard let transformAnimation = animation as? TransformAnimation else {
return defaultScale
}
let animFunc = transformAnimation.getVFunc()
let origBounds = Rect(x: 0.0, y: 0.0, w: 1.0, h: 1.0)
let startTransform = animFunc(0.0)
let startBounds = origBounds.applyTransform(startTransform)
var startArea = startBounds.w * startBounds.h
// zero scale protection
if startArea == 0.0 {
startArea = 0.1
}
var maxArea = startArea
var t = 0.0
let step = 0.1
while t <= 1.0 {
let currentTransform = animFunc(t)
let currentBounds = origBounds.applyTransform(currentTransform)
let currentArea = currentBounds.w * currentBounds.h
if maxArea < currentArea {
maxArea = currentArea
}
t += step
}
return defaultScale * CGFloat(sqrt(maxArea))
}
var maxArea = startArea
var t = 0.0
let step = 0.1
while t <= 1.0 {
let currentTransform = animFunc(t)
let currentBounds = origBounds.applyTransform(currentTransform)
let currentArea = currentBounds.w * currentBounds.h
if maxArea < currentArea {
maxArea = currentArea
}
t = t + step
func freeLayer(_ node: Node) {
guard let cachedLayer = layerCache[node] else {
return
}
cachedLayer.linksCounter -= 1
if cachedLayer.linksCounter != 0 {
return
}
let layer = cachedLayer.layer
layerCache.removeValue(forKey: node)
sceneLayer.setNeedsDisplay()
layer.removeFromSuperlayer()
}
return defaultScale * CGFloat(sqrt(maxArea))
}
func freeLayer(_ node: Node) {
guard let cachedLayer = layerCache[node] else {
return
func isAnimating(_ node: Node) -> Bool {
if let _ = layerCache[node] {
return true
}
return false
}
cachedLayer.linksCounter -= 1
if cachedLayer.linksCounter != 0 {
return
func isChildrenAnimating(_ group: Group) -> Bool {
for child in group.contents {
if isAnimating(child) {
return true
}
if let childGroup = child as? Group {
return isChildrenAnimating(childGroup)
}
}
return false
}
let layer = cachedLayer.layer
layerCache.removeValue(forKey: node)
sceneLayer.setNeedsDisplay()
layer.removeFromSuperlayer()
}
func isAnimating(_ node: Node) -> Bool {
if let _ = layerCache[node] {
return true
func containsAnimation(_ node: Node) -> Bool {
if isAnimating(node) {
return true
}
if let group = node as? Group {
return isChildrenAnimating(group)
}
return false
}
return false
}
func isChildrenAnimating(_ group: Group) -> Bool {
for child in group.contents {
if isAnimating(child) {
return true
}
if let childGroup = child as? Group {
return isChildrenAnimating(childGroup)
}
func animations() -> [Animation] {
return layerCache.map { $0.1.animation }
}
return false
}
func containsAnimation(_ node: Node) -> Bool {
if isAnimating(node) {
return true
func replace(original: Node, replacement: Node) {
guard let layer = layerCache[original] else {
return
}
layerCache[replacement] = layer
layerCache.removeValue(forKey: original)
}
if let group = node as? Group {
return isChildrenAnimating(group)
}
return false
}
func animations() -> [Animation] {
return layerCache.map ({ $0.1.animation })
}
func replace(original: Node, replacement: Node) {
guard let layer = layerCache[original] else {
return
}
layerCache[replacement] = layer
layerCache.removeValue(forKey: original)
}
}

View File

@ -1,21 +1,24 @@
import Foundation
let animationRestorer = AnimationRestorer()
open class AnimationRestorer {
typealias RestoreClosure = () -> ()
var restoreClosures = [RestoreClosure]()
func addRestoreClosure(_ closure: @escaping RestoreClosure) {
restoreClosures.append(closure)
}
open class func restore() {
DispatchQueue.main.async {
animationRestorer.restoreClosures.forEach { restoreClosure in
restoreClosure()
}
animationRestorer.restoreClosures.removeAll()
typealias RestoreClosure = () -> Void
var restoreClosures = [RestoreClosure]()
func addRestoreClosure(_ closure: @escaping RestoreClosure) {
restoreClosures.append(closure)
}
open class func restore() {
DispatchQueue.main.async {
animationRestorer.restoreClosures.forEach { restoreClosure in
restoreClosure()
}
animationRestorer.restoreClosures.removeAll()
}
}
}
}

View File

@ -1,12 +1,11 @@
// TODO: Implement better hash
extension Node: Hashable {
public var hashValue: Int {
return Unmanaged.passUnretained(self).toOpaque().hashValue
}
public var hashValue: Int {
return Unmanaged.passUnretained(self).toOpaque().hashValue
}
}
public func == (lhs: Node, rhs: Node) -> Bool {
return lhs === rhs
return lhs === rhs
}

View File

@ -1,20 +1,19 @@
extension Transform: Hashable {
public var hashValue: Int {
return m11.hashValue ^
m12.hashValue ^
m21.hashValue ^
m22.hashValue ^
dx.hashValue ^
dy.hashValue
}
public var hashValue: Int {
return m11.hashValue ^
m12.hashValue ^
m21.hashValue ^
m22.hashValue ^
dx.hashValue ^
dy.hashValue
}
}
public func == (lhs: Transform, rhs: Transform) -> Bool {
return lhs.m11 == rhs.m11 &&
lhs.m12 == rhs.m12 &&
lhs.m21 == rhs.m21 &&
lhs.m22 == rhs.m22 &&
lhs.dx == rhs.dx &&
lhs.dy == rhs.dy
return lhs.m11 == rhs.m11 &&
lhs.m12 == rhs.m12 &&
lhs.m21 == rhs.m21 &&
lhs.m22 == rhs.m22 &&
lhs.dx == rhs.dx &&
lhs.dy == rhs.dy
}

View File

@ -9,47 +9,46 @@
import Foundation
#if os(iOS)
import UIKit
import UIKit
#elseif os(OSX)
import AppKit
import AppKit
#endif
func addMorphingAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animationCache: AnimationCache?, completion: @escaping (() -> ())) {
func addMorphingAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animationCache: AnimationCache?, completion: @escaping (() -> Void)) {
guard let morphingAnimation = animation as? MorphingAnimation else {
return
}
guard let nodeId = animation.nodeId, let shape = Node.nodeBy(id: nodeId) as? Shape else {
return
}
let mutatingShape = SceneUtils.shapeCopy(from: shape)
nodesMap.replace(node: shape, to: mutatingShape)
animationCache?.replace(original: shape, replacement: mutatingShape)
animation.nodeId = mutatingShape.id
let fromLocus = morphingAnimation.getVFunc()(0.0)
let toLocus = morphingAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0)
let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration()
guard let layer = animationCache?.layerForNode(mutatingShape, animation: animation, shouldRenderContent: false) else {
return
}
// Creating proper animation
let generatedAnim = pathAnimation(
from:fromLocus,
to:toLocus,
from: fromLocus,
to: toLocus,
duration: duration,
renderTransform: layer.renderTransform!)
generatedAnim.repeatCount = Float(animation.repeatCount)
generatedAnim.timingFunction = caTimingFunction(animation.easing)
generatedAnim.autoreverses = animation.autoreverses
generatedAnim.completion = { finished in
if animation.manualStop {
animation.progress = 0.0
mutatingShape.form = morphingAnimation.getVFunc()(0.0)
@ -57,33 +56,33 @@ func addMorphingAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anim
animation.progress = 1.0
mutatingShape.form = morphingAnimation.getVFunc()(1.0)
}
animationCache?.freeLayer(mutatingShape)
if !animation.cycled &&
!animation.manualStop {
animation.completion?()
}
if !finished {
animationRestorer.addRestoreClosure(completion)
return
}
completion()
}
generatedAnim.progress = { progress in
let t = Double(progress)
mutatingShape.form = morphingAnimation.getVFunc()(t)
animation.progress = t
animation.onProgressUpdate?(t)
}
layer.path = RenderUtils.toCGPath(fromLocus)
// Stroke
if let stroke = mutatingShape.stroke {
if let color = stroke.fill as? Color {
@ -91,16 +90,16 @@ func addMorphingAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anim
} else {
layer.strokeColor = MColor.black.cgColor
}
layer.lineWidth = CGFloat(stroke.width)
layer.lineCap = RenderUtils.mapLineCapToString(stroke.cap)
layer.lineJoin = RenderUtils.mapLineJoinToString(stroke.join)
layer.lineDashPattern = stroke.dashes.map{ NSNumber(value: $0)}
layer.lineDashPattern = stroke.dashes.map { NSNumber(value: $0) }
} else {
layer.strokeColor = MColor.black.cgColor
layer.lineWidth = 1.0
}
// Fill
if let color = mutatingShape.fill as? Color {
layer.fillColor = RenderUtils.mapColor(color)
@ -114,18 +113,18 @@ func addMorphingAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anim
}
}
fileprivate func pathAnimation(from:Locus, to: Locus, duration: Double, renderTransform: CGAffineTransform) -> CAAnimation {
fileprivate func pathAnimation(from: Locus, to: Locus, duration: Double, renderTransform: CGAffineTransform) -> CAAnimation {
var transform = renderTransform
let fromPath = RenderUtils.toCGPath(from).copy(using: &transform)
let toPath = RenderUtils.toCGPath(to).copy(using: &transform)
let animation = CABasicAnimation(keyPath: "path")
animation.fromValue = fromPath
animation.toValue = toPath
animation.duration = duration
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
return animation
}

View File

@ -6,29 +6,29 @@ import Foundation
import AppKit
#endif
func addOpacityAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animationCache: AnimationCache?, completion: @escaping (() -> ())) {
guard let opacityAnimation = animation as? OpacityAnimation else {
return
}
func addOpacityAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animationCache: AnimationCache?, completion: @escaping (() -> Void)) {
guard let opacityAnimation = animation as? OpacityAnimation else {
return
}
guard let nodeId = animation.nodeId, let node = Node.nodeBy(id: nodeId) else {
return
}
guard let nodeId = animation.nodeId, let node = Node.nodeBy(id: nodeId) else {
return
}
// Creating proper animation
// Creating proper animation
let generatedAnimation = opacityAnimationByFunc(opacityAnimation.getVFunc(),
duration: animation.getDuration(),
offset: animation.pausedProgress,
fps: opacityAnimation.logicalFps)
generatedAnimation.repeatCount = Float(animation.repeatCount)
generatedAnimation.timingFunction = caTimingFunction(animation.easing)
generatedAnimation.repeatCount = Float(animation.repeatCount)
generatedAnimation.timingFunction = caTimingFunction(animation.easing)
generatedAnimation.completion = { finished in
generatedAnimation.completion = { finished in
animationCache?.freeLayer(node)
animationCache?.freeLayer(node)
if animation.paused {
animation.pausedProgress = animation.pausedProgress + animation.progress
animation.pausedProgress += animation.progress
node.opacityVar.value = opacityAnimation.getVFunc()(animation.pausedProgress)
} else if animation.manualStop {
animation.pausedProgress = 0.0
@ -46,22 +46,22 @@ func addOpacityAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anima
animation.completion?()
}
if !finished {
animationRestorer.addRestoreClosure(completion)
return
}
if !finished {
animationRestorer.addRestoreClosure(completion)
return
}
completion()
}
completion()
}
generatedAnimation.progress = { progress in
generatedAnimation.progress = { progress in
let t = Double(progress)
node.opacityVar.value = opacityAnimation.getVFunc()(t)
let t = Double(progress)
node.opacityVar.value = opacityAnimation.getVFunc()(t)
animation.progress = t
animation.onProgressUpdate?(t)
}
animation.progress = t
animation.onProgressUpdate?(t)
}
if let layer = animationCache?.layerForNode(node, animation: animation) {
layer.add(generatedAnimation, forKey: animation.ID)
@ -73,33 +73,33 @@ func addOpacityAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anima
func opacityAnimationByFunc(_ valueFunc: (Double) -> Double, duration: Double, offset: Double, fps: UInt) -> CAAnimation {
var opacityValues = [Double]()
var timeValues = [Double]()
var opacityValues = [Double]()
var timeValues = [Double]()
let step = 1.0 / (duration * Double(fps))
let step = 1.0 / (duration * Double(fps))
var dt = 0.0
var dt = 0.0
var tValue = Array(stride(from: 0.0, to: 1.0, by: step))
tValue.append(1.0)
for t in tValue {
for t in tValue {
dt = t
if 1.0 - dt < step {
dt = 1.0
}
dt = t
if 1.0 - dt < step {
dt = 1.0
}
let value = valueFunc(offset + dt)
opacityValues.append(value)
timeValues.append(dt)
}
let value = valueFunc(offset + dt)
opacityValues.append(value)
timeValues.append(dt)
}
let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity")
opacityAnimation.fillMode = kCAFillModeForwards
opacityAnimation.isRemovedOnCompletion = false
let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity")
opacityAnimation.fillMode = kCAFillModeForwards
opacityAnimation.isRemovedOnCompletion = false
opacityAnimation.duration = duration
opacityAnimation.values = opacityValues
opacityAnimation.keyTimes = timeValues as [NSNumber]?
opacityAnimation.duration = duration
opacityAnimation.values = opacityValues
opacityAnimation.keyTimes = timeValues as [NSNumber]?
return opacityAnimation
return opacityAnimation
}

View File

@ -14,15 +14,15 @@ import Foundation
import AppKit
#endif
func addShapeAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animationCache: AnimationCache?, completion: @escaping (() -> ())) {
func addShapeAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animationCache: AnimationCache?, completion: @escaping (() -> Void)) {
guard let shapeAnimation = animation as? ShapeAnimation else {
return
}
guard let nodeId = animation.nodeId, let shape = Node.nodeBy(id: nodeId) as? Shape else {
return
}
let mutatingShape = SceneUtils.shapeCopy(from: shape)
nodesMap.replace(node: shape, to: mutatingShape)
animationCache?.replace(original: shape, replacement: mutatingShape)
@ -30,71 +30,70 @@ func addShapeAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animati
let fromShape = shapeAnimation.getVFunc()(0.0)
let toShape = shapeAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0)
let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration()
guard let layer = animationCache?.layerForNode(mutatingShape, animation: animation, shouldRenderContent: false) else {
return
}
// Creating proper animation
let generatedAnim = generateShapeAnimation(
from:fromShape,
to:toShape,
from: fromShape,
to: toShape,
duration: duration,
renderTransform: layer.renderTransform!)
generatedAnim.repeatCount = Float(animation.repeatCount)
generatedAnim.timingFunction = caTimingFunction(animation.easing)
generatedAnim.autoreverses = animation.autoreverses
generatedAnim.completion = { finished in
animation.progress = animation.manualStop ? 0.0 : 1.0
if !animation.autoreverses && finished {
mutatingShape.form = toShape.form
mutatingShape.stroke = toShape.stroke
mutatingShape.fill = toShape.fill
}
if !finished {
animation.progress = 0.0
mutatingShape.form = fromShape.form
mutatingShape.stroke = fromShape.stroke
mutatingShape.fill = fromShape.fill
}
animationCache?.freeLayer(mutatingShape)
if !animation.cycled && !animation.manualStop {
animation.completion?()
}
if !finished {
animationRestorer.addRestoreClosure(completion)
return
}
completion()
}
generatedAnim.progress = { progress in
let t = Double(progress)
if !animation.autoreverses {
let currentShape = shapeAnimation.getVFunc()(t)
mutatingShape.form = currentShape.form
mutatingShape.stroke = currentShape.stroke
mutatingShape.fill = currentShape.fill
}
animation.progress = t
animation.onProgressUpdate?(t)
}
layer.path = RenderUtils.toCGPath(fromShape.form)
// Stroke
if let stroke = shape.stroke {
if let color = stroke.fill as? Color {
@ -102,101 +101,100 @@ func addShapeAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animati
} else {
layer.strokeColor = MColor.black.cgColor
}
layer.lineWidth = CGFloat(stroke.width)
layer.lineCap = RenderUtils.mapLineCapToString(stroke.cap)
layer.lineJoin = RenderUtils.mapLineJoinToString(stroke.join)
layer.lineDashPattern = stroke.dashes.map{ NSNumber(value: $0)}
layer.lineDashPattern = stroke.dashes.map { NSNumber(value: $0) }
} else if shape.fill == nil {
layer.strokeColor = MColor.black.cgColor
layer.lineWidth = 1.0
}
// Fill
if let color = shape.fill as? Color {
layer.fillColor = RenderUtils.mapColor(color)
} else {
layer.fillColor = MColor.clear.cgColor
}
layer.add(generatedAnim, forKey: animation.ID)
animation.removeFunc = {
layer.removeAnimation(forKey: animation.ID)
}
}
fileprivate func generateShapeAnimation(from:Shape, to: Shape, duration: Double, renderTransform: CGAffineTransform) -> CAAnimation {
fileprivate func generateShapeAnimation(from: Shape, to: Shape, duration: Double, renderTransform: CGAffineTransform) -> CAAnimation {
let group = CAAnimationGroup()
// Shape
// Path
var transform = renderTransform
let fromPath = RenderUtils.toCGPath(from.form).copy(using: &transform)
let toPath = RenderUtils.toCGPath(to.form).copy(using: &transform)
let pathAnimation = CABasicAnimation(keyPath: "path")
pathAnimation.fromValue = fromPath
pathAnimation.toValue = toPath
pathAnimation.duration = duration
group.animations = [pathAnimation]
group.animations = [pathAnimation]
// Fill
let fromFillColor = from.fill as? Color ?? Color.clear
let toFillColor = to.fill as? Color ?? Color.clear
if fromFillColor != toFillColor {
let fillAnimation = CABasicAnimation(keyPath: "fillColor")
fillAnimation.fromValue = RenderUtils.mapColor(fromFillColor)
fillAnimation.toValue = RenderUtils.mapColor(toFillColor)
fillAnimation.duration = duration
group.animations?.append(fillAnimation)
}
// Stroke
let fromStroke = from.stroke ?? Stroke(fill: Color.black, width: 1.0)
let toStroke = to.stroke ?? Stroke(fill: Color.black, width: 1.0)
// Line width
if fromStroke.width != toStroke.width {
let strokeWidthAnimation = CABasicAnimation(keyPath: "lineWidth")
strokeWidthAnimation.fromValue = fromStroke.width
strokeWidthAnimation.toValue = toStroke.width
strokeWidthAnimation.duration = duration
group.animations?.append(strokeWidthAnimation)
}
// Line color
// Line color
let fromStrokeColor = fromStroke.fill as? Color ?? Color.black
let toStrokeColor = toStroke.fill as? Color ?? Color.black
if fromStrokeColor != toStrokeColor {
let strokeColorAnimation = CABasicAnimation(keyPath: "strokeColor")
strokeColorAnimation.fromValue = RenderUtils.mapColor(fromStrokeColor)
strokeColorAnimation.toValue = RenderUtils.mapColor(toStrokeColor)
strokeColorAnimation.duration = duration
group.animations?.append(strokeColorAnimation)
}
// Dash pattern
if fromStroke.dashes != toStroke.dashes {
let dashPatternAnimation = CABasicAnimation(keyPath: "lineDashPattern")
dashPatternAnimation.fromValue = fromStroke.dashes
dashPatternAnimation.toValue = toStroke.dashes
dashPatternAnimation.duration = duration
group.animations?.append(dashPatternAnimation)
}
// Group
group.duration = duration
group.fillMode = kCAFillModeForwards
group.isRemovedOnCompletion = false
return group
}

View File

@ -1,43 +1,43 @@
import Foundation
#if os(iOS)
import UIKit
import UIKit
#elseif os(OSX)
import AppKit
import AppKit
#endif
func caTimingFunction(_ easing: Easing) -> CAMediaTimingFunction {
switch easing {
case .ease:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)
case .linear:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
case .easeIn:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
case .easeOut:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
case .easeInOut:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
}
switch easing {
case .ease:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)
case .linear:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
case .easeIn:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
case .easeOut:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
case .easeInOut:
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
}
}
func progressForTimingFunction(_ easing: Easing, progress: Double) -> Double {
let t = progress
switch easing {
case .ease:
return t
case .linear:
return t
case .easeIn:
return t * t
case .easeOut:
return -(t * (t - 2))
case .easeInOut:
if t < 0.5 {
return 2.0 * t * t
} else {
return -2.0 * t * t + 4.0 * t - 1.0
let t = progress
switch easing {
case .ease:
return t
case .linear:
return t
case .easeIn:
return t * t
case .easeOut:
return -(t * (t - 2))
case .easeInOut:
if t < 0.5 {
return 2.0 * t * t
} else {
return -2.0 * t * t + 4.0 * t - 1.0
}
}
}
}

View File

@ -6,35 +6,35 @@ import Foundation
import AppKit
#endif
func addTransformAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animationCache: AnimationCache?, completion: @escaping (() -> ())) {
guard let transformAnimation = animation as? TransformAnimation else {
return
}
func addTransformAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animationCache: AnimationCache?, completion: @escaping (() -> Void)) {
guard let transformAnimation = animation as? TransformAnimation else {
return
}
guard let nodeId = animation.nodeId, let node = Node.nodeBy(id: nodeId) else {
return
}
guard let nodeId = animation.nodeId, let node = Node.nodeBy(id: nodeId) else {
return
}
// Creating proper animation
var generatedAnimation: CAAnimation?
// Creating proper animation
var generatedAnimation: CAAnimation?
generatedAnimation = transformAnimationByFunc(node,
valueFunc: transformAnimation.getVFunc(),
duration: animation.getDuration(),
offset: animation.pausedProgress,
fps: transformAnimation.logicalFps)
generatedAnimation = transformAnimationByFunc(node,
valueFunc: transformAnimation.getVFunc(),
duration: animation.getDuration(),
offset: animation.pausedProgress,
fps: transformAnimation.logicalFps)
guard let generatedAnim = generatedAnimation else {
return
}
guard let generatedAnim = generatedAnimation else {
return
}
generatedAnim.repeatCount = Float(animation.repeatCount)
generatedAnim.timingFunction = caTimingFunction(animation.easing)
generatedAnim.repeatCount = Float(animation.repeatCount)
generatedAnim.timingFunction = caTimingFunction(animation.easing)
generatedAnim.completion = { finished in
generatedAnim.completion = { finished in
if animation.paused {
animation.pausedProgress = animation.pausedProgress + animation.progress
animation.pausedProgress += animation.progress
node.placeVar.value = transformAnimation.getVFunc()(animation.pausedProgress)
} else if animation.manualStop {
animation.pausedProgress = 0.0
@ -45,71 +45,71 @@ func addTransformAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, ani
animation.progress = 1.0
node.placeVar.value = transformAnimation.getVFunc()(1.0)
}
animationCache?.freeLayer(node)
animationCache?.freeLayer(node)
if !animation.cycled &&
!animation.manualStop &&
!animation.paused {
animation.completion?()
}
if !finished {
animationRestorer.addRestoreClosure(completion)
return
}
if !finished {
animationRestorer.addRestoreClosure(completion)
return
}
completion()
}
completion()
}
generatedAnim.progress = { progress in
generatedAnim.progress = { progress in
let t = Double(progress)
node.placeVar.value = transformAnimation.getVFunc()(t)
let t = Double(progress)
node.placeVar.value = transformAnimation.getVFunc()(t)
animation.progress = t
animation.onProgressUpdate?(t)
}
animation.progress = t
animation.onProgressUpdate?(t)
}
if let layer = animationCache?.layerForNode(node, animation: animation) {
layer.add(generatedAnim, forKey: animation.ID)
animation.removeFunc = {
layer.removeAnimation(forKey: animation.ID)
layer.removeAnimation(forKey: animation.ID)
}
}
}
func transfomToCG(_ transform: Transform) -> CGAffineTransform {
return CGAffineTransform(
a: CGFloat(transform.m11),
b: CGFloat(transform.m12),
c: CGFloat(transform.m21),
d: CGFloat(transform.m22),
tx: CGFloat(transform.dx),
ty: CGFloat(transform.dy))
return CGAffineTransform(
a: CGFloat(transform.m11),
b: CGFloat(transform.m12),
c: CGFloat(transform.m21),
d: CGFloat(transform.m22),
tx: CGFloat(transform.dx),
ty: CGFloat(transform.dy))
}
func transformAnimationByFunc(_ node: Node, valueFunc: (Double) -> Transform, duration: Double, offset: Double, fps: UInt) -> CAAnimation {
var transformValues = [CATransform3D]()
var timeValues = [Double]()
var timeValues = [Double]()
let step = 1.0 / (duration * Double(fps))
var dt = 0.0
let step = 1.0 / (duration * Double(fps))
var dt = 0.0
var tValue = Array(stride(from: 0.0, to: 1.0, by: step))
tValue.append(1.0)
for t in tValue {
for t in tValue {
dt = t
if 1.0 - dt < step {
dt = 1.0
}
dt = t
if 1.0 - dt < step {
dt = 1.0
}
timeValues.append(dt)
let value = AnimationUtils.absoluteTransform(node, pos: valueFunc(offset + dt))
let value = AnimationUtils.absoluteTransform(node, pos: valueFunc(offset + dt))
let cgValue = CATransform3DMakeAffineTransform(RenderUtils.mapTransform(value))
transformValues.append(cgValue)
}
}
let transformAnimation = CAKeyframeAnimation(keyPath: "transform")
transformAnimation.duration = duration
@ -118,9 +118,9 @@ func transformAnimationByFunc(_ node: Node, valueFunc: (Double) -> Transform, du
transformAnimation.fillMode = kCAFillModeForwards
transformAnimation.isRemovedOnCompletion = false
return transformAnimation
return transformAnimation
}
func fixedAngle(_ angle: CGFloat) -> CGFloat {
return angle > -0.0000000000000000000000001 ? angle : CGFloat(2.0 * Double.pi) + angle
return angle > -0.0000000000000000000000001 ? angle : CGFloat(2.0 * Double.pi) + angle
}

View File

@ -9,14 +9,14 @@
import Foundation
open class Disposable {
let handler: (()->())
init (_ disposeHandler: @escaping (()->()) ) {
handler = disposeHandler
}
open func dispose() {
handler()
}
let handler: (() -> Void)
init (_ disposeHandler: @escaping (() -> Void) ) {
handler = disposeHandler
}
open func dispose() {
handler()
}
}

View File

@ -7,24 +7,24 @@
//
open class GroupDisposable {
fileprivate var items: [Disposable] = []
open func dispose() {
for disposable in items {
disposable.dispose()
fileprivate var items: [Disposable] = []
open func dispose() {
for disposable in items {
disposable.dispose()
}
items = []
}
items = []
}
open func add(_ item: Disposable) {
items.append(item)
}
open func add(_ item: Disposable) {
items.append(item)
}
}
extension Disposable {
public func addTo(_ group: GroupDisposable) {
group.add(self)
}
public func addTo(_ group: GroupDisposable) {
group.add(self)
}
}

View File

@ -9,39 +9,39 @@
import Foundation
class ChangeHandler<T>: Equatable {
let handle: ((T)->())
init(_ f: @escaping ((T)->()) ) {
handle = f
}
static func ==(lhs: ChangeHandler<T>, rhs: ChangeHandler<T>) -> Bool {
return lhs === rhs
}
let handle: ((T) -> Void)
init(_ f: @escaping ((T) -> Void) ) {
handle = f
}
static func == (lhs: ChangeHandler<T>, rhs: ChangeHandler<T>) -> Bool {
return lhs === rhs
}
}
open class Variable<T> {
var handlers = [ChangeHandler<T>]()
open var value: T {
didSet {
handlers.forEach { handler in handler.handle(value) }
var handlers = [ChangeHandler<T>]()
open var value: T {
didSet {
handlers.forEach { handler in handler.handle(value) }
}
}
init(_ v: T) {
value = v
}
@discardableResult open func onChange(_ f: @escaping ((T) -> Void)) -> Disposable {
let handler = ChangeHandler<T>(f)
handlers.append(handler)
return Disposable { [weak self] in
guard let index = self?.handlers.index(of: handler) else {
return
}
self?.handlers.remove(at: index)
}
}
}
init(_ v: T) {
value = v
}
@discardableResult open func onChange(_ f: @escaping ((T)->())) -> Disposable {
let handler = ChangeHandler<T>(f)
handlers.append(handler)
return Disposable({ [weak self] in
guard let index = self?.handlers.index(of: handler) else {
return
}
self?.handlers.remove(at: index)
})
}
}

View File

@ -9,16 +9,16 @@
import Foundation
open class Event {
public weak var node: Node?
var consumed = false
init(node: Node) {
self.node = node
}
public func consume() {
consumed = true
}
public weak var node: Node?
var consumed = false
init(node: Node) {
self.node = node
}
public func consume() {
consumed = true
}
}

View File

@ -1,14 +1,14 @@
open class PanEvent : Event {
open let dx: Double
open let dy: Double
open let count: Int
init(node: Node, dx: Double, dy: Double, count: Int) {
self.dx = dx
self.dy = dy
self.count = count
super.init(node: node)
}
open class PanEvent: Event {
open let dx: Double
open let dy: Double
open let count: Int
init(node: Node, dx: Double, dy: Double, count: Int) {
self.dx = dx
self.dy = dy
self.count = count
super.init(node: node)
}
}

View File

@ -1,10 +1,10 @@
open class PinchEvent : Event {
open let scale: Double
init(node: Node, scale: Double) {
self.scale = scale
super.init(node: node)
}
open class PinchEvent: Event {
open let scale: Double
init(node: Node, scale: Double) {
self.scale = scale
super.init(node: node)
}
}

View File

@ -1,10 +1,10 @@
open class RotateEvent : Event {
open let angle: Double
init(node: Node, angle: Double) {
self.angle = angle
super.init(node: node)
}
open class RotateEvent: Event {
open let angle: Double
init(node: Node, angle: Double) {
self.angle = angle
super.init(node: node)
}
}

View File

@ -1,10 +1,10 @@
open class TapEvent : Event {
open let location: Point
init(node: Node, location: Point) {
self.location = location
super.init(node: node)
}
open class TapEvent: Event {
open let location: Point
init(node: Node, location: Point) {
self.location = location
super.init(node: node)
}
}

View File

@ -6,19 +6,18 @@
//
//
public struct TouchPoint {
public let id: Int
public let location: Point
public let id: Int
public let location: Point
}
public class TouchEvent : Event {
public let points: [TouchPoint]
public init(node: Node, points: [TouchPoint]) {
self.points = points
super.init(node: node)
}
public class TouchEvent: Event {
public let points: [TouchPoint]
public init(node: Node, points: [TouchPoint]) {
self.points = points
super.init(node: node)
}
}

View File

@ -16,7 +16,7 @@ public extension MacawView {
defer {
backgroundColor = currentColor
}
var frame = CGRect(origin: CGPoint.zero, size: size)
let ctx = CGContext(path as CFURL, mediaBox: &frame, .none)!
@ -26,10 +26,10 @@ public extension MacawView {
x: size.width / bounds.width,
y: -size.height / bounds.height
)
context.cgContext = ctx
renderer?.render(force: false, opacity: node.opacity)
ctx.endPDFPage()
}
}

View File

@ -1,5 +1,5 @@
public enum Align {
case min
case mid
case max
case min
case mid
case max
}

View File

@ -1,5 +1,5 @@
public enum AspectRatio {
case none
case meet
case slice
case none
case meet
case slice
}

View File

@ -1,6 +1,6 @@
public enum Baseline {
case top
case alphabetic
case bottom
case mid
case top
case alphabetic
case bottom
case mid
}

View File

@ -1,9 +1,9 @@
import Foundation
open class Color: Fill, Equatable {
open let val: Int
open static let white: Color = Color( val: 0xFFFFFF )
open static let silver: Color = Color( val: 0xC0C0C0 )
open static let gray: Color = Color( val: 0x808080 )
@ -21,56 +21,56 @@ open class Color: Fill, Equatable {
open static let fuchsia: Color = Color( val: 0xFF00FF )
open static let purple: Color = Color( val: 0x800080 )
open static let clear: Color = Color.rgba(r: 0, g: 0, b: 0, a: 0)
public init(val: Int = 0) {
self.val = val
}
// GENERATED
open func r() -> Int {
return ( ( val >> 16 ) & 0xff )
}
// GENERATED
open func g() -> Int {
return ( ( val >> 8 ) & 0xff )
}
// GENERATED
open func b() -> Int {
return ( val & 0xff )
}
// GENERATED
open func a() -> Int {
return ( 255 - ( ( val >> 24 ) & 0xff ) )
}
// GENERATED
public func with(a: Double) -> Color {
return Color.rgba(r: r(), g: g(), b: b(), a: a)
}
// GENERATED
open class func rgbt(r: Int, g: Int, b: Int, t: Int) -> Color {
let x = ( ( t & 0xff ) << 24 )
let y = ( ( r & 0xff ) << 16 )
let z = ( ( g & 0xff ) << 8 )
let q = b & 0xff
return Color( val: ( ( ( x | y ) | z ) | q ) )
let q = b & 0xff
return Color( val: ( ( ( x | y ) | z ) | q ) )
}
// GENERATED
open class func rgba(r: Int, g: Int, b: Int, a: Double) -> Color {
return rgbt( r: r, g: g, b: b, t: Int( ( ( 1 - a ) * 255 ) ) )
}
// GENERATED
open class func rgb(r: Int, g: Int, b: Int) -> Color {
return rgbt( r: r, g: g, b: b, t: 0 )
}
public static func ==(lhs: Color, rhs: Color) -> Bool {
public static func == (lhs: Color, rhs: Color) -> Bool {
return lhs.val == rhs.val
}
}

View File

@ -1,12 +1,12 @@
import Foundation
open class Drawable {
open let visible: Bool
open let tag: [String]
public init(visible: Bool = true, tag: [String] = []) {
self.visible = visible
self.tag = tag
}
open let visible: Bool
open let tag: [String]
public init(visible: Bool = true, tag: [String] = []) {
self.visible = visible
self.tag = tag
}
}

View File

@ -1,16 +1,16 @@
import Foundation
open class DropShadow: Effect {
open let radius: Double
open let offset: Point
open let color: Color
open let input: Effect?
public init(radius: Double = 0, offset: Point = Point.origin, color: Color = Color.black, input: Effect? = nil) {
self.radius = radius
self.offset = offset
self.color = color
self.input = input
}
open let radius: Double
open let offset: Point
open let color: Color
open let input: Effect?
public init(radius: Double = 0, offset: Point = Point.origin, color: Color = Color.black, input: Effect? = nil) {
self.radius = radius
self.offset = offset
self.color = color
self.input = input
}
}

View File

@ -1,8 +1,8 @@
import Foundation
open class Effect {
public init() {
}
public init() {
}
}

View File

@ -2,7 +2,7 @@ import Foundation
open class Fill {
public init() {
}
public init() {
}
}

View File

@ -1,11 +1,11 @@
import Foundation
open class Font {
open let name: String
open let size: Int
open let weight: String
public init(name: String = "Serif", size: Int = 12, weight: String = "normal") {
self.name = name
self.size = size

View File

@ -2,11 +2,11 @@ import Foundation
open class GaussianBlur: Effect {
open let radius: Double
open let input: Effect?
open let radius: Double
open let input: Effect?
public init(radius: Double = 0, input: Effect? = nil) {
self.radius = radius
self.input = input
}
public init(radius: Double = 0, input: Effect? = nil) {
self.radius = radius
self.input = input
}
}

View File

@ -2,11 +2,11 @@ import Foundation
open class Gradient: Fill {
open let userSpace: Bool
open let stops: [Stop]
open let userSpace: Bool
open let stops: [Stop]
public init(userSpace: Bool = false, stops: [Stop] = []) {
self.userSpace = userSpace
self.stops = stops
}
public init(userSpace: Bool = false, stops: [Stop] = []) {
self.userSpace = userSpace
self.stops = stops
}
}

View File

@ -1,5 +1,5 @@
public enum LineCap {
case butt
case round
case square
case butt
case round
case square
}

View File

@ -1,5 +1,5 @@
public enum LineJoin {
case miter
case round
case bevel
case miter
case round
case bevel
}

View File

@ -1,49 +1,49 @@
import Foundation
#if os(iOS)
import UIKit
import UIKit
#endif
open class LinearGradient: Gradient {
open var x1: Double
open var y1: Double
open var x2: Double
open var y2: Double
public init(x1: Double = 0, y1: Double = 0, x2: Double = 0, y2: Double = 0, userSpace: Bool = false, stops: [Stop] = []) {
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
super.init(
userSpace: userSpace,
stops: stops
)
}
public init(degree: Double = 0, from: Color, to: Color) {
self.x1 = degree >= 135 && degree < 270 ? 1 : 0
self.y1 = degree < 225 ? 0 : 1
self.x2 = degree < 90 || degree >= 315 ? 1 : 0
self.y2 = degree >= 45 && degree < 180 ? 1 : 0
super.init(
userSpace: false,
stops: [Stop(offset: 0, color: from), Stop(offset: 1, color: to)]
)
}
func applyTransform(_ transform: Transform) {
// TODO: - Check logic
let cgTransform = RenderUtils.mapTransform(transform)
let point1 = CGPoint(x: x1, y: y1).applying(cgTransform)
x1 = point1.x.doubleValue
y1 = point1.y.doubleValue
let point2 = CGPoint(x: x2, y: y2).applying(cgTransform)
x2 = point2.x.doubleValue
y2 = point2.y.doubleValue
}
open var x1: Double
open var y1: Double
open var x2: Double
open var y2: Double
public init(x1: Double = 0, y1: Double = 0, x2: Double = 0, y2: Double = 0, userSpace: Bool = false, stops: [Stop] = []) {
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
super.init(
userSpace: userSpace,
stops: stops
)
}
public init(degree: Double = 0, from: Color, to: Color) {
self.x1 = degree >= 135 && degree < 270 ? 1 : 0
self.y1 = degree < 225 ? 0 : 1
self.x2 = degree < 90 || degree >= 315 ? 1 : 0
self.y2 = degree >= 45 && degree < 180 ? 1 : 0
super.init(
userSpace: false,
stops: [Stop(offset: 0, color: from), Stop(offset: 1, color: to)]
)
}
func applyTransform(_ transform: Transform) {
// TODO: - Check logic
let cgTransform = RenderUtils.mapTransform(transform)
let point1 = CGPoint(x: x1, y: y1).applying(cgTransform)
x1 = point1.x.doubleValue
y1 = point1.y.doubleValue
let point2 = CGPoint(x: x2, y: y2).applying(cgTransform)
x2 = point2.x.doubleValue
y2 = point2.y.doubleValue
}
}

View File

@ -1,40 +1,40 @@
import Foundation
#if os(iOS)
import UIKit
import UIKit
#endif
open class RadialGradient: Gradient {
open var cx: Double
open var cy: Double
open var fx: Double
open var fy: Double
open let r: Double
public init(cx: Double = 0.5, cy: Double = 0.5, fx: Double = 0.5, fy: Double = 0.5, r: Double = 0.5, userSpace: Bool = false, stops: [Stop] = []) {
self.cx = cx
self.cy = cy
self.fx = fx
self.fy = fy
self.r = r
super.init(
userSpace: userSpace,
stops: stops
)
}
func applyTransform(_ transform: Transform) {
// TODO: - Check logic
let cgTransform = RenderUtils.mapTransform(transform)
let point1 = CGPoint(x: cx, y: cy).applying(cgTransform)
cx = point1.x.doubleValue
cy = point1.y.doubleValue
let point2 = CGPoint(x: fx, y: fy).applying(cgTransform)
fx = point2.x.doubleValue
fy = point2.y.doubleValue
}
open var cx: Double
open var cy: Double
open var fx: Double
open var fy: Double
open let r: Double
public init(cx: Double = 0.5, cy: Double = 0.5, fx: Double = 0.5, fy: Double = 0.5, r: Double = 0.5, userSpace: Bool = false, stops: [Stop] = []) {
self.cx = cx
self.cy = cy
self.fx = fx
self.fy = fy
self.r = r
super.init(
userSpace: userSpace,
stops: stops
)
}
func applyTransform(_ transform: Transform) {
// TODO: - Check logic
let cgTransform = RenderUtils.mapTransform(transform)
let point1 = CGPoint(x: cx, y: cy).applying(cgTransform)
cx = point1.x.doubleValue
cy = point1.y.doubleValue
let point2 = CGPoint(x: fx, y: fy).applying(cgTransform)
fx = point2.x.doubleValue
fy = point2.y.doubleValue
}
}

View File

@ -2,11 +2,11 @@ import Foundation
open class Stop {
open let offset: Double
open let color: Color
open let offset: Double
open let color: Color
public init(offset: Double = 0, color: Color) {
self.offset = offset
self.color = color
}
public init(offset: Double = 0, color: Color) {
self.offset = offset
self.color = color
}
}

View File

@ -2,17 +2,17 @@ import Foundation
open class Stroke {
open let fill: Fill
open let width: Double
open let cap: LineCap
open let join: LineJoin
open let dashes: [Double]
open let fill: Fill
open let width: Double
open let cap: LineCap
open let join: LineJoin
open let dashes: [Double]
public init(fill: Fill = Color.black, width: Double = 1, cap: LineCap = .butt, join: LineJoin = .miter, dashes: [Double] = []) {
self.fill = fill
self.width = width
self.cap = cap
self.join = join
self.dashes = dashes
}
public init(fill: Fill = Color.black, width: Double = 1, cap: LineCap = .butt, join: LineJoin = .miter, dashes: [Double] = []) {
self.fill = fill
self.width = width
self.cap = cap
self.join = join
self.dashes = dashes
}
}

View File

@ -1,22 +1,22 @@
import Foundation
open class Arc: Locus {
open let ellipse: Ellipse
open let shift: Double
open let extent: Double
public init(ellipse: Ellipse, shift: Double = 0, extent: Double = 0) {
self.ellipse = ellipse
self.shift = shift
self.extent = extent
}
override open func bounds() -> Rect {
return Rect(
x: ellipse.cx - ellipse.rx,
y: ellipse.cy - ellipse.ry,
w: ellipse.rx * 2.0,
h: ellipse.ry * 2.0)
}
open let ellipse: Ellipse
open let shift: Double
open let extent: Double
public init(ellipse: Ellipse, shift: Double = 0, extent: Double = 0) {
self.ellipse = ellipse
self.shift = shift
self.extent = extent
}
override open func bounds() -> Rect {
return Rect(
x: ellipse.cx - ellipse.rx,
y: ellipse.cy - ellipse.ry,
w: ellipse.rx * 2.0,
h: ellipse.ry * 2.0)
}
}

View File

@ -1,27 +1,27 @@
import Foundation
open class Circle: Locus {
open let cx: Double
open let cy: Double
open let r: Double
public init(cx: Double = 0, cy: Double = 0, r: Double = 0) {
self.cx = cx
self.cy = cy
self.r = r
}
override open func bounds() -> Rect {
return Rect(
x: cx - r,
y: cy - r,
w: r * 2.0,
h: r * 2.0)
}
// GENERATED NOT
open func arc(shift: Double, extent: Double) -> Arc {
return Arc(ellipse: Ellipse(cx: cx, cy: cy, rx: r, ry: r), shift: shift, extent: extent)
}
open let cx: Double
open let cy: Double
open let r: Double
public init(cx: Double = 0, cy: Double = 0, r: Double = 0) {
self.cx = cx
self.cy = cy
self.r = r
}
override open func bounds() -> Rect {
return Rect(
x: cx - r,
y: cy - r,
w: r * 2.0,
h: r * 2.0)
}
// GENERATED NOT
open func arc(shift: Double, extent: Double) -> Arc {
return Arc(ellipse: Ellipse(cx: cx, cy: cy, rx: r, ry: r), shift: shift, extent: extent)
}
}

View File

@ -1,29 +1,29 @@
import Foundation
open class Ellipse: Locus {
open let cx: Double
open let cy: Double
open let rx: Double
open let ry: Double
public init(cx: Double = 0, cy: Double = 0, rx: Double = 0, ry: Double = 0) {
self.cx = cx
self.cy = cy
self.rx = rx
self.ry = ry
}
override open func bounds() -> Rect {
return Rect(
x: cx - rx,
y: cy - ry,
w: rx * 2.0,
h: ry * 2.0)
}
// GENERATED NOT
open func arc(shift: Double, extent: Double) -> Arc {
return Arc(ellipse: self, shift: shift, extent: extent)
}
open let cx: Double
open let cy: Double
open let rx: Double
open let ry: Double
public init(cx: Double = 0, cy: Double = 0, rx: Double = 0, ry: Double = 0) {
self.cx = cx
self.cy = cy
self.rx = rx
self.ry = ry
}
override open func bounds() -> Rect {
return Rect(
x: cx - rx,
y: cy - ry,
w: rx * 2.0,
h: ry * 2.0)
}
// GENERATED NOT
open func arc(shift: Double, extent: Double) -> Arc {
return Arc(ellipse: self, shift: shift, extent: extent)
}
}

View File

@ -1,15 +1,15 @@
import Foundation
open class GeomUtils {
fileprivate class func rectToPath(_ rect: Rect) -> Path {
return MoveTo(x: rect.x, y: rect.y).lineTo(x: rect.x, y: rect.y + rect.h).lineTo(x: rect.x + rect.w, y: rect.y + rect.h).lineTo(x: rect.x + rect.w, y: rect.y).close().build()
}
fileprivate class func circleToPath(_ circle: Circle) -> Path {
return MoveTo(x: circle.cx, y: circle.cy).m(-circle.r, 0).a(circle.r, circle.r, 0.0, true, false, circle.r * 2.0, 0.0).a(circle.r, circle.r, 0.0, true, false, -(circle.r * 2.0), 0.0).build()
}
fileprivate class func arcToPath(_ arc: Arc) -> Path {
let rx = arc.ellipse.rx
let ry = arc.ellipse.ry
@ -17,32 +17,32 @@ open class GeomUtils {
let cy = arc.ellipse.cy
var delta = arc.extent
if (arc.shift == 0.0 && abs(arc.extent - Double.pi * 2.0) < 0.00001) {
if arc.shift == 0.0 && abs(arc.extent - Double.pi * 2.0) < 0.00001 {
delta = Double.pi * 2.0 - 0.001
}
let theta1 = arc.shift
let theta2 = theta1 + delta
let x1 = cx + rx * cos(theta1)
let y1 = cy + ry * sin(theta1)
let x2 = cx + rx * cos(theta2)
let y2 = cy + ry * sin(theta2)
let largeArcFlag = abs(delta) > .pi ? true : false
let sweepFlag = delta > 0.0 ? true : false
return PathBuilder(segment: PathSegment(type: .M, data: [x1, y1])).A(rx, ry, 0.0, largeArcFlag, sweepFlag, x2, y2).build()
}
fileprivate class func pointToPath(_ point: Point) -> Path {
return MoveTo(x: point.x, y: point.y).lineTo(x: point.x, y: point.y).build()
}
fileprivate class func pointsToPath(_ points: [Double], close: Bool = false) -> Path {
var pb = PathBuilder(segment: PathSegment(type: .M, data: [points[0], points[1]]))
if (points.count > 2) {
if points.count > 2 {
let parts = stride(from: 2, to: points.count, by: 2).map { Array(points[$0 ..< $0 + 2]) }
for part in parts {
pb = pb.lineTo(x: part[0], y: part[1])
@ -53,7 +53,7 @@ open class GeomUtils {
}
return pb.build()
}
open class func locusToPath(_ locus: Locus) -> Path {
if let rect = locus as? Rect {
return rectToPath(rect)
@ -63,7 +63,7 @@ open class GeomUtils {
return arcToPath(arc)
} else if let point = locus as? Point {
return MoveTo(x: point.x, y: point.y).lineTo(x: point.x, y: point.y).build()
} else if let line = locus as? Line {
} else if let line = locus as? Line {
return MoveTo(x: line.x1, y: line.y1).lineTo(x: line.x2, y: line.y2).build()
} else if let polygon = locus as? Polygon {
return pointsToPath(polygon.points, close: true)
@ -72,7 +72,7 @@ open class GeomUtils {
}
fatalError("Unsupported locus: \(locus)")
}
open class func concat(t1: Transform, t2: Transform) -> Transform {
let nm11 = t2.m11 * t1.m11 + t2.m12 * t1.m21
let nm21 = t2.m21 * t1.m11 + t2.m22 * t1.m21
@ -82,40 +82,40 @@ open class GeomUtils {
let ndy = t2.dx * t1.m12 + t2.dy * t1.m22 + t1.dy
return Transform(m11: nm11, m12: nm12, m21: nm21, m22: nm22, dx: ndx, dy: ndy)
}
open class func centerRotation(node: Node, place: Transform, angle: Double) -> Transform {
let center = GeomUtils.center(node: node)
return GeomUtils.anchorRotation(node: node, place: place, anchor: center, angle: angle)
}
open class func anchorRotation(node: Node, place: Transform, anchor: Point, angle: Double) -> Transform {
let move = Transform.move(dx: anchor.x, dy: anchor.y)
let asin = sin(angle); let acos = cos(angle)
let rotation = Transform(
let rotation = Transform(
m11: acos, m12: -asin,
m21: asin, m22: acos,
dx: 0.0, dy: 0.0
)
let t1 = GeomUtils.concat(t1: move, t2: rotation)
let t2 = GeomUtils.concat(t1: t1, t2: move.invert()!)
let result = GeomUtils.concat(t1: place, t2: t2)
return result
}
open class func centerScale(node: Node, sx: Double, sy: Double) -> Transform {
let center = GeomUtils.center(node: node)
return Transform.move(dx: center.x * (1.0 - sx), dy: center.y * (1.0 - sy)).scale(sx: sx, sy: sy)
}
open class func center(node: Node) -> Point {
guard let bounds = node.bounds() else {
return Point()
}
return Point(x: bounds.x + bounds.w / 2.0, y: bounds.y + bounds.h / 2.0)
}
}

View File

@ -1,16 +1,16 @@
import Foundation
open class Insets {
open let top: Double
open let right: Double
open let bottom: Double
open let left: Double
public init(top: Double = 0, right: Double = 0, bottom: Double = 0, left: Double = 0) {
self.top = top
self.right = right
self.bottom = bottom
self.left = left
}
open let top: Double
open let right: Double
open let bottom: Double
open let left: Double
public init(top: Double = 0, right: Double = 0, bottom: Double = 0, left: Double = 0) {
self.top = top
self.right = right
self.bottom = bottom
self.left = left
}
}

View File

@ -1,24 +1,24 @@
import Foundation
open class Line: Locus {
open let x1: Double
open let y1: Double
open let x2: Double
open let y2: Double
public init(x1: Double = 0, y1: Double = 0, x2: Double = 0, y2: Double = 0) {
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
}
override open func bounds() -> Rect {
return Rect(
x: min(x1, x2),
y: min(y1, y2),
w: abs(x1 - x2),
h: abs(y1 - y2))
}
open let x1: Double
open let y1: Double
open let x2: Double
open let y2: Double
public init(x1: Double = 0, y1: Double = 0, x2: Double = 0, y2: Double = 0) {
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
}
override open func bounds() -> Rect {
return Rect(
x: min(x1, x2),
y: min(y1, y2),
w: abs(x1 - x2),
h: abs(y1 - y2))
}
}

View File

@ -1,27 +1,27 @@
import Foundation
open class Locus {
public init() {
}
// GENERATED NOT
open func bounds() -> Rect {
return Rect()
}
// GENERATED NOT
open func stroke(with: Stroke) -> Shape {
return Shape(form: self, stroke: with)
}
// GENERATED NOT
open func fill(with: Fill) -> Shape {
return Shape(form: self, fill: with)
}
// GENERATED NOT
open func stroke(fill: Fill = Color.black, width: Double = 1, cap: LineCap = .butt, join: LineJoin = .miter, dashes: [Double] = []) -> Shape {
return Shape(form: self, stroke: Stroke(fill: fill, width: width, cap: cap, join: join, dashes: dashes))
}
public init() {
}
// GENERATED NOT
open func bounds() -> Rect {
return Rect()
}
// GENERATED NOT
open func stroke(with: Stroke) -> Shape {
return Shape(form: self, stroke: with)
}
// GENERATED NOT
open func fill(with: Fill) -> Shape {
return Shape(form: self, fill: with)
}
// GENERATED NOT
open func stroke(fill: Fill = Color.black, width: Double = 1, cap: LineCap = .butt, join: LineJoin = .miter, dashes: [Double] = []) -> Shape {
return Shape(form: self, stroke: Stroke(fill: fill, width: width, cap: cap, join: join, dashes: dashes))
}
}

View File

@ -1,9 +1,9 @@
import Foundation
open class MoveTo: PathBuilder {
// GENERATED NOT
public init(x: Double, y: Double) {
super.init(segment: PathSegment(type: .M, data: [x, y]))
}
// GENERATED NOT
public init(x: Double, y: Double) {
super.init(segment: PathSegment(type: .M, data: [x, y]))
}
}

View File

@ -1,14 +1,14 @@
import Foundation
open class Path: Locus {
open let segments: [PathSegment]
public init(segments: [PathSegment] = []) {
self.segments = segments
}
override open func bounds() -> Rect {
return pathBounds(self)!
}
open let segments: [PathSegment]
public init(segments: [PathSegment] = []) {
self.segments = segments
}
override open func bounds() -> Rect {
return pathBounds(self)!
}
}

View File

@ -1,163 +1,163 @@
import Foundation
open class PathBuilder {
open let segment: PathSegment
open let rest: PathBuilder?
public init(segment: PathSegment, rest: PathBuilder? = nil) {
self.segment = segment
self.rest = rest
}
// GENERATED NOT
open func moveTo(x: Double, y: Double) -> PathBuilder {
return M(x, y)
}
// GENERATED NOT
open func lineTo(x: Double, y: Double) -> PathBuilder {
return L(x, y)
}
// GENERATED NOT
open func cubicTo(x1: Double, y1: Double, x2: Double, y2: Double, x: Double, y: Double) -> PathBuilder {
return C(x1, y1, x2, y2, x, y)
}
// GENERATED NOT
open func quadraticTo(x1: Double, y1: Double, x: Double, y: Double) -> PathBuilder {
return Q(x1, y1, x, y)
}
// GENERATED NOT
open func arcTo(rx: Double, ry: Double, angle: Double, largeArc: Bool, sweep: Bool, x: Double, y: Double) -> PathBuilder {
return A(rx, ry, angle, largeArc, sweep, x, y)
}
// GENERATED NOT
open func close() -> PathBuilder {
return Z()
}
// GENERATED NOT
open func m(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .m, data: [x, y]), rest: self)
}
// GENERATED NOT
open func M(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .M, data: [x, y]), rest: self)
}
// GENERATED NOT
open func l(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .l, data: [x, y]), rest: self)
}
// GENERATED NOT
open func L(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .L, data: [x, y]), rest: self)
}
// GENERATED NOT
open func h(_ x: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .h, data: [x]), rest: self)
}
// GENERATED NOT
open func H(_ x: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .H, data: [x]), rest: self)
}
// GENERATED NOT
open func v(_ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .v, data: [y]), rest: self)
}
// GENERATED NOT
open func V(_ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .V, data: [y]), rest: self)
}
// GENERATED NOT
open func c(_ x1: Double, _ y1: Double, _ x2: Double, _ y2: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .c, data: [x1, y1, x2, y2, x, y]), rest: self)
}
// GENERATED NOT
open func C(_ x1: Double, _ y1: Double, _ x2: Double, _ y2: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .C, data: [x1, y1, x2, y2, x, y]), rest: self)
}
// GENERATED NOT
open func s(_ x2: Double, _ y2: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .s, data: [x2, y2, x, y]), rest: self)
}
// GENERATED NOT
open func S(_ x2: Double, _ y2: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .S, data: [x2, y2, x, y]), rest: self)
}
// GENERATED NOT
open func q(_ x1: Double, _ y1: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .q, data: [x1, y1, x, y]), rest: self)
}
// GENERATED NOT
open func Q(_ x1: Double, _ y1: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .Q, data: [x1, y1, x, y]), rest: self)
}
// GENERATED NOT
open func t(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .t, data: [x, y]), rest: self)
}
// GENERATED NOT
open func T(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .T, data: [x, y]), rest: self)
}
// GENERATED NOT
open func a(_ rx: Double, _ ry: Double, _ angle: Double, _ largeArc: Bool, _ sweep: Bool, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .a, data: [rx, ry, angle, boolsToNum(largeArc, sweep: sweep), x, y]), rest: self)
}
// GENERATED NOT
open func A(_ rx: Double, _ ry: Double, _ angle: Double, _ largeArc: Bool, _ sweep: Bool, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .A, data: [rx, ry, angle, boolsToNum(largeArc, sweep: sweep), x, y]), rest: self)
}
// GENERATED NOT
open func e(_ x: Double, _ y: Double, _ w: Double, _ h: Double, _ startAngle: Double, _ arcAngle: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .e, data: [x, y, w, h, startAngle, arcAngle]), rest: self)
}
open let segment: PathSegment
open let rest: PathBuilder?
// GENERATED NOT
open func E(_ x: Double, _ y: Double, _ w: Double, _ h: Double, _ startAngle: Double, _ arcAngle: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .E, data: [x, y, w, h, startAngle, arcAngle]), rest: self)
}
// GENERATED NOT
open func Z() -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .z), rest: self)
}
// GENERATED NOT
open func build() -> Path {
var segments : [PathSegment] = []
var builder : PathBuilder? = self
while(builder != nil) {
segments.append(builder!.segment)
builder = builder!.rest
public init(segment: PathSegment, rest: PathBuilder? = nil) {
self.segment = segment
self.rest = rest
}
// GENERATED NOT
open func moveTo(x: Double, y: Double) -> PathBuilder {
return M(x, y)
}
// GENERATED NOT
open func lineTo(x: Double, y: Double) -> PathBuilder {
return L(x, y)
}
// GENERATED NOT
open func cubicTo(x1: Double, y1: Double, x2: Double, y2: Double, x: Double, y: Double) -> PathBuilder {
return C(x1, y1, x2, y2, x, y)
}
// GENERATED NOT
open func quadraticTo(x1: Double, y1: Double, x: Double, y: Double) -> PathBuilder {
return Q(x1, y1, x, y)
}
// GENERATED NOT
open func arcTo(rx: Double, ry: Double, angle: Double, largeArc: Bool, sweep: Bool, x: Double, y: Double) -> PathBuilder {
return A(rx, ry, angle, largeArc, sweep, x, y)
}
// GENERATED NOT
open func close() -> PathBuilder {
return Z()
}
// GENERATED NOT
open func m(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .m, data: [x, y]), rest: self)
}
// GENERATED NOT
open func M(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .M, data: [x, y]), rest: self)
}
// GENERATED NOT
open func l(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .l, data: [x, y]), rest: self)
}
// GENERATED NOT
open func L(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .L, data: [x, y]), rest: self)
}
// GENERATED NOT
open func h(_ x: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .h, data: [x]), rest: self)
}
// GENERATED NOT
open func H(_ x: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .H, data: [x]), rest: self)
}
// GENERATED NOT
open func v(_ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .v, data: [y]), rest: self)
}
// GENERATED NOT
open func V(_ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .V, data: [y]), rest: self)
}
// GENERATED NOT
open func c(_ x1: Double, _ y1: Double, _ x2: Double, _ y2: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .c, data: [x1, y1, x2, y2, x, y]), rest: self)
}
// GENERATED NOT
open func C(_ x1: Double, _ y1: Double, _ x2: Double, _ y2: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .C, data: [x1, y1, x2, y2, x, y]), rest: self)
}
// GENERATED NOT
open func s(_ x2: Double, _ y2: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .s, data: [x2, y2, x, y]), rest: self)
}
// GENERATED NOT
open func S(_ x2: Double, _ y2: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .S, data: [x2, y2, x, y]), rest: self)
}
// GENERATED NOT
open func q(_ x1: Double, _ y1: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .q, data: [x1, y1, x, y]), rest: self)
}
// GENERATED NOT
open func Q(_ x1: Double, _ y1: Double, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .Q, data: [x1, y1, x, y]), rest: self)
}
// GENERATED NOT
open func t(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .t, data: [x, y]), rest: self)
}
// GENERATED NOT
open func T(_ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .T, data: [x, y]), rest: self)
}
// GENERATED NOT
open func a(_ rx: Double, _ ry: Double, _ angle: Double, _ largeArc: Bool, _ sweep: Bool, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .a, data: [rx, ry, angle, boolsToNum(largeArc, sweep: sweep), x, y]), rest: self)
}
// GENERATED NOT
open func A(_ rx: Double, _ ry: Double, _ angle: Double, _ largeArc: Bool, _ sweep: Bool, _ x: Double, _ y: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .A, data: [rx, ry, angle, boolsToNum(largeArc, sweep: sweep), x, y]), rest: self)
}
// GENERATED NOT
open func e(_ x: Double, _ y: Double, _ w: Double, _ h: Double, _ startAngle: Double, _ arcAngle: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .e, data: [x, y, w, h, startAngle, arcAngle]), rest: self)
}
// GENERATED NOT
open func E(_ x: Double, _ y: Double, _ w: Double, _ h: Double, _ startAngle: Double, _ arcAngle: Double) -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .E, data: [x, y, w, h, startAngle, arcAngle]), rest: self)
}
// GENERATED NOT
open func Z() -> PathBuilder {
return PathBuilder(segment: PathSegment(type: .z), rest: self)
}
// GENERATED NOT
open func build() -> Path {
var segments: [PathSegment] = []
var builder: PathBuilder? = self
while builder != nil {
segments.append(builder!.segment)
builder = builder!.rest
}
return Path(segments: segments.reversed())
}
// GENERATED NOT
fileprivate func boolsToNum(_ largeArc: Bool, sweep: Bool) -> Double {
return (largeArc ? 1 : 0) + (sweep ? 1 : 0) * 2
}
return Path(segments: segments.reversed())
}
// GENERATED NOT
fileprivate func boolsToNum(_ largeArc: Bool, sweep: Bool) -> Double {
return (largeArc ? 1 : 0) + (sweep ? 1 : 0) * 2;
}
}

View File

@ -1,22 +1,22 @@
import Foundation
open class PathSegment {
open let type: PathSegmentType
open let data: [Double]
public init(type: PathSegmentType = .M, data: [Double] = []) {
self.type = type
self.data = data
}
// GENERATED NOT
open func isAbsolute() -> Bool {
switch type {
case .M, .L, .H, .V, .C, .S, .Q, .T, .A, .E:
return true
default:
return false
open let type: PathSegmentType
open let data: [Double]
public init(type: PathSegmentType = .M, data: [Double] = []) {
self.type = type
self.data = data
}
// GENERATED NOT
open func isAbsolute() -> Bool {
switch type {
case .M, .L, .H, .V, .C, .S, .Q, .T, .A, .E:
return true
default:
return false
}
}
}
}

View File

@ -1,23 +1,23 @@
public enum PathSegmentType {
case M
case L
case C
case Q
case A
case z
case H
case V
case S
case T
case m
case l
case c
case q
case a
case h
case v
case s
case t
case E
case e
case M
case L
case C
case Q
case A
case z
case H
case V
case S
case T
case m
case l
case c
case q
case a
case h
case v
case s
case t
case E
case e
}

View File

@ -1,29 +1,29 @@
import Foundation
open class Point: Locus {
open let x: Double
open let y: Double
open static let origin: Point = Point( x: 0, y: 0 )
public init(x: Double = 0, y: Double = 0) {
self.x = x
self.y = y
}
override open func bounds() -> Rect {
return Rect(
x: x,
y: y,
w: 0.0,
h: 0.0)
}
// GENERATED NOT
open func add(_ point: Point) -> Point {
return Point(
x: self.x + point.x,
y: self.y + point.y)
}
open let x: Double
open let y: Double
open static let origin: Point = Point( x: 0, y: 0 )
public init(x: Double = 0, y: Double = 0) {
self.x = x
self.y = y
}
override open func bounds() -> Rect {
return Rect(
x: x,
y: y,
w: 0.0,
h: 0.0)
}
// GENERATED NOT
open func add(_ point: Point) -> Point {
return Point(
x: self.x + point.x,
y: self.y + point.y)
}
}

View File

@ -1,46 +1,48 @@
import Foundation
open class Polygon: Locus {
open let points: [Double]
public init(points: [Double] = []) {
self.points = points
}
override open func bounds() -> Rect {
guard !points.isEmpty else { return Rect.zero() }
var minX = Double(INT16_MAX)
var minY = Double(INT16_MAX)
var maxX = Double(INT16_MIN)
var maxY = Double(INT16_MIN)
var isX = true
for point in points {
if isX {
if minX > point {
minX = point
}
if maxX < point {
maxX = point
}
} else {
if minY > point {
minY = point
}
if maxY < point {
maxY = point
}
}
isX = !isX
open let points: [Double]
public init(points: [Double] = []) {
self.points = points
}
override open func bounds() -> Rect {
guard !points.isEmpty else {
return Rect.zero()
}
var minX = Double(INT16_MAX)
var minY = Double(INT16_MAX)
var maxX = Double(INT16_MIN)
var maxY = Double(INT16_MIN)
var isX = true
for point in points {
if isX {
if minX > point {
minX = point
}
if maxX < point {
maxX = point
}
} else {
if minY > point {
minY = point
}
if maxY < point {
maxY = point
}
}
isX = !isX
}
return Rect(x: minX, y: minY,
w: maxX - minX,
h: maxY - minY)
}
return Rect(x: minX, y: minY,
w: maxX - minX,
h: maxY - minY)
}
}

View File

@ -1,46 +1,48 @@
import Foundation
open class Polyline: Locus {
open let points: [Double]
public init(points: [Double] = []) {
self.points = points
}
override open func bounds() -> Rect {
guard !points.isEmpty else { return Rect.zero() }
var minX = Double(INT16_MAX)
var minY = Double(INT16_MAX)
var maxX = Double(INT16_MIN)
var maxY = Double(INT16_MIN)
var isX = true
for point in points {
if isX {
if minX > point {
minX = point
}
if maxX < point {
maxX = point
}
} else {
if minY > point {
minY = point
}
if maxY < point {
maxY = point
}
}
isX = !isX
open let points: [Double]
public init(points: [Double] = []) {
self.points = points
}
override open func bounds() -> Rect {
guard !points.isEmpty else {
return Rect.zero()
}
var minX = Double(INT16_MAX)
var minY = Double(INT16_MAX)
var maxX = Double(INT16_MIN)
var maxY = Double(INT16_MIN)
var isX = true
for point in points {
if isX {
if minX > point {
minX = point
}
if maxX < point {
maxX = point
}
} else {
if minY > point {
minY = point
}
if maxY < point {
maxY = point
}
}
isX = !isX
}
return Rect(x: minX, y: minY,
w: maxX - minX,
h: maxY - minY)
}
return Rect(x: minX, y: minY,
w: maxX - minX,
h: maxY - minY)
}
}

View File

@ -1,68 +1,68 @@
import Foundation
open class Rect: Locus {
open let x: Double
open let y: Double
open let w: Double
open let h: Double
public init(x: Double = 0, y: Double = 0, w: Double = 0, h: Double = 0) {
self.x = x
self.y = y
self.w = w
self.h = h
}
override open func bounds() -> Rect {
return self
}
// GENERATED NOT
open func round(rx: Double, ry: Double) -> RoundRect {
return RoundRect(rect: self, rx: rx, ry: ry)
}
// GENERATED NOT
public func round(r: Double) -> RoundRect {
return RoundRect(rect: self, rx: r, ry: r)
}
// GENERATED NOT
open func contains(locus: Locus) -> Bool {
return false
}
// GENERATED NOT
class func zero() -> Rect {
return Rect(x: 0.0, y: 0.0, w: 0.0, h: 0.0)
}
// GENERATED NOT
open func move(offset: Point) -> Rect {
return Rect(
x: self.x + offset.x,
y: self.y + offset.y,
w: self.w,
h: self.h)
}
// GENERATED NOT
open func union(rect: Rect) -> Rect {
return Rect(
x: min(self.x, rect.x),
y: min(self.y, rect.y),
w: max(self.x + self.w, rect.x + rect.w) - min(self.x, rect.x),
h: max(self.y + self.h, rect.y + rect.h) - min(self.y, rect.y))
}
open let x: Double
open let y: Double
open let w: Double
open let h: Double
public init(x: Double = 0, y: Double = 0, w: Double = 0, h: Double = 0) {
self.x = x
self.y = y
self.w = w
self.h = h
}
override open func bounds() -> Rect {
return self
}
// GENERATED NOT
open func round(rx: Double, ry: Double) -> RoundRect {
return RoundRect(rect: self, rx: rx, ry: ry)
}
// GENERATED NOT
public func round(r: Double) -> RoundRect {
return RoundRect(rect: self, rx: r, ry: r)
}
// GENERATED NOT
open func contains(locus: Locus) -> Bool {
return false
}
// GENERATED NOT
class func zero() -> Rect {
return Rect(x: 0.0, y: 0.0, w: 0.0, h: 0.0)
}
// GENERATED NOT
open func move(offset: Point) -> Rect {
return Rect(
x: self.x + offset.x,
y: self.y + offset.y,
w: self.w,
h: self.h)
}
// GENERATED NOT
open func union(rect: Rect) -> Rect {
return Rect(
x: min(self.x, rect.x),
y: min(self.y, rect.y),
w: max(self.x + self.w, rect.x + rect.w) - min(self.x, rect.x),
h: max(self.y + self.h, rect.y + rect.h) - min(self.y, rect.y))
}
}
extension Rect {
public static func ==(lhs: Rect, rhs: Rect) -> Bool {
return lhs.x == rhs.x
&& lhs.y == rhs.y
&& lhs.w == rhs.w
&& lhs.h == rhs.h
}
public static func == (lhs: Rect, rhs: Rect) -> Bool {
return lhs.x == rhs.x
&& lhs.y == rhs.y
&& lhs.w == rhs.w
&& lhs.h == rhs.h
}
}

View File

@ -1,18 +1,18 @@
import Foundation
open class RoundRect: Locus {
open let rect: Rect
open let rx: Double
open let ry: Double
public init(rect: Rect, rx: Double = 0, ry: Double = 0) {
self.rect = rect
self.rx = rx
self.ry = ry
}
override open func bounds() -> Rect {
return rect
}
open let rect: Rect
open let rx: Double
open let ry: Double
public init(rect: Rect, rx: Double = 0, ry: Double = 0) {
self.rect = rect
self.rx = rx
self.ry = ry
}
override open func bounds() -> Rect {
return rect
}
}

View File

@ -1,12 +1,12 @@
import Foundation
open class Size {
open let w: Double
open let h: Double
public init(w: Double = 0, h: Double = 0) {
self.w = w
self.h = h
}
open let w: Double
open let h: Double
public init(w: Double = 0, h: Double = 0) {
self.w = w
self.h = h
}
}

View File

@ -1,88 +1,88 @@
import Foundation
public final class Transform {
public let m11: Double
public let m12: Double
public let m21: Double
public let m22: Double
public let dx: Double
public let dy: Double
public static let identity: Transform = Transform()
public init(m11: Double = 1, m12: Double = 0, m21: Double = 0, m22: Double = 1, dx: Double = 0, dy: Double = 0) {
self.m11 = m11
self.m12 = m12
self.m21 = m21
self.m22 = m22
self.dx = dx
self.dy = dy
}
// GENERATED NOT
public func move(dx: Double, dy: Double) -> Transform {
return Transform(m11: m11, m12: m12, m21: m21, m22: m22,
dx: dx * m11 + dy * m21 + self.dx, dy: dx * m12 + dy * m22 + self.dy)
}
// GENERATED NOT
public func scale(sx: Double, sy: Double) -> Transform {
return Transform(m11: m11 * sx, m12: m12 * sx, m21: m21 * sy, m22: m22 * sy, dx: dx, dy: dy)
}
// GENERATED NOT
public func shear(shx: Double, shy: Double) -> Transform {
return Transform(m11: m11 + m21 * shy, m12: m12 + m22 * shy,
m21: m11 * shx + m21, m22: m12 * shx + m22, dx: dx, dy: dy)
}
// GENERATED NOT
public func rotate(angle: Double) -> Transform {
let asin = sin(angle); let acos = cos(angle)
return Transform(m11: acos * m11 + asin * m21, m12: acos * m12 + asin * m22,
m21: -asin * m11 + acos * m21, m22: -asin * m12 + acos * m22, dx: dx, dy: dy)
}
// GENERATED NOT
public func rotate(angle: Double, x: Double, y: Double) -> Transform {
return move(dx: x, dy: y).rotate(angle: angle).move(dx: -x, dy: -y)
}
// GENERATED
public class func move(dx: Double, dy: Double) -> Transform {
return Transform(dx: dx, dy: dy)
}
// GENERATED
public class func scale(sx: Double, sy: Double) -> Transform {
return Transform(m11: sx, m22: sy)
}
// GENERATED
public class func shear(shx: Double, shy: Double) -> Transform {
return Transform(m12: shy, m21: shx)
}
// GENERATED NOT
public class func rotate(angle: Double) -> Transform {
let asin = sin(angle); let acos = cos(angle)
return Transform(m11: acos, m12: asin, m21: -asin, m22: acos)
}
// GENERATED NOT
public class func rotate(angle: Double, x: Double, y: Double) -> Transform {
return Transform.move(dx: x, dy: y).rotate(angle: angle).move(dx: -x, dy: -y)
}
// GENERATED NOT
public func invert() -> Transform? {
let det = self.m11 * self.m22 - self.m12 * self.m21
if (det == 0) {
return nil
public let m11: Double
public let m12: Double
public let m21: Double
public let m22: Double
public let dx: Double
public let dy: Double
public static let identity: Transform = Transform()
public init(m11: Double = 1, m12: Double = 0, m21: Double = 0, m22: Double = 1, dx: Double = 0, dy: Double = 0) {
self.m11 = m11
self.m12 = m12
self.m21 = m21
self.m22 = m22
self.dx = dx
self.dy = dy
}
// GENERATED NOT
public func move(dx: Double, dy: Double) -> Transform {
return Transform(m11: m11, m12: m12, m21: m21, m22: m22,
dx: dx * m11 + dy * m21 + self.dx, dy: dx * m12 + dy * m22 + self.dy)
}
// GENERATED NOT
public func scale(sx: Double, sy: Double) -> Transform {
return Transform(m11: m11 * sx, m12: m12 * sx, m21: m21 * sy, m22: m22 * sy, dx: dx, dy: dy)
}
// GENERATED NOT
public func shear(shx: Double, shy: Double) -> Transform {
return Transform(m11: m11 + m21 * shy, m12: m12 + m22 * shy,
m21: m11 * shx + m21, m22: m12 * shx + m22, dx: dx, dy: dy)
}
// GENERATED NOT
public func rotate(angle: Double) -> Transform {
let asin = sin(angle); let acos = cos(angle)
return Transform(m11: acos * m11 + asin * m21, m12: acos * m12 + asin * m22,
m21: -asin * m11 + acos * m21, m22: -asin * m12 + acos * m22, dx: dx, dy: dy)
}
// GENERATED NOT
public func rotate(angle: Double, x: Double, y: Double) -> Transform {
return move(dx: x, dy: y).rotate(angle: angle).move(dx: -x, dy: -y)
}
// GENERATED
public class func move(dx: Double, dy: Double) -> Transform {
return Transform(dx: dx, dy: dy)
}
// GENERATED
public class func scale(sx: Double, sy: Double) -> Transform {
return Transform(m11: sx, m22: sy)
}
// GENERATED
public class func shear(shx: Double, shy: Double) -> Transform {
return Transform(m12: shy, m21: shx)
}
// GENERATED NOT
public class func rotate(angle: Double) -> Transform {
let asin = sin(angle); let acos = cos(angle)
return Transform(m11: acos, m12: asin, m21: -asin, m22: acos)
}
// GENERATED NOT
public class func rotate(angle: Double, x: Double, y: Double) -> Transform {
return Transform.move(dx: x, dy: y).rotate(angle: angle).move(dx: -x, dy: -y)
}
// GENERATED NOT
public func invert() -> Transform? {
let det = self.m11 * self.m22 - self.m12 * self.m21
if det == 0 {
return nil
}
return Transform(m11: m22 / det, m12: -m12 / det, m21: -m21 / det, m22: m11 / det,
dx: (m21 * dy - m22 * dx) / det,
dy: (m12 * dx - m11 * dy) / det)
}
return Transform(m11: m22 / det, m12: -m12 / det, m21: -m21 / det, m22: m11 / det,
dx: (m21 * dy - m22 * dx) / det,
dy: (m12 * dx - m11 * dy) / det)
}
}

View File

@ -1,151 +1,150 @@
import Foundation
open class Group: Node {
open var contentsVar: AnimatableVariable<[Node]>
open var contents: [Node] {
get { return contentsVar.value }
set(val) {
contentsVar.value = val
if let view = nodesMap.getView(self) {
val.forEach { subNode in
nodesMap.add(subNode, view: view)
open var contentsVar: AnimatableVariable<[Node]>
open var contents: [Node] {
get { return contentsVar.value }
set(val) {
contentsVar.value = val
if let view = nodesMap.getView(self) {
val.forEach { subNode in
nodesMap.add(subNode, view: view)
}
}
val.forEach { subNode in
nodesMap.add(subNode, parent: self)
}
}
}
val.forEach { subNode in
nodesMap.add(subNode, parent: self)
}
}
}
public init(contents: [Node] = [], place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.contentsVar = AnimatableVariable<[Node]>(contents)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
self.contentsVar.node = self
}
// Searching
override public func nodeBy(tag: String) -> Node? {
if let node = super.nodeBy(tag: tag) {
return node
public init(contents: [Node] = [], place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.contentsVar = AnimatableVariable<[Node]>(contents)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
self.contentsVar.node = self
}
for child in contents {
if let node = child.nodeBy(tag: tag) {
return node
}
// Searching
override public func nodeBy(tag: String) -> Node? {
if let node = super.nodeBy(tag: tag) {
return node
}
for child in contents {
if let node = child.nodeBy(tag: tag) {
return node
}
}
return .none
}
return .none
}
override public func nodesBy(tag: String) -> [Node] {
var result = [Node]()
contents.forEach { child in
result.append(contentsOf: child.nodesBy(tag: tag))
override public func nodesBy(tag: String) -> [Node] {
var result = [Node]()
contents.forEach { child in
result.append(contentsOf: child.nodesBy(tag: tag))
}
if let node = super.nodeBy(tag: tag) {
result.append(node)
}
return result
}
if let node = super.nodeBy(tag: tag) {
result.append(node)
// GENERATED NOT
override internal func bounds() -> Rect? {
var union: Rect?
contents.forEach { node in
guard let nodeBounds = node.bounds()?.applyTransform(node.place) else {
return
}
union = union?.union(rect: nodeBounds) ?? nodeBounds
}
return union
}
return result
}
// GENERATED NOT
override internal func bounds() -> Rect? {
var union: Rect?
contents.forEach { node in
guard let nodeBounds = node.bounds()?.applyTransform(node.place) else {
return
}
union = union?.union(rect: nodeBounds) ?? nodeBounds
override func shouldCheckForPressed() -> Bool {
var shouldCheck = super.shouldCheckForPressed()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForPressed()
}
return shouldCheck
}
return union
}
override func shouldCheckForPressed() -> Bool {
var shouldCheck = super.shouldCheckForPressed()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForPressed()
override func shouldCheckForMoved() -> Bool {
var shouldCheck = super.shouldCheckForMoved()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForMoved()
}
return shouldCheck
}
return shouldCheck
}
override func shouldCheckForMoved() -> Bool {
var shouldCheck = super.shouldCheckForMoved()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForMoved()
override func shouldCheckForReleased() -> Bool {
var shouldCheck = super.shouldCheckForReleased()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForReleased()
}
return shouldCheck
}
return shouldCheck
}
override func shouldCheckForReleased() -> Bool {
var shouldCheck = super.shouldCheckForReleased()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForReleased()
override func shouldCheckForTap() -> Bool {
var shouldCheck = super.shouldCheckForTap()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForTap()
}
return shouldCheck
}
return shouldCheck
}
override func shouldCheckForTap() -> Bool {
var shouldCheck = super.shouldCheckForTap()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForTap()
override func shouldCheckForPan() -> Bool {
var shouldCheck = super.shouldCheckForPan()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForPan()
}
return shouldCheck
}
return shouldCheck
}
override func shouldCheckForPan() -> Bool {
var shouldCheck = super.shouldCheckForPan()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForPan()
override func shouldCheckForPinch() -> Bool {
var shouldCheck = super.shouldCheckForPinch()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForPinch()
}
return shouldCheck
}
return shouldCheck
}
override func shouldCheckForPinch() -> Bool {
var shouldCheck = super.shouldCheckForPinch()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForPinch()
override func shouldCheckForRotate() -> Bool {
var shouldCheck = super.shouldCheckForRotate()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForRotate()
}
return shouldCheck
}
return shouldCheck
}
override func shouldCheckForRotate() -> Bool {
var shouldCheck = super.shouldCheckForRotate()
contents.forEach { node in
shouldCheck = shouldCheck || node.shouldCheckForRotate()
}
return shouldCheck
}
}
public extension Array where Element: Node {
public func group( place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) -> Group {
return Group(contents: self, place: place, opaque: opaque, opacity: opacity, clip: clip, effect: effect, visible: visible, tag: tag)
}
public func group( place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) -> Group {
return Group(contents: self, place: place, opaque: opaque, opacity: opacity, clip: clip, effect: effect, visible: visible, tag: tag)
}
}

View File

@ -5,166 +5,164 @@ import Foundation
#endif
#if os(iOS)
import UIKit
import UIKit
#endif
open class Image: Node {
open let srcVar: Variable<String>
open var src: String {
get { return srcVar.value }
set(val) { srcVar.value = val }
}
open let xAlignVar: Variable<Align>
open var xAlign: Align {
get { return xAlignVar.value }
set(val) { xAlignVar.value = val }
}
open let yAlignVar: Variable<Align>
open var yAlign: Align {
get { return yAlignVar.value }
set(val) { yAlignVar.value = val }
}
open let aspectRatioVar: Variable<AspectRatio>
open var aspectRatio: AspectRatio {
get { return aspectRatioVar.value }
set(val) { aspectRatioVar.value = val }
}
open let wVar: Variable<Int>
open var w: Int {
get { return wVar.value }
set(val) { wVar.value = val }
}
open let hVar: Variable<Int>
open var h: Int {
get { return hVar.value }
set(val) { hVar.value = val }
}
private var mImage: MImage?
public init(src: String, xAlign: Align = .min, yAlign: Align = .min, aspectRatio: AspectRatio = .none, w: Int = 0, h: Int = 0, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.srcVar = Variable<String>(src)
self.xAlignVar = Variable<Align>(xAlign)
self.yAlignVar = Variable<Align>(yAlign)
self.aspectRatioVar = Variable<AspectRatio>(aspectRatio)
self.wVar = Variable<Int>(w)
self.hVar = Variable<Int>(h)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
}
public init(image: MImage, xAlign: Align = .min, yAlign: Align = .min, aspectRatio: AspectRatio = .none, w: Int = 0, h: Int = 0, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
var oldId: String?
for key in imagesMap.keys {
if image === imagesMap[key] {
oldId = key
}
}
let id = oldId ?? UUID().uuidString
imagesMap[id] = image
self.srcVar = Variable<String>("memory://\(id)")
self.xAlignVar = Variable<Align>(xAlign)
self.yAlignVar = Variable<Align>(yAlign)
self.aspectRatioVar = Variable<AspectRatio>(aspectRatio)
self.wVar = Variable<Int>(w)
self.hVar = Variable<Int>(h)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
}
override func bounds() -> Rect? {
if w != 0 && h != 0 {
return Rect(x: 0.0, y: 0.0, w: Double(w), h: Double(h))
}
mImage = image()
guard let mImage = mImage else {
return .none
}
return Rect(x: 0.0, y: 0.0,
w: Double(mImage.size.width),
h: Double(mImage.size.height))
}
internal enum ImageRepresentationType {
case JPEG
case PNG
}
internal func base64encoded(type:ImageRepresentationType) -> String? {
if let image = self.image() {
switch (type) {
case .JPEG:
if let data = MImageJPEGRepresentation(image) {
return data.base64EncodedString()
}
case .PNG:
if let data = MImagePNGRepresentation(image) {
return data.base64EncodedString()
}
}
open let srcVar: Variable<String>
open var src: String {
get { return srcVar.value }
set(val) { srcVar.value = val }
}
return .none
}
func image() -> MImage? {
// image already loaded
if let _ = mImage {
return mImage
open let xAlignVar: Variable<Align>
open var xAlign: Align {
get { return xAlignVar.value }
set(val) { xAlignVar.value = val }
}
// In-memory image
if src.contains("memory") {
let id = src.replacingOccurrences(of: "memory://", with: "")
return imagesMap[id]
}
// Base64 image
let decodableFormat = ["image/png", "image/jpg"]
for format in decodableFormat {
let prefix = "data:\(format);base64,"
if src.hasPrefix(prefix) {
let src = String(self.src.suffix(from: prefix.endIndex))
guard let decodedData = Data(base64Encoded: src, options: .ignoreUnknownCharacters) else {
return .none
}
return MImage(data: decodedData)
open let yAlignVar: Variable<Align>
open var yAlign: Align {
get { return yAlignVar.value }
set(val) { yAlignVar.value = val }
}
open let aspectRatioVar: Variable<AspectRatio>
open var aspectRatio: AspectRatio {
get { return aspectRatioVar.value }
set(val) { aspectRatioVar.value = val }
}
open let wVar: Variable<Int>
open var w: Int {
get { return wVar.value }
set(val) { wVar.value = val }
}
open let hVar: Variable<Int>
open var h: Int {
get { return hVar.value }
set(val) { hVar.value = val }
}
private var mImage: MImage?
public init(src: String, xAlign: Align = .min, yAlign: Align = .min, aspectRatio: AspectRatio = .none, w: Int = 0, h: Int = 0, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.srcVar = Variable<String>(src)
self.xAlignVar = Variable<Align>(xAlign)
self.yAlignVar = Variable<Align>(yAlign)
self.aspectRatioVar = Variable<AspectRatio>(aspectRatio)
self.wVar = Variable<Int>(w)
self.hVar = Variable<Int>(h)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
}
public init(image: MImage, xAlign: Align = .min, yAlign: Align = .min, aspectRatio: AspectRatio = .none, w: Int = 0, h: Int = 0, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
var oldId: String?
for key in imagesMap.keys where image === imagesMap[key] {
oldId = key
}
let id = oldId ?? UUID().uuidString
imagesMap[id] = image
self.srcVar = Variable<String>("memory://\(id)")
self.xAlignVar = Variable<Align>(xAlign)
self.yAlignVar = Variable<Align>(yAlign)
self.aspectRatioVar = Variable<AspectRatio>(aspectRatio)
self.wVar = Variable<Int>(w)
self.hVar = Variable<Int>(h)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
}
override func bounds() -> Rect? {
if w != 0 && h != 0 {
return Rect(x: 0.0, y: 0.0, w: Double(w), h: Double(h))
}
mImage = image()
guard let mImage = mImage else {
return .none
}
return Rect(x: 0.0, y: 0.0,
w: Double(mImage.size.width),
h: Double(mImage.size.height))
}
internal enum ImageRepresentationType {
case JPEG
case PNG
}
internal func base64encoded(type: ImageRepresentationType) -> String? {
if let image = self.image() {
switch type {
case .JPEG:
if let data = MImageJPEGRepresentation(image) {
return data.base64EncodedString()
}
case .PNG:
if let data = MImagePNGRepresentation(image) {
return data.base64EncodedString()
}
}
}
return .none
}
func image() -> MImage? {
// image already loaded
if let _ = mImage {
return mImage
}
// In-memory image
if src.contains("memory") {
let id = src.replacingOccurrences(of: "memory://", with: "")
return imagesMap[id]
}
// Base64 image
let decodableFormat = ["image/png", "image/jpg"]
for format in decodableFormat {
let prefix = "data:\(format);base64,"
if src.hasPrefix(prefix) {
let src = String(self.src.suffix(from: prefix.endIndex))
guard let decodedData = Data(base64Encoded: src, options: .ignoreUnknownCharacters) else {
return .none
}
return MImage(data: decodedData)
}
}
// General case
#if os(iOS)
return MImage(named: src)
#elseif os(OSX)
return MImage(named: NSImage.Name(rawValue: src))
#endif
}
// General case
#if os(iOS)
return MImage(named: src)
#elseif os(OSX)
return MImage(named: NSImage.Name(rawValue: src))
#endif
}
}

View File

@ -2,172 +2,171 @@ import Foundation
open class Node: Drawable {
open let placeVar: AnimatableVariable<Transform>
open var place: Transform {
get { return placeVar.value }
set(val) { placeVar.value = val }
}
open let placeVar: AnimatableVariable<Transform>
open var place: Transform {
get { return placeVar.value }
set(val) { placeVar.value = val }
}
open let opaqueVar: Variable<Bool>
open var opaque: Bool {
get { return opaqueVar.value }
set(val) { opaqueVar.value = val }
}
open let opaqueVar: Variable<Bool>
open var opaque: Bool {
get { return opaqueVar.value }
set(val) { opaqueVar.value = val }
}
open let opacityVar: AnimatableVariable<Double>
open var opacity: Double {
get { return opacityVar.value }
set(val) { opacityVar.value = val }
}
open let opacityVar: AnimatableVariable<Double>
open var opacity: Double {
get { return opacityVar.value }
set(val) { opacityVar.value = val }
}
open let clipVar: Variable<Locus?>
open var clip: Locus? {
get { return clipVar.value }
set(val) { clipVar.value = val }
}
open let clipVar: Variable<Locus?>
open var clip: Locus? {
get { return clipVar.value }
set(val) { clipVar.value = val }
}
open let effectVar: Variable<Effect?>
open var effect: Effect? {
get { return effectVar.value }
set(val) { effectVar.value = val }
}
open let effectVar: Variable<Effect?>
open var effect: Effect? {
get { return effectVar.value }
set(val) { effectVar.value = val }
}
internal var id: String {
didSet {
Node.map.removeObject(forKey: id as NSString)
Node.map.setObject(self, forKey: id as NSString)
}
}
// MARK: - ID map
private static let map = NSMapTable<NSString, Node>(keyOptions: NSMapTableStrongMemory, valueOptions: NSMapTableWeakMemory)
open static func nodeBy(id: String) -> Node? {
return Node.map.object(forKey: id as NSString)
}
// MARK: - Searching
public func nodeBy(tag: String) -> Node? {
if self.tag.contains(tag) {
return self
}
return .none
}
public func nodesBy(tag: String) -> [Node] {
return [nodeBy(tag: tag)].flatMap { $0 }
}
// MARK: - Events
var touchPressedHandlers = [ChangeHandler<TouchEvent>]()
var touchMovedHandlers = [ChangeHandler<TouchEvent>]()
var touchReleasedHandlers = [ChangeHandler<TouchEvent>]()
var tapHandlers = [ChangeHandler<TapEvent>]()
var panHandlers = [ChangeHandler<PanEvent>]()
var rotateHandlers = [ChangeHandler<RotateEvent>]()
var pinchHandlers = [ChangeHandler<PinchEvent>]()
@discardableResult public func onTouchPressed (_ f: @escaping (TouchEvent) -> ()) -> Disposable {
@discardableResult public func onTouchPressed (_ f: @escaping (TouchEvent) -> Void) -> Disposable {
let handler = ChangeHandler<TouchEvent>(f)
touchPressedHandlers.append(handler)
return Disposable({ [weak self] in
return Disposable { [weak self] in
guard let index = self?.touchPressedHandlers.index(of: handler) else {
return
}
self?.touchPressedHandlers.remove(at: index)
})
}
}
@discardableResult public func onTouchMoved (_ f: @escaping (TouchEvent) -> ()) -> Disposable {
@discardableResult public func onTouchMoved (_ f: @escaping (TouchEvent) -> Void) -> Disposable {
let handler = ChangeHandler<TouchEvent>(f)
touchMovedHandlers.append(handler)
return Disposable({ [weak self] in
return Disposable { [weak self] in
guard let index = self?.touchMovedHandlers.index(of: handler) else {
return
}
self?.touchMovedHandlers.remove(at: index)
})
}
}
@discardableResult public func onTouchReleased(_ f: @escaping (TouchEvent) -> ()) -> Disposable {
@discardableResult public func onTouchReleased(_ f: @escaping (TouchEvent) -> Void) -> Disposable {
let handler = ChangeHandler<TouchEvent>(f)
touchReleasedHandlers.append(handler)
return Disposable({ [weak self] in
return Disposable { [weak self] in
guard let index = self?.touchReleasedHandlers.index(of: handler) else {
return
}
self?.touchReleasedHandlers.remove(at: index)
})
}
}
@discardableResult public func onTap(_ f: @escaping (TapEvent) -> ()) -> Disposable {
@discardableResult public func onTap(_ f: @escaping (TapEvent) -> Void) -> Disposable {
let handler = ChangeHandler<TapEvent>(f)
tapHandlers.append(handler)
return Disposable({ [weak self] in
return Disposable { [weak self] in
guard let index = self?.tapHandlers.index(of: handler) else {
return
}
self?.tapHandlers.remove(at: index)
})
}
}
@discardableResult public func onPan(_ f: @escaping (PanEvent) -> ()) -> Disposable {
@discardableResult public func onPan(_ f: @escaping (PanEvent) -> Void) -> Disposable {
let handler = ChangeHandler<PanEvent>(f)
panHandlers.append(handler)
return Disposable({ [weak self] in
return Disposable { [weak self] in
guard let index = self?.panHandlers.index(of: handler) else {
return
}
self?.panHandlers.remove(at: index)
})
}
}
@discardableResult public func onRotate(_ f: @escaping (RotateEvent) -> ()) -> Disposable {
@discardableResult public func onRotate(_ f: @escaping (RotateEvent) -> Void) -> Disposable {
let handler = ChangeHandler<RotateEvent>(f)
rotateHandlers.append(handler)
return Disposable({ [weak self] in
return Disposable { [weak self] in
guard let index = self?.rotateHandlers.index(of: handler) else {
return
}
self?.rotateHandlers.remove(at: index)
})
}
}
@discardableResult public func onPinch(_ f: @escaping (PinchEvent) -> ()) -> Disposable {
@discardableResult public func onPinch(_ f: @escaping (PinchEvent) -> Void) -> Disposable {
let handler = ChangeHandler<PinchEvent>(f)
pinchHandlers.append(handler)
return Disposable({ [weak self] in
return Disposable { [weak self] in
guard let index = self?.pinchHandlers.index(of: handler) else {
return
}
self?.pinchHandlers.remove(at: index)
})
}
}
// Helpers
func handleTouchPressed(_ event: TouchEvent) {
touchPressedHandlers.forEach { handler in handler.handle(event) }
}
func handleTouchReleased(_ event: TouchEvent) {
touchReleasedHandlers.forEach { handler in handler.handle(event) }
}
@ -175,75 +174,72 @@ open class Node: Drawable {
func handleTouchMoved(_ event: TouchEvent) {
touchMovedHandlers.forEach { handler in handler.handle(event) }
}
func handleTap( _ event: TapEvent ) {
tapHandlers.forEach { handler in handler.handle(event) }
}
func handlePan( _ event: PanEvent ) {
panHandlers.forEach { handler in handler.handle(event) }
}
func handleRotate( _ event: RotateEvent ) {
rotateHandlers.forEach { handler in handler.handle(event) }
}
func handlePinch( _ event: PinchEvent ) {
pinchHandlers.forEach { handler in handler.handle(event) }
}
func shouldCheckForPressed() -> Bool {
return touchPressedHandlers.count > 0
}
func shouldCheckForMoved() -> Bool {
return touchMovedHandlers.count > 0
return !touchPressedHandlers.isEmpty
}
func shouldCheckForMoved() -> Bool {
return !touchMovedHandlers.isEmpty
}
func shouldCheckForReleased() -> Bool {
return touchReleasedHandlers.count > 0
return !touchReleasedHandlers.isEmpty
}
func shouldCheckForTap() -> Bool {
return tapHandlers.count > 0
return !tapHandlers.isEmpty
}
func shouldCheckForPan() -> Bool {
return panHandlers.count > 0
return !panHandlers.isEmpty
}
func shouldCheckForRotate() -> Bool {
return rotateHandlers.count > 0
return !rotateHandlers.isEmpty
}
func shouldCheckForPinch() -> Bool {
return pinchHandlers.count > 0
return !pinchHandlers.isEmpty
}
public init(place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.placeVar = AnimatableVariable<Transform>(place)
self.opaqueVar = Variable<Bool>(opaque)
self.opacityVar = AnimatableVariable<Double>(opacity)
self.clipVar = Variable<Locus?>(clip)
self.effectVar = Variable<Effect?>(effect)
public init(place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.placeVar = AnimatableVariable<Transform>(place)
self.opaqueVar = Variable<Bool>(opaque)
self.opacityVar = AnimatableVariable<Double>(opacity)
self.clipVar = Variable<Locus?>(clip)
self.effectVar = Variable<Effect?>(effect)
self.id = NSUUID().uuidString
super.init(
visible: visible,
tag: tag
)
self.placeVar.node = self
self.opacityVar.node = self
Node.map.setObject(self, forKey: self.id as NSString)
}
// GENERATED NOT
internal func bounds() -> Rect? {
return Rect()
}
super.init(
visible: visible,
tag: tag
)
self.placeVar.node = self
self.opacityVar.node = self
Node.map.setObject(self, forKey: self.id as NSString)
}
// GENERATED NOT
internal func bounds() -> Rect? {
return Rect()
}
}

View File

@ -7,16 +7,16 @@
//
class SceneUtils {
static func shapeCopy(from: Shape) -> Shape {
return Shape(form: from.form,
fill: from.fill,
stroke: from.stroke,
place: from.place,
opaque: from.opaque,
opacity: from.opacity,
clip: from.clip,
effect: from.effect,
visible: from.visible,
tag: from.tag)
}
static func shapeCopy(from: Shape) -> Shape {
return Shape(form: from.form,
fill: from.fill,
stroke: from.stroke,
place: from.place,
opaque: from.opaque,
opacity: from.opacity,
clip: from.clip,
effect: from.effect,
visible: from.visible,
tag: from.tag)
}
}

View File

@ -2,56 +2,56 @@ import Foundation
open class Shape: Node {
open let formVar: AnimatableVariable<Locus>
open var form: Locus {
get { return formVar.value }
set(val) { formVar.value = val }
}
open let fillVar: AnimatableVariable<Fill?>
open var fill: Fill? {
get { return fillVar.value }
set(val) { fillVar.value = val }
}
open let strokeVar: AnimatableVariable<Stroke?>
open var stroke: Stroke? {
get { return strokeVar.value }
set(val) { strokeVar.value = val }
}
public init(form: Locus, fill: Fill? = nil, stroke: Stroke? = nil, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.formVar = AnimatableVariable<Locus>(form)
self.fillVar = AnimatableVariable<Fill?>(fill)
self.strokeVar = AnimatableVariable<Stroke?>(stroke)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
self.formVar.node = self
self.strokeVar.node = self
self.fillVar.node = self
}
// GENERATED NOT
override internal func bounds() -> Rect? {
var bounds = form.bounds()
if let shapeStroke = self.stroke {
let r = shapeStroke.width / 2.0
bounds = Rect(
x: bounds.x - r,
y: bounds.y - r,
w: bounds.w + r * 2.0,
h: bounds.h + r * 2.0)
open let formVar: AnimatableVariable<Locus>
open var form: Locus {
get { return formVar.value }
set(val) { formVar.value = val }
}
open let fillVar: AnimatableVariable<Fill?>
open var fill: Fill? {
get { return fillVar.value }
set(val) { fillVar.value = val }
}
open let strokeVar: AnimatableVariable<Stroke?>
open var stroke: Stroke? {
get { return strokeVar.value }
set(val) { strokeVar.value = val }
}
public init(form: Locus, fill: Fill? = nil, stroke: Stroke? = nil, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.formVar = AnimatableVariable<Locus>(form)
self.fillVar = AnimatableVariable<Fill?>(fill)
self.strokeVar = AnimatableVariable<Stroke?>(stroke)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
self.formVar.node = self
self.strokeVar.node = self
self.fillVar.node = self
}
// GENERATED NOT
override internal func bounds() -> Rect? {
var bounds = form.bounds()
if let shapeStroke = self.stroke {
let r = shapeStroke.width / 2.0
bounds = Rect(
x: bounds.x - r,
y: bounds.y - r,
w: bounds.w + r * 2.0,
h: bounds.h + r * 2.0)
}
return bounds
}
return bounds
}
}

View File

@ -1,122 +1,121 @@
import Foundation
#if os(iOS)
import UIKit
import UIKit
#elseif os(OSX)
import AppKit
import AppKit
#endif
open class Text: Node {
open let textVar: Variable<String>
open var text: String {
get { return textVar.value }
set(val) { textVar.value = val }
}
open let fontVar: Variable<Font?>
open var font: Font? {
get { return fontVar.value }
set(val) { fontVar.value = val }
}
open let fillVar: Variable<Fill>
open var fill: Fill {
get { return fillVar.value }
set(val) { fillVar.value = val }
}
open let strokeVar: Variable<Stroke?>
open var stroke: Stroke? {
get { return strokeVar.value }
set(val) { strokeVar.value = val }
}
open let textVar: Variable<String>
open var text: String {
get { return textVar.value }
set(val) { textVar.value = val }
}
open let fontVar: Variable<Font?>
open var font: Font? {
get { return fontVar.value }
set(val) { fontVar.value = val }
}
open let fillVar: Variable<Fill>
open var fill: Fill {
get { return fillVar.value }
set(val) { fillVar.value = val }
}
open let strokeVar: Variable<Stroke?>
open var stroke: Stroke? {
get { return strokeVar.value }
set(val) { strokeVar.value = val }
}
open let alignVar: Variable<Align>
open var align: Align {
get { return alignVar.value }
set(val) { alignVar.value = val }
}
open let baselineVar: Variable<Baseline>
open var baseline: Baseline {
get { return baselineVar.value }
set(val) { baselineVar.value = val }
}
open let alignVar: Variable<Align>
open var align: Align {
get { return alignVar.value }
set(val) { alignVar.value = val }
}
open let baselineVar: Variable<Baseline>
open var baseline: Baseline {
get { return baselineVar.value }
set(val) { baselineVar.value = val }
}
public init(text: String, font: Font? = nil, fill: Fill = Color.black, stroke: Stroke? = nil, align: Align = .min, baseline: Baseline = .top, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.textVar = Variable<String>(text)
self.fontVar = Variable<Font?>(font)
self.fillVar = Variable<Fill>(fill)
self.strokeVar = Variable<Stroke?>(stroke)
self.alignVar = Variable<Align>(align)
self.baselineVar = Variable<Baseline>(baseline)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
}
// GENERATED NOT
override internal func bounds() -> Rect {
let font: MFont
if let f = self.font {
if let customFont = RenderUtils.loadFont(name: f.name, size:f.size) {
font = customFont
} else {
font = MFont.systemFont(ofSize: CGFloat(f.size))
}
} else {
font = MFont.systemFont(ofSize: MFont.mSystemFontSize)
self.textVar = Variable<String>(text)
self.fontVar = Variable<Font?>(font)
self.fillVar = Variable<Fill>(fill)
self.strokeVar = Variable<Stroke?>(stroke)
self.alignVar = Variable<Align>(align)
self.baselineVar = Variable<Baseline>(baseline)
super.init(
place: place,
opaque: opaque,
opacity: opacity,
clip: clip,
effect: effect,
visible: visible,
tag: tag
)
}
var stringAttributes: [NSAttributedStringKey: AnyObject] = [:]
stringAttributes[NSAttributedStringKey.font] = font
let size = (text as NSString).size(withAttributes: stringAttributes)
return Rect(
x: calculateAlignmentOffset(font: font),
y: calculateBaselineOffset(font: font),
w: size.width.doubleValue,
h: size.height.doubleValue
)
}
fileprivate func calculateBaselineOffset(font: MFont) -> Double {
var baselineOffset = 0.0
switch baseline {
case .alphabetic:
baselineOffset = font.ascender.doubleValue
case .bottom:
baselineOffset = (font.ascender - font.descender).doubleValue
case .mid:
baselineOffset = ((font.ascender - font.descender) / 2).doubleValue
default:
break
// GENERATED NOT
override internal func bounds() -> Rect {
let font: MFont
if let f = self.font {
if let customFont = RenderUtils.loadFont(name: f.name, size: f.size) {
font = customFont
} else {
font = MFont.systemFont(ofSize: CGFloat(f.size))
}
} else {
font = MFont.systemFont(ofSize: MFont.mSystemFontSize)
}
var stringAttributes: [NSAttributedStringKey: AnyObject] = [:]
stringAttributes[NSAttributedStringKey.font] = font
let size = (text as NSString).size(withAttributes: stringAttributes)
return Rect(
x: calculateAlignmentOffset(font: font),
y: calculateBaselineOffset(font: font),
w: size.width.doubleValue,
h: size.height.doubleValue
)
}
return -baselineOffset
}
fileprivate func calculateAlignmentOffset(font: MFont) -> Double {
let textAttributes = [
NSAttributedStringKey.font: font
]
let textSize = NSString(string: text).size(withAttributes: textAttributes)
var alignmentOffset = 0.0
switch align {
case .mid:
alignmentOffset = (textSize.width / 2).doubleValue
case .max:
alignmentOffset = textSize.width.doubleValue
default:
break
fileprivate func calculateBaselineOffset(font: MFont) -> Double {
var baselineOffset = 0.0
switch baseline {
case .alphabetic:
baselineOffset = font.ascender.doubleValue
case .bottom:
baselineOffset = (font.ascender - font.descender).doubleValue
case .mid:
baselineOffset = ((font.ascender - font.descender) / 2).doubleValue
default:
break
}
return -baselineOffset
}
return -alignmentOffset
}
fileprivate func calculateAlignmentOffset(font: MFont) -> Double {
let textAttributes = [
NSAttributedStringKey.font: font
]
let textSize = NSString(string: text).size(withAttributes: textAttributes)
var alignmentOffset = 0.0
switch align {
case .mid:
alignmentOffset = (textSize.width / 2).doubleValue
case .max:
alignmentOffset = textSize.width.doubleValue
default:
break
}
return -alignmentOffset
}
}

View File

@ -9,80 +9,79 @@
import Foundation
#if os(iOS)
import UIKit
public typealias MRectCorner = UIRectCorner
public typealias MFont = UIFont
public typealias MColor = UIColor
public typealias MEvent = UIEvent
public typealias MTouch = UITouch
public typealias MImage = UIImage
public typealias MBezierPath = UIBezierPath
public typealias MGestureRecognizer = UIGestureRecognizer
public typealias MGestureRecognizerState = UIGestureRecognizerState
public typealias MGestureRecognizerDelegate = UIGestureRecognizerDelegate
public typealias MTapGestureRecognizer = UITapGestureRecognizer
public typealias MPanGestureRecognizer = UIPanGestureRecognizer
public typealias MPinchGestureRecognizer = UIPinchGestureRecognizer
public typealias MRotationGestureRecognizer = UIRotationGestureRecognizer
public typealias MScreen = UIScreen
public typealias MViewContentMode = UIViewContentMode
extension MTapGestureRecognizer {
func mNumberOfTouches() -> Int {
return numberOfTouches
import UIKit
public typealias MRectCorner = UIRectCorner
public typealias MFont = UIFont
public typealias MColor = UIColor
public typealias MEvent = UIEvent
public typealias MTouch = UITouch
public typealias MImage = UIImage
public typealias MBezierPath = UIBezierPath
public typealias MGestureRecognizer = UIGestureRecognizer
public typealias MGestureRecognizerState = UIGestureRecognizerState
public typealias MGestureRecognizerDelegate = UIGestureRecognizerDelegate
public typealias MTapGestureRecognizer = UITapGestureRecognizer
public typealias MPanGestureRecognizer = UIPanGestureRecognizer
public typealias MPinchGestureRecognizer = UIPinchGestureRecognizer
public typealias MRotationGestureRecognizer = UIRotationGestureRecognizer
public typealias MScreen = UIScreen
public typealias MViewContentMode = UIViewContentMode
extension MTapGestureRecognizer {
func mNumberOfTouches() -> Int {
return numberOfTouches
}
}
}
extension MPanGestureRecognizer {
func mNumberOfTouches() -> Int {
return numberOfTouches
extension MPanGestureRecognizer {
func mNumberOfTouches() -> Int {
return numberOfTouches
}
func mLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint {
return super.location(ofTouch: touch, in: inView)
}
}
func mLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint {
return super.location(ofTouch: touch, in: inView)
extension MRotationGestureRecognizer {
final var mRotation: CGFloat {
get {
return rotation
}
set {
rotation = newValue
}
}
}
}
extension MRotationGestureRecognizer {
final var mRotation: CGFloat {
get {
return rotation
}
set {
rotation = newValue
}
extension MPinchGestureRecognizer {
var mScale: CGFloat {
get {
return scale
}
set {
scale = newValue
}
}
func mLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint {
return super.location(ofTouch: touch, in: inView)
}
}
}
extension MPinchGestureRecognizer {
var mScale: CGFloat {
get {
return scale
}
set {
scale = newValue
}
extension MFont {
class var mSystemFontSize: CGFloat {
return UIFont.systemFontSize
}
}
func mLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint {
return super.location(ofTouch: touch, in: inView)
extension UIScreen {
var mScale: CGFloat {
return self.scale
}
}
}
extension MFont {
class var mSystemFontSize: CGFloat {
return UIFont.systemFontSize
}
}
extension UIScreen {
var mScale: CGFloat {
return self.scale
}
}
#endif

View File

@ -9,42 +9,42 @@
import Foundation
#if os(iOS)
import UIKit
func MGraphicsGetCurrentContext() -> CGContext? {
return UIGraphicsGetCurrentContext()
}
func MGraphicsGetImageFromCurrentImageContext() -> MImage! {
return UIGraphicsGetImageFromCurrentImageContext()
}
func MGraphicsPushContext(_ context: CGContext) {
UIGraphicsPushContext(context)
}
func MGraphicsPopContext() {
UIGraphicsPopContext()
}
func MGraphicsEndImageContext() {
UIGraphicsEndImageContext()
}
func MImagePNGRepresentation(_ image: MImage) -> Data? {
return UIImagePNGRepresentation(image)
}
func MImageJPEGRepresentation(_ image: MImage, _ quality: CGFloat = 0.8) -> Data? {
return UIImageJPEGRepresentation(image, quality)
}
func MMainScreen() -> MScreen? {
return MScreen.main
}
func MGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) {
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
}
import UIKit
func MGraphicsGetCurrentContext() -> CGContext? {
return UIGraphicsGetCurrentContext()
}
func MGraphicsGetImageFromCurrentImageContext() -> MImage! {
return UIGraphicsGetImageFromCurrentImageContext()
}
func MGraphicsPushContext(_ context: CGContext) {
UIGraphicsPushContext(context)
}
func MGraphicsPopContext() {
UIGraphicsPopContext()
}
func MGraphicsEndImageContext() {
UIGraphicsEndImageContext()
}
func MImagePNGRepresentation(_ image: MImage) -> Data? {
return UIImagePNGRepresentation(image)
}
func MImageJPEGRepresentation(_ image: MImage, _ quality: CGFloat = 0.8) -> Data? {
return UIImageJPEGRepresentation(image, quality)
}
func MMainScreen() -> MScreen? {
return MScreen.main
}
func MGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) {
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
}
#endif

View File

@ -8,29 +8,29 @@
#if os(iOS)
import UIKit
class MDisplayLink: MDisplayLinkProtocol {
private var displayLink: CADisplayLink?
private var onUpdate: (() -> Void)?
// MARK: - Lifecycle
deinit {
displayLink?.invalidate()
}
//MARK: - MDisplayLinkProtocol
// MARK: - MDisplayLinkProtocol
func startUpdates(_ onUpdate: @escaping () -> Void) {
self.onUpdate = onUpdate
displayLink = CADisplayLink(target: self, selector: #selector(updateHandler))
displayLink?.frameInterval = 1
displayLink?.add(to: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
}
func invalidate() {
displayLink?.invalidate()
}
// MARK: - Private
@objc func updateHandler() {
onUpdate?()

View File

@ -10,88 +10,87 @@ import Foundation
#if os(iOS)
import UIKit
open class MView: UIView, Touchable {
var mLayer: CALayer? {
return self.layer
}
var mGestureRecognizers: [MGestureRecognizer]? {
return self.gestureRecognizers
}
func removeGestureRecognizers() {
self.gestureRecognizers?.removeAll()
}
open override func touchesBegan(_ touches: Set<MTouch>, with event: MEvent?) {
super.touchesBegan(touches, with: event)
let touchPoints = touches.map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesBegan(touchPoints)
}
open override func touchesMoved(_ touches: Set<MTouch>, with event: MEvent?) {
super.touchesMoved(touches, with: event)
let touchPoints = touches.map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
self.mTouchesMoved(touchPoints)
}
open override func touchesEnded(_ touches: Set<MTouch>, with event: MEvent?) {
super.touchesEnded(touches, with: event)
let touchPoints = touches.map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesEnded(touchPoints)
}
override open func touchesCancelled(_ touches: Set<MTouch>, with event: MEvent?) {
super.touchesCancelled(touches, with: event)
let touchPoints = touches.map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesCancelled(touchPoints)
}
func mTouchesBegan(_ touches: [MTouchEvent]) {
}
func mTouchesMoved(_ touches: [MTouchEvent]) {
}
func mTouchesEnded(_ touches: [MTouchEvent]) {
}
func mTouchesCancelled(_ touches: [MTouchEvent]) {
}
}
#endif
#endif

View File

@ -9,122 +9,119 @@
import Foundation
#if os(OSX)
import Cocoa
import Quartz
public typealias MFont = NSFont
public typealias MColor = NSColor
public typealias MEvent = NSEvent
public typealias MTouch = NSTouch
public typealias MImage = NSImage
public typealias MBezierPath = NSBezierPath
public typealias MGestureRecognizer = NSGestureRecognizer
import Cocoa
import Quartz
public typealias MFont = NSFont
public typealias MColor = NSColor
public typealias MEvent = NSEvent
public typealias MTouch = NSTouch
public typealias MImage = NSImage
public typealias MBezierPath = NSBezierPath
public typealias MGestureRecognizer = NSGestureRecognizer
public typealias MGestureRecognizerState = NSGestureRecognizer.State
public typealias MGestureRecognizerDelegate = NSGestureRecognizerDelegate
public typealias MTapGestureRecognizer = NSClickGestureRecognizer
public typealias MPanGestureRecognizer = NSPanGestureRecognizer
public typealias MPinchGestureRecognizer = NSMagnificationGestureRecognizer
public typealias MRotationGestureRecognizer = NSRotationGestureRecognizer
public typealias MScreen = NSScreen
extension MGestureRecognizer {
var cancelsTouchesInView: Bool {
get {
return false
} set { }
public typealias MGestureRecognizerDelegate = NSGestureRecognizerDelegate
public typealias MTapGestureRecognizer = NSClickGestureRecognizer
public typealias MPanGestureRecognizer = NSPanGestureRecognizer
public typealias MPinchGestureRecognizer = NSMagnificationGestureRecognizer
public typealias MRotationGestureRecognizer = NSRotationGestureRecognizer
public typealias MScreen = NSScreen
extension MGestureRecognizer {
var cancelsTouchesInView: Bool {
get {
return false
} set { }
}
}
}
extension MTapGestureRecognizer {
func mNumberOfTouches() -> Int {
return 1
extension MTapGestureRecognizer {
func mNumberOfTouches() -> Int {
return 1
}
}
}
extension MPanGestureRecognizer {
func mNumberOfTouches() -> Int {
return 1
extension MPanGestureRecognizer {
func mNumberOfTouches() -> Int {
return 1
}
func mLocationOfTouch(_ touch: Int, inView: NSView?) -> NSPoint {
return super.location(in: inView)
}
}
func mLocationOfTouch(_ touch: Int, inView: NSView?) -> NSPoint {
return super.location(in: inView)
extension MRotationGestureRecognizer {
var velocity: CGFloat {
return 0.1
}
var mRotation: CGFloat {
get {
return -rotation
}
set {
rotation = -newValue
}
}
}
}
extension MRotationGestureRecognizer {
var velocity: CGFloat {
return 0.1
extension MPinchGestureRecognizer {
var mScale: CGFloat {
get {
return magnification + 1.0
}
set {
magnification = newValue - 1.0
}
}
func mLocationOfTouch(_ touch: Int, inView view: NSView?) -> NSPoint {
return super.location(in: view)
}
}
var mRotation: CGFloat {
get {
return -rotation
}
set {
rotation = -newValue
}
extension NSFont {
var lineHeight: CGFloat {
return self.boundingRectForFont.size.height
}
class var mSystemFontSize: CGFloat {
return NSFont.systemFontSize
}
}
}
extension MPinchGestureRecognizer {
var mScale: CGFloat {
get {
return magnification + 1.0
}
set {
magnification = newValue - 1.0
}
extension NSScreen {
var mScale: CGFloat {
return self.backingScaleFactor
}
}
func mLocationOfTouch(_ touch: Int, inView view: NSView?) -> NSPoint {
return super.location(in: view)
extension NSImage {
var cgImage: CGImage? {
return self.cgImage(forProposedRect: nil, context: nil, hints: nil)
}
}
}
extension NSFont {
var lineHeight: CGFloat {
return self.boundingRectForFont.size.height
extension NSTouch {
func location(in view: NSView) -> NSPoint {
let n = self.normalizedPosition
let b = view.bounds
return NSPoint(x: b.origin.x + b.size.width * n.x, y: b.origin.y + b.size.height * n.y)
}
}
class var mSystemFontSize: CGFloat {
return NSFont.systemFontSize
extension NSString {
@nonobjc
func size(attributes attrs: [NSAttributedStringKey: Any]? = nil) -> NSSize {
return size(withAttributes: attrs)
}
}
}
extension NSScreen {
var mScale: CGFloat {
return self.backingScaleFactor
func MMainScreen() -> MScreen? {
return MScreen.main
}
}
extension NSImage {
var cgImage: CGImage? {
return self.cgImage(forProposedRect: nil, context: nil, hints: nil)
}
}
extension NSTouch {
func location(in view: NSView) -> NSPoint {
let n = self.normalizedPosition
let b = view.bounds
return NSPoint(x: b.origin.x + b.size.width * n.x, y: b.origin.y + b.size.height * n.y)
}
}
extension NSString {
@nonobjc
func size(attributes attrs: [NSAttributedStringKey : Any]? = nil) -> NSSize {
return size(withAttributes: attrs)
}
}
func MMainScreen() -> MScreen? {
return MScreen.main
}
#endif

View File

@ -9,88 +9,87 @@
import Foundation
#if os(OSX)
import AppKit
private var imageContextStack: [CGFloat] = []
func MGraphicsGetCurrentContext() -> CGContext? {
return NSGraphicsContext.current?.cgContext
}
func MGraphicsPushContext(_ context: CGContext) {
let cx = NSGraphicsContext(cgContext: context, flipped: true)
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = cx
}
func MGraphicsPopContext() {
NSGraphicsContext.restoreGraphicsState()
}
func MImagePNGRepresentation(_ image: MImage) -> Data? {
image.lockFocus()
let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height))
image.unlockFocus()
return rep?.representation(using: NSBitmapImageRep.FileType.png, properties: [:])
}
func MImageJPEGRepresentation(_ image: MImage, _ quality: CGFloat = 0.9) -> Data? {
image.lockFocus()
let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height))
image.unlockFocus()
return rep?.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [NSBitmapImageRep.PropertyKey.compressionFactor: quality])
}
func MGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) {
var scale = scale
if scale == 0.0 {
scale = NSScreen.main?.backingScaleFactor ?? 1.0
import AppKit
private var imageContextStack: [CGFloat] = []
func MGraphicsGetCurrentContext() -> CGContext? {
return NSGraphicsContext.current?.cgContext
}
let width = Int(size.width * scale)
let height = Int(size.height * scale)
if width > 0 && height > 0 {
imageContextStack.append(scale)
let colorSpace = CGColorSpaceCreateDeviceRGB()
guard let ctx = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4*width, space: colorSpace, bitmapInfo: (opaque ? CGImageAlphaInfo.noneSkipFirst.rawValue : CGImageAlphaInfo.premultipliedFirst.rawValue)) else {
return
}
ctx.concatenate(CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(height)))
ctx.scaleBy(x: scale, y: scale)
MGraphicsPushContext(ctx)
func MGraphicsPushContext(_ context: CGContext) {
let cx = NSGraphicsContext(cgContext: context, flipped: true)
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = cx
}
}
func MGraphicsGetImageFromCurrentImageContext() -> MImage? {
if !imageContextStack.isEmpty {
guard let ctx = MGraphicsGetCurrentContext() else {
func MGraphicsPopContext() {
NSGraphicsContext.restoreGraphicsState()
}
func MImagePNGRepresentation(_ image: MImage) -> Data? {
image.lockFocus()
let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height))
image.unlockFocus()
return rep?.representation(using: NSBitmapImageRep.FileType.png, properties: [:])
}
func MImageJPEGRepresentation(_ image: MImage, _ quality: CGFloat = 0.9) -> Data? {
image.lockFocus()
let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height))
image.unlockFocus()
return rep?.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [NSBitmapImageRep.PropertyKey.compressionFactor: quality])
}
func MGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) {
var scale = scale
if scale == 0.0 {
scale = NSScreen.main?.backingScaleFactor ?? 1.0
}
let width = Int(size.width * scale)
let height = Int(size.height * scale)
if width > 0 && height > 0 {
imageContextStack.append(scale)
let colorSpace = CGColorSpaceCreateDeviceRGB()
guard let ctx = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4 * width, space: colorSpace, bitmapInfo: (opaque ? CGImageAlphaInfo.noneSkipFirst.rawValue : CGImageAlphaInfo.premultipliedFirst.rawValue)) else {
return
}
ctx.concatenate(CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(height)))
ctx.scaleBy(x: scale, y: scale)
MGraphicsPushContext(ctx)
}
}
func MGraphicsGetImageFromCurrentImageContext() -> MImage? {
if !imageContextStack.isEmpty {
guard let ctx = MGraphicsGetCurrentContext() else {
return nil
}
let scale = imageContextStack.last!
if let theCGImage = ctx.makeImage() {
let size = CGSize(width: CGFloat(ctx.width) / scale, height: CGFloat(ctx.height) / scale)
let image = NSImage(cgImage: theCGImage, size: size)
return image
}
}
return nil
}
let scale = imageContextStack.last!
if let theCGImage = ctx.makeImage() {
let size = CGSize(width: CGFloat(ctx.width) / scale, height: CGFloat(ctx.height) / scale)
let image = NSImage(cgImage: theCGImage, size: size)
return image
}
}
return nil
}
func MGraphicsEndImageContext() {
if imageContextStack.last != nil {
imageContextStack.removeLast()
MGraphicsPopContext()
func MGraphicsEndImageContext() {
if imageContextStack.last != nil {
imageContextStack.removeLast()
MGraphicsPopContext()
}
}
}
#endif

View File

@ -9,164 +9,162 @@
import Foundation
#if os(OSX)
import AppKit
import AppKit
public struct MRectCorner: OptionSet {
public let rawValue: UInt
public static let none = MRectCorner(rawValue: 0)
public static let topLeft = MRectCorner(rawValue: 1 << 0)
public static let topRight = MRectCorner(rawValue: 1 << 1)
public static let bottomLeft = MRectCorner(rawValue: 1 << 2)
public static let bottomRight = MRectCorner(rawValue: 1 << 3)
public static var allCorners: MRectCorner {
return [.topLeft, .topRight, .bottomLeft, .bottomRight]
}
public init(rawValue: UInt) {
self.rawValue = rawValue
}
}
extension MBezierPath {
public var cgPath: CGPath {
get { let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< self.elementCount {
let type = self.element(at: i, associatedPoints: &points)
switch type {
case .moveToBezierPathElement:
path.move(to: CGPoint(x: points[0].x, y: points[0].y))
case .lineToBezierPathElement:
path.addLine(to: CGPoint(x: points[0].x, y: points[0].y))
case .curveToBezierPathElement:
path.addCurve(
to: CGPoint(x: points[2].x, y: points[2].y),
control1: CGPoint(x: points[0].x, y: points[0].y),
control2: CGPoint(x: points[1].x, y: points[1].y))
case .closePathBezierPathElement:
path.closeSubpath()
}
public struct MRectCorner: OptionSet {
public let rawValue: UInt
public static let none = MRectCorner(rawValue: 0)
public static let topLeft = MRectCorner(rawValue: 1 << 0)
public static let topRight = MRectCorner(rawValue: 1 << 1)
public static let bottomLeft = MRectCorner(rawValue: 1 << 2)
public static let bottomRight = MRectCorner(rawValue: 1 << 3)
public static var allCorners: MRectCorner {
return [.topLeft, .topRight, .bottomLeft, .bottomRight]
}
public init(rawValue: UInt) {
self.rawValue = rawValue
}
return path
}
}
public convenience init(arcCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) {
self.init()
self.addArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: clockwise)
extension MBezierPath {
public var cgPath: CGPath {
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< self.elementCount {
let type = self.element(at: i, associatedPoints: &points)
switch type {
case .moveToBezierPathElement:
path.move(to: CGPoint(x: points[0].x, y: points[0].y))
case .lineToBezierPathElement:
path.addLine(to: CGPoint(x: points[0].x, y: points[0].y))
case .curveToBezierPathElement:
path.addCurve(
to: CGPoint(x: points[2].x, y: points[2].y),
control1: CGPoint(x: points[0].x, y: points[0].y),
control2: CGPoint(x: points[1].x, y: points[1].y))
case .closePathBezierPathElement:
path.closeSubpath()
}
}
return path
}
public convenience init(arcCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) {
self.init()
self.addArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: clockwise)
}
public convenience init(roundedRect rect: NSRect, byRoundingCorners corners: MRectCorner, cornerRadii: NSSize) {
self.init()
let kappa: CGFloat = 1.0 - 0.552228474
let topLeft = rect.origin
let topRight = NSPoint(x: rect.maxX, y: rect.minY)
let bottomRight = NSPoint(x: rect.maxX, y: rect.maxY)
let bottomLeft = NSPoint(x: rect.minX, y: rect.maxY)
if corners.contains(.topLeft) {
move(to: CGPoint(x: topLeft.x + cornerRadii.width, y: topLeft.y))
} else {
move(to: topLeft)
}
if corners.contains(.topRight) {
line(to: CGPoint(x: topRight.x - cornerRadii.width, y: topRight.y))
curve(to: CGPoint(x: topRight.x, y: topRight.y + cornerRadii.height),
controlPoint1: CGPoint(x: topRight.x - cornerRadii.width * kappa, y: topRight.y),
controlPoint2: CGPoint(x: topRight.x, y: topRight.y + cornerRadii.height * kappa))
} else {
line(to: topRight)
}
if corners.contains(.bottomRight) {
line(to: CGPoint(x: bottomRight.x, y: bottomRight.y - cornerRadii.height))
curve(to: CGPoint(x: bottomRight.x - cornerRadii.width, y: bottomRight.y),
controlPoint1: CGPoint(x: bottomRight.x, y: bottomRight.y - cornerRadii.height * kappa),
controlPoint2: CGPoint(x: bottomRight.x - cornerRadii.width * kappa, y: bottomRight.y))
} else {
line(to: bottomRight)
}
if corners.contains(.bottomLeft) {
line(to: CGPoint(x: bottomLeft.x + cornerRadii.width, y: bottomLeft.y))
curve(to: CGPoint(x: bottomLeft.x, y: bottomLeft.y - cornerRadii.height),
controlPoint1: CGPoint(x: bottomLeft.x + cornerRadii.width * kappa, y: bottomLeft.y),
controlPoint2: CGPoint(x: bottomLeft.x, y: bottomLeft.y - cornerRadii.height * kappa))
} else {
line(to: bottomLeft)
}
if corners.contains(.topLeft) {
line(to: CGPoint(x: topLeft.x, y: topLeft.y + cornerRadii.height))
curve(to: CGPoint(x: topLeft.x + cornerRadii.width, y: topLeft.y),
controlPoint1: CGPoint(x: topLeft.x, y: topLeft.y + cornerRadii.height * kappa),
controlPoint2: CGPoint(x: topLeft.x + cornerRadii.width * kappa, y: topLeft.y))
} else {
line(to: topLeft)
}
close()
}
func reversing() -> MBezierPath {
return self.reversed
}
func addLine(to: NSPoint) {
self.line(to: to)
}
func addCurve(to: NSPoint, controlPoint1: NSPoint, controlPoint2: NSPoint) {
self.curve(to: to, controlPoint1: controlPoint1, controlPoint2: controlPoint2)
}
func addQuadCurveToPoint(endPoint: NSPoint, controlPoint: NSPoint) {
let QP0 = self.currentPoint
let CP3 = endPoint
let CP1 = CGPoint(
x: QP0.x + ((2.0 / 3.0) * (controlPoint.x - QP0.x)),
y: QP0.y + ((2.0 / 3.0) * (controlPoint.y - QP0.y))
)
let CP2 = CGPoint(
x: endPoint.x + (2.0 / 3.0) * (controlPoint.x - endPoint.x),
y: endPoint.y + (2.0 / 3.0) * (controlPoint.y - endPoint.y)
)
self.addCurve(to: CP3, controlPoint1: CP1, controlPoint2: CP2)
}
func addArc(withCenter: NSPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) {
let startAngleRadian = ((startAngle) * (180.0 / .pi))
let endAngleRadian = ((endAngle) * (180.0 / .pi))
self.appendArc(withCenter: withCenter, radius: radius, startAngle: startAngleRadian, endAngle: endAngleRadian, clockwise: !clockwise)
}
func addPath(path: NSBezierPath!) {
self.append(path)
}
}
public convenience init(roundedRect rect: NSRect, byRoundingCorners corners: MRectCorner, cornerRadii: NSSize) {
self.init()
let kappa: CGFloat = 1.0 - 0.552228474
let topLeft = rect.origin
let topRight = NSPoint(x: rect.maxX, y: rect.minY);
let bottomRight = NSPoint(x: rect.maxX, y: rect.maxY);
let bottomLeft = NSPoint(x: rect.minX, y: rect.maxY);
if corners.contains(.topLeft) {
move(to: CGPoint(x: topLeft.x + cornerRadii.width, y: topLeft.y))
} else {
move(to: topLeft)
}
if corners.contains(.topRight) {
line(to: CGPoint(x: topRight.x - cornerRadii.width, y: topRight.y))
curve(to: CGPoint(x: topRight.x, y: topRight.y + cornerRadii.height),
controlPoint1: CGPoint(x: topRight.x - cornerRadii.width * kappa, y: topRight.y),
controlPoint2: CGPoint(x: topRight.x, y: topRight.y + cornerRadii.height * kappa))
} else {
line(to: topRight)
}
if corners.contains(.bottomRight) {
line(to: CGPoint(x: bottomRight.x, y: bottomRight.y - cornerRadii.height))
curve(to: CGPoint(x: bottomRight.x - cornerRadii.width, y: bottomRight.y),
controlPoint1: CGPoint(x: bottomRight.x, y: bottomRight.y - cornerRadii.height * kappa),
controlPoint2: CGPoint(x: bottomRight.x - cornerRadii.width * kappa, y: bottomRight.y))
} else {
line(to: bottomRight)
}
if corners.contains(.bottomLeft) {
line(to: CGPoint(x: bottomLeft.x + cornerRadii.width, y: bottomLeft.y))
curve(to: CGPoint(x: bottomLeft.x, y: bottomLeft.y - cornerRadii.height),
controlPoint1: CGPoint(x: bottomLeft.x + cornerRadii.width * kappa, y: bottomLeft.y),
controlPoint2: CGPoint(x: bottomLeft.x, y: bottomLeft.y - cornerRadii.height * kappa))
} else {
line(to: bottomLeft)
}
if corners.contains(.topLeft) {
line(to: CGPoint(x: topLeft.x, y: topLeft.y + cornerRadii.height))
curve(to: CGPoint(x: topLeft.x + cornerRadii.width, y: topLeft.y),
controlPoint1: CGPoint(x: topLeft.x, y: topLeft.y + cornerRadii.height * kappa),
controlPoint2: CGPoint(x: topLeft.x + cornerRadii.width * kappa, y: topLeft.y))
} else {
line(to: topLeft)
}
close()
}
func reversing() -> MBezierPath {
return self.reversed
}
func addLine(to: NSPoint) {
self.line(to: to)
}
func addCurve(to: NSPoint, controlPoint1: NSPoint, controlPoint2: NSPoint) {
self.curve(to: to, controlPoint1: controlPoint1, controlPoint2: controlPoint2)
}
func addQuadCurveToPoint(endPoint: NSPoint, controlPoint: NSPoint) {
let QP0 = self.currentPoint
let CP3 = endPoint
let CP1 = CGPoint(
x: QP0.x + ((2.0 / 3.0) * (controlPoint.x - QP0.x)),
y: QP0.y + ((2.0 / 3.0) * (controlPoint.y - QP0.y))
)
let CP2 = CGPoint(
x: endPoint.x + (2.0 / 3.0) * (controlPoint.x - endPoint.x),
y: endPoint.y + (2.0 / 3.0) * (controlPoint.y - endPoint.y)
)
self.addCurve(to: CP3, controlPoint1: CP1, controlPoint2: CP2)
}
func addArc(withCenter: NSPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) {
let startAngleRadian = ((startAngle) * (180.0 / .pi))
let endAngleRadian = ((endAngle) * (180.0 / .pi))
self.appendArc(withCenter: withCenter, radius: radius, startAngle: startAngleRadian, endAngle: endAngleRadian, clockwise: !clockwise)
}
func addPath(path: NSBezierPath!) {
self.append(path)
}
}
#endif

View File

@ -10,37 +10,37 @@ import Foundation
#if os(OSX)
import AppKit
public class MDisplayLink: MDisplayLinkProtocol {
private var displayLink: CVDisplayLink?
private var onUpdate: (() -> Void)?
// MARK: - Lifecycle
deinit {
stop()
}
// MARK: - MDisplayLinkProtocol
func startUpdates(_ onUpdate: @escaping () -> Void) {
self.onUpdate = onUpdate
if CVDisplayLinkCreateWithActiveCGDisplays(&displayLink) != kCVReturnSuccess {
return
}
CVDisplayLinkSetOutputCallback(displayLink!, { (_, _, _, _, _, userData) -> CVReturn in
let `self` = unsafeBitCast(userData, to: MDisplayLink.self)
`self`.onUpdate?()
return kCVReturnSuccess
}, Unmanaged.passUnretained(self).toOpaque())
if displayLink != nil {
CVDisplayLinkStart(displayLink!)
}
}
func invalidate() {
stop()
}
@ -51,5 +51,5 @@ import Foundation
}
}
}
#endif

View File

@ -9,192 +9,189 @@
import Foundation
#if os(OSX)
import AppKit
public enum MViewContentMode: Int {
case scaleToFill
case scaleAspectFit
case scaleAspectFill
case redraw
case center
case top
case bottom
case left
case right
case topLeft
case topRight
case bottomLeft
case bottomRight
}
open class MView: NSView, Touchable {
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.wantsLayer = true
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
self.wantsLayer = true
setupMouse()
}
open override var isFlipped: Bool {
return true
}
var mGestureRecognizers: [NSGestureRecognizer]? {
return self.gestureRecognizers
}
open var backgroundColor: MColor? {
get {
return self.layer?.backgroundColor == nil ? nil : NSColor(cgColor: self.layer!.backgroundColor!)
}
set {
self.layer?.backgroundColor = newValue == nil ? nil : newValue?.cgColor ?? MColor.black.cgColor
}
}
var mLayer: CALayer? {
return self.layer
}
var contentMode: MViewContentMode = .scaleToFill
func removeGestureRecognizers() {
self.gestureRecognizers.removeAll()
}
func didMoveToSuperview() {
super.viewDidMoveToSuperview()
}
func setNeedsDisplay() {
self.setNeedsDisplay(self.bounds)
}
func layoutSubviews() {
super.resizeSubviews(withOldSize: self.bounds.size)
}
// MARK: - Touch pad
open override func touchesBegan(with event: NSEvent) {
super.touchesBegan(with: event)
let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesBegan(touchPoints)
}
open override func touchesEnded(with event: NSEvent) {
super.touchesEnded(with: event)
let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesEnded(touchPoints)
}
open override func touchesMoved(with event: NSEvent) {
super.touchesMoved(with: event)
let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesMoved(touchPoints)
}
open override func touchesCancelled(with event: NSEvent) {
super.touchesCancelled(with: event)
let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesCancelled(touchPoints)
}
// MARK: - Mouse
private func setupMouse() {
subscribeForMouseDown()
subscribeForMouseUp()
subscribeForMouseDragged()
}
private func subscribeForMouseDown() {
NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown, handler: { [weak self] event -> NSEvent? in
self?.handleInput(event: event, handler: { touches in
self?.mTouchesBegan(touches)
})
return event
})
}
private func subscribeForMouseUp() {
NSEvent.addLocalMonitorForEvents(matching: .leftMouseUp, handler: { [weak self] event -> NSEvent? in
self?.handleInput(event: event, handler: { touches in
self?.mTouchesEnded(touches)
})
return event
})
import AppKit
public enum MViewContentMode: Int {
case scaleToFill = 0
case scaleAspectFit = 1
case scaleAspectFill = 2
case redraw = 3
case center = 4
case top = 5
case bottom = 6
case left = 7
case right = 8
case topLeft = 9
case topRight = 10
case bottomLeft = 11
case bottomRight = 12
}
private func subscribeForMouseDragged() {
NSEvent.addLocalMonitorForEvents(matching: .leftMouseDragged, handler: { [weak self] event -> NSEvent? in
self?.handleInput(event: event, handler: { touches in
self?.mTouchesMoved(touches)
})
return event
})
open class MView: NSView, Touchable {
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.wantsLayer = true
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
self.wantsLayer = true
setupMouse()
}
open override var isFlipped: Bool {
return true
}
var mGestureRecognizers: [NSGestureRecognizer]? {
return self.gestureRecognizers
}
open var backgroundColor: MColor? {
get {
return self.layer?.backgroundColor == nil ? nil : NSColor(cgColor: self.layer!.backgroundColor!)
}
set {
self.layer?.backgroundColor = newValue == nil ? nil : newValue?.cgColor ?? MColor.black.cgColor
}
}
var mLayer: CALayer? {
return self.layer
}
var contentMode: MViewContentMode = .scaleToFill
func removeGestureRecognizers() {
self.gestureRecognizers.removeAll()
}
func didMoveToSuperview() {
super.viewDidMoveToSuperview()
}
func setNeedsDisplay() {
self.setNeedsDisplay(self.bounds)
}
func layoutSubviews() {
super.resizeSubviews(withOldSize: self.bounds.size)
}
// MARK: - Touch pad
open override func touchesBegan(with event: NSEvent) {
super.touchesBegan(with: event)
let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesBegan(touchPoints)
}
open override func touchesEnded(with event: NSEvent) {
super.touchesEnded(with: event)
let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesEnded(touchPoints)
}
open override func touchesMoved(with event: NSEvent) {
super.touchesMoved(with: event)
let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesMoved(touchPoints)
}
open override func touchesCancelled(with event: NSEvent) {
super.touchesCancelled(with: event)
let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in
let location = touch.location(in: self)
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
}
mTouchesCancelled(touchPoints)
}
// MARK: - Mouse
private func setupMouse() {
subscribeForMouseDown()
subscribeForMouseUp()
subscribeForMouseDragged()
}
private func subscribeForMouseDown() {
NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) { [weak self] event -> NSEvent? in
self?.handleInput(event: event) { touches in
self?.mTouchesBegan(touches)
}
return event
}
}
private func subscribeForMouseUp() {
NSEvent.addLocalMonitorForEvents(matching: .leftMouseUp) { [weak self] event -> NSEvent? in
self?.handleInput(event: event) { touches in
self?.mTouchesEnded(touches)
}
return event
}
}
private func subscribeForMouseDragged() {
NSEvent.addLocalMonitorForEvents(matching: .leftMouseDragged) { [weak self] event -> NSEvent? in
self?.handleInput(event: event) { touches in
self?.mTouchesMoved(touches)
}
return event
}
}
private func handleInput(event: NSEvent, handler: (_ touches: [MTouchEvent]) -> Void ) {
let location = self.convert(event.locationInWindow, to: .none)
let touchPoint = MTouchEvent(x: Double(location.x), y: Double(location.y), id: 0)
handler([touchPoint])
return
}
// MARK: - Touchable
func mTouchesBegan(_ touches: [MTouchEvent]) {
}
func mTouchesMoved(_ touches: [MTouchEvent]) {
}
func mTouchesEnded(_ touches: [MTouchEvent]) {
}
func mTouchesCancelled(_ touches: [MTouchEvent]) {
}
}
private func handleInput(event: NSEvent, handler: (_ touches: [MTouchEvent]) -> Void ) {
let location = self.convert(event.locationInWindow, to: .none)
let touchPoint = MTouchEvent(x: Double(location.x), y: Double(location.y), id: 0)
handler([touchPoint])
return
}
// MARK: - Touchable
func mTouchesBegan(_ touches: [MTouchEvent]) {
}
func mTouchesMoved(_ touches: [MTouchEvent]) {
}
func mTouchesEnded(_ touches: [MTouchEvent]) {
}
func mTouchesCancelled(_ touches: [MTouchEvent]) {
}
}
#endif

View File

@ -1,79 +1,79 @@
import Foundation
#if os(iOS)
import UIKit
import UIKit
#endif
class GroupRenderer: NodeRenderer {
weak var group: Group?
fileprivate var renderers: [NodeRenderer] = []
let renderingInterval: RenderingInterval?
init(group: Group, ctx: RenderContext, animationCache: AnimationCache?, interval: RenderingInterval? = .none) {
self.group = group
self.renderingInterval = interval
super.init(node: group, ctx: ctx, animationCache: animationCache)
updateRenderers()
}
override func doAddObservers() {
super.doAddObservers()
guard let group = group else {
return
weak var group: Group?
fileprivate var renderers: [NodeRenderer] = []
let renderingInterval: RenderingInterval?
init(group: Group, ctx: RenderContext, animationCache: AnimationCache?, interval: RenderingInterval? = .none) {
self.group = group
self.renderingInterval = interval
super.init(node: group, ctx: ctx, animationCache: animationCache)
updateRenderers()
}
group.contentsVar.onChange { [weak self] _ in
self?.updateRenderers()
override func doAddObservers() {
super.doAddObservers()
guard let group = group else {
return
}
group.contentsVar.onChange { [weak self] _ in
self?.updateRenderers()
}
observe(group.contentsVar)
}
observe(group.contentsVar)
}
override func node() -> Node? {
return group
}
override func doRender(_ force: Bool, opacity: Double) {
renderers.forEach { renderer in
renderer.render(force: force, opacity: opacity)
override func node() -> Node? {
return group
}
}
override func doFindNodeAt(location: CGPoint, ctx: CGContext) -> Node? {
for renderer in renderers.reversed() {
if let node = renderer.findNodeAt(location: location, ctx: ctx) {
return node
}
override func doRender(_ force: Bool, opacity: Double) {
renderers.forEach { renderer in
renderer.render(force: force, opacity: opacity)
}
}
return nil
}
override func dispose() {
super.dispose()
renderers.forEach { renderer in renderer.dispose() }
renderers = []
}
private func updateRenderers() {
renderers.forEach{ $0.dispose() }
renderers.removeAll()
if let updatedRenderers = group?.contents.flatMap ({ (child) -> NodeRenderer? in
guard let interval = renderingInterval else {
return RenderUtils.createNodeRenderer(child, context: ctx, animationCache: animationCache)
}
let index = AnimationUtils.absoluteIndex(child, useCache: true)
if index > interval.from && index < interval.to {
return RenderUtils.createNodeRenderer(child, context: ctx, animationCache: animationCache, interval: interval)
}
return .none
}) {
renderers = updatedRenderers
override func doFindNodeAt(location: CGPoint, ctx: CGContext) -> Node? {
for renderer in renderers.reversed() {
if let node = renderer.findNodeAt(location: location, ctx: ctx) {
return node
}
}
return nil
}
override func dispose() {
super.dispose()
renderers.forEach { renderer in renderer.dispose() }
renderers = []
}
private func updateRenderers() {
renderers.forEach { $0.dispose() }
renderers.removeAll()
if let updatedRenderers = group?.contents.flatMap ({ child -> NodeRenderer? in
guard let interval = renderingInterval else {
return RenderUtils.createNodeRenderer(child, context: ctx, animationCache: animationCache)
}
let index = AnimationUtils.absoluteIndex(child, useCache: true)
if index > interval.from && index < interval.to {
return RenderUtils.createNodeRenderer(child, context: ctx, animationCache: animationCache, interval: interval)
}
return .none
}) {
renderers = updatedRenderers
}
}
}
}

View File

@ -5,181 +5,181 @@ import Foundation
#endif
#if os(iOS)
import UIKit
import UIKit
#endif
class ImageRenderer: NodeRenderer {
weak var image: Image?
var renderedPaths: [CGPath] = [CGPath]()
init(image: Image, ctx: RenderContext, animationCache: AnimationCache?) {
self.image = image
super.init(node: image, ctx: ctx, animationCache: animationCache)
}
override func node() -> Node? {
return image
}
override func doAddObservers() {
super.doAddObservers()
guard let image = image else {
return
weak var image: Image?
var renderedPaths: [CGPath] = [CGPath]()
init(image: Image, ctx: RenderContext, animationCache: AnimationCache?) {
self.image = image
super.init(node: image, ctx: ctx, animationCache: animationCache)
}
observe(image.srcVar)
observe(image.xAlignVar)
observe(image.yAlignVar)
observe(image.aspectRatioVar)
observe(image.wVar)
observe(image.hVar)
}
override func doRender(_ force: Bool, opacity: Double) {
guard let image = image else {
return
override func node() -> Node? {
return image
}
var mImage: MImage?
if image.src.contains("memory") {
let id = image.src.replacingOccurrences(of: "memory://", with: "")
mImage = imagesMap[id]
} else {
mImage = image.image()
override func doAddObservers() {
super.doAddObservers()
guard let image = image else {
return
}
observe(image.srcVar)
observe(image.xAlignVar)
observe(image.yAlignVar)
observe(image.aspectRatioVar)
observe(image.wVar)
observe(image.hVar)
}
if let mImage = mImage {
let rect = getRect(mImage)
ctx.cgContext!.scaleBy(x: 1.0, y: -1.0)
ctx.cgContext!.translateBy(x: 0.0, y: -1.0 * rect.height)
ctx.cgContext!.setAlpha(CGFloat(opacity))
ctx.cgContext!.draw(mImage.cgImage!, in: rect)
override func doRender(_ force: Bool, opacity: Double) {
guard let image = image else {
return
}
var mImage: MImage?
if image.src.contains("memory") {
let id = image.src.replacingOccurrences(of: "memory://", with: "")
mImage = imagesMap[id]
} else {
mImage = image.image()
}
if let mImage = mImage {
let rect = getRect(mImage)
ctx.cgContext!.scaleBy(x: 1.0, y: -1.0)
ctx.cgContext!.translateBy(x: 0.0, y: -1.0 * rect.height)
ctx.cgContext!.setAlpha(CGFloat(opacity))
ctx.cgContext!.draw(mImage.cgImage!, in: rect)
}
}
}
override func doFindNodeAt(location: CGPoint, ctx: CGContext) -> Node? {
guard let image = image else {
return .none
override func doFindNodeAt(location: CGPoint, ctx: CGContext) -> Node? {
guard let image = image else {
return .none
}
#if os(iOS)
let osImage = MImage(named: image.src)
#elseif os(OSX)
let osImage = MImage(named: NSImage.Name(rawValue: image.src))
#endif
if let mImage = osImage {
let rect = getRect(mImage)
if rect.contains(location) {
return node()
}
}
return nil
}
#if os(iOS)
let osImage = MImage(named: image.src)
#elseif os(OSX)
let osImage = MImage(named: NSImage.Name(rawValue: image.src))
#endif
if let mImage = osImage {
let rect = getRect(mImage)
if (rect.contains(location)) {
return node()
}
fileprivate func getRect(_ mImage: MImage) -> CGRect {
guard let image = image else {
return .zero
}
let imageSize = mImage.size
var w = CGFloat(image.w)
var h = CGFloat(image.h)
if (w == 0 || w == imageSize.width) && (h == 0 || h == imageSize.height) {
return CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height)
} else {
if w == 0 {
w = imageSize.width * h / imageSize.height
} else if h == 0 {
h = imageSize.height * w / imageSize.width
}
switch image.aspectRatio {
case AspectRatio.meet:
return calculateMeetAspectRatio(image, size: imageSize)
case AspectRatio.slice:
return calculateSliceAspectRatio(image, size: imageSize)
//ctx.cgContext!.clip(to: CGRect(x: 0, y: 0, width: w, height: h))
default:
return CGRect(x: 0, y: 0, width: w, height: h)
}
}
}
return nil
}
fileprivate func getRect(_ mImage: MImage) -> CGRect {
guard let image = image else {
return .zero
fileprivate func calculateMeetAspectRatio(_ image: Image, size: CGSize) -> CGRect {
let w = CGFloat(image.w)
let h = CGFloat(image.h)
// destination and source aspect ratios
let destAR = w / h
let srcAR = size.width / size.height
var resultW = w
var resultH = h
var destX = CGFloat(0)
var destY = CGFloat(0)
if destAR < srcAR {
// fill all available width and scale height
resultH = size.height * w / size.width
} else {
// fill all available height and scale width
resultW = size.width * h / size.height
}
let xalign = image.xAlign
switch xalign {
case Align.min:
destX = 0
case Align.mid:
destX = w / 2 - resultW / 2
case Align.max:
destX = w - resultW
}
let yalign = image.yAlign
switch yalign {
case Align.min:
destY = 0
case Align.mid:
destY = h / 2 - resultH / 2
case Align.max:
destY = h - resultH
}
return CGRect(x: destX, y: destY, width: resultW, height: resultH)
}
let imageSize = mImage.size
var w = CGFloat(image.w)
var h = CGFloat(image.h)
if ((w == 0 || w == imageSize.width) && (h == 0 || h == imageSize.height)) {
return CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height)
} else {
if (w == 0) {
w = imageSize.width * h / imageSize.height
} else if (h == 0) {
h = imageSize.height * w / imageSize.width
}
switch (image.aspectRatio) {
case AspectRatio.meet:
return calculateMeetAspectRatio(image, size: imageSize)
case AspectRatio.slice:
return calculateSliceAspectRatio(image, size: imageSize)
//ctx.cgContext!.clip(to: CGRect(x: 0, y: 0, width: w, height: h))
default:
return CGRect(x: 0, y: 0, width: w, height: h)
}
fileprivate func calculateSliceAspectRatio(_ image: Image, size: CGSize) -> CGRect {
let w = CGFloat(image.w)
let h = CGFloat(image.h)
var srcX = CGFloat(0)
var srcY = CGFloat(0)
var totalH: CGFloat = 0
var totalW: CGFloat = 0
// destination and source aspect ratios
let destAR = w / h
let srcAR = size.width / size.height
if destAR > srcAR {
// fill all available width and scale height
totalH = size.height * w / size.width
totalW = w
switch image.yAlign {
case Align.min:
srcY = 0
case Align.mid:
srcY = -(totalH / 2 - h / 2)
case Align.max:
srcY = -(totalH - h)
}
} else {
// fill all available height and scale width
totalW = size.width * h / size.height
totalH = h
switch image.xAlign {
case Align.min:
srcX = 0
case Align.mid:
srcX = -(totalW / 2 - w / 2)
case Align.max:
srcX = -(totalW - w)
}
}
return CGRect(x: srcX, y: srcY, width: totalW, height: totalH)
}
}
fileprivate func calculateMeetAspectRatio(_ image: Image, size: CGSize) -> CGRect {
let w = CGFloat(image.w)
let h = CGFloat(image.h)
// destination and source aspect ratios
let destAR = w / h
let srcAR = size.width / size.height
var resultW = w
var resultH = h
var destX = CGFloat(0)
var destY = CGFloat(0)
if (destAR < srcAR) {
// fill all available width and scale height
resultH = size.height * w / size.width
} else {
// fill all available height and scale width
resultW = size.width * h / size.height
}
let xalign = image.xAlign
switch (xalign) {
case Align.min:
destX = 0
case Align.mid:
destX = w / 2 - resultW / 2
case Align.max:
destX = w - resultW
}
let yalign = image.yAlign
switch (yalign) {
case Align.min:
destY = 0
case Align.mid:
destY = h / 2 - resultH / 2
case Align.max:
destY = h - resultH
}
return CGRect(x: destX, y: destY, width: resultW, height: resultH)
}
fileprivate func calculateSliceAspectRatio(_ image: Image, size: CGSize) -> CGRect {
let w = CGFloat(image.w)
let h = CGFloat(image.h)
var srcX = CGFloat(0)
var srcY = CGFloat(0)
var totalH: CGFloat = 0
var totalW: CGFloat = 0
// destination and source aspect ratios
let destAR = w / h
let srcAR = size.width / size.height
if (destAR > srcAR) {
// fill all available width and scale height
totalH = size.height * w / size.width
totalW = w
switch (image.yAlign) {
case Align.min:
srcY = 0
case Align.mid:
srcY = -(totalH / 2 - h / 2)
case Align.max:
srcY = -(totalH - h)
}
} else {
// fill all available height and scale width
totalW = size.width * h / size.height
totalH = h
switch (image.xAlign) {
case Align.min:
srcX = 0
case Align.mid:
srcX = -(totalW / 2 - w / 2)
case Align.max:
srcX = -(totalW - w)
}
}
return CGRect(x: srcX, y: srcY, width: totalW, height: totalH)
}
}

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