1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-09-11 05:05:23 +03:00

Merge pull request #512 from f3dm76/task/fix-boy-animation

Fix morphing issues
This commit is contained in:
Yuri Strot 2018-12-20 14:05:57 +07:00 committed by GitHub
commit 42be7d1144
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 360 additions and 193 deletions

View File

@ -483,6 +483,8 @@
5BAE204C208E1EF4006BF277 /* SVGCanvas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAE201E208E1211006BF277 /* SVGCanvas.swift */; };
5BAE2058208F24DE006BF277 /* SceneSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAE2057208F24DE006BF277 /* SceneSerialization.swift */; };
5BAEA9C9206CEAA20049AAAE /* viewBox.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5BAEA9C8206CEAA20049AAAE /* viewBox.svg */; };
5BC2CA0D21C7B8F500AC46D9 /* CombinationAnimationGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2CA0C21C7B8F400AC46D9 /* CombinationAnimationGenerator.swift */; };
5BC2CA0E21C7B8F900AC46D9 /* CombinationAnimationGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2CA0C21C7B8F400AC46D9 /* CombinationAnimationGenerator.swift */; };
5BFEF5CE20B80A83008DAC11 /* BlendEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFEF5CC20B80A82008DAC11 /* BlendEffect.swift */; };
5BFEF5CF20B80A83008DAC11 /* BlendEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFEF5CC20B80A82008DAC11 /* BlendEffect.swift */; };
5BFEF5D020B80A83008DAC11 /* ColorMatrixEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFEF5CD20B80A83008DAC11 /* ColorMatrixEffect.swift */; };
@ -894,6 +896,7 @@
5BAE2035208E163C006BF277 /* clip.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = clip.reference; sourceTree = "<group>"; };
5BAE2057208F24DE006BF277 /* SceneSerialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneSerialization.swift; sourceTree = "<group>"; };
5BAEA9C8206CEAA20049AAAE /* viewBox.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = viewBox.svg; sourceTree = "<group>"; };
5BC2CA0C21C7B8F400AC46D9 /* CombinationAnimationGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombinationAnimationGenerator.swift; sourceTree = "<group>"; };
5BFEF5CC20B80A82008DAC11 /* BlendEffect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlendEffect.swift; sourceTree = "<group>"; };
5BFEF5CD20B80A83008DAC11 /* ColorMatrixEffect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorMatrixEffect.swift; sourceTree = "<group>"; };
5BFEF5D420BC1C1E008DAC11 /* paths-data-18-f-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "paths-data-18-f-manual.svg"; sourceTree = "<group>"; };
@ -1121,6 +1124,7 @@
57E5E0FC1E3B393900D1CB28 /* OpacityGenerator.swift */,
5B7D7ED221300D4A00B5ED00 /* TimingFunction.swift */,
57E5E0FE1E3B393900D1CB28 /* TransformGenerator.swift */,
5BC2CA0C21C7B8F400AC46D9 /* CombinationAnimationGenerator.swift */,
);
path = animation_generators;
sourceTree = "<group>";
@ -2094,6 +2098,7 @@
5B6E194220AC58F900454E7E /* Color.swift in Sources */,
57614B551F83D15600875933 /* Common_iOS.swift in Sources */,
57614B561F83D15600875933 /* Common_macOS.swift in Sources */,
5BC2CA0E21C7B8F900AC46D9 /* CombinationAnimationGenerator.swift in Sources */,
57614B571F83D15600875933 /* MDisplayLink.swift in Sources */,
57614B591F83D15600875933 /* Event.swift in Sources */,
57614B5B1F83D15600875933 /* Interpolable.swift in Sources */,
@ -2233,6 +2238,7 @@
5B6E194120AC58F900454E7E /* Color.swift in Sources */,
A718CD4D1F45C28F00966E06 /* Common_macOS.swift in Sources */,
57F1087A1F53C92000DC365B /* MDisplayLink.swift in Sources */,
5BC2CA0D21C7B8F500AC46D9 /* CombinationAnimationGenerator.swift in Sources */,
57E5E1741E3B393900D1CB28 /* Event.swift in Sources */,
57E5E15D1E3B393900D1CB28 /* Interpolable.swift in Sources */,
57E5E1731E3B393900D1CB28 /* Variable.swift in Sources */,

View File

@ -45,8 +45,8 @@ class AnimationUtilsTests: XCTestCase {
XCTAssert(aRenderer?.zPosition == 1)
XCTAssert(bRenderer?.zPosition == 2)
XCTAssert(cRenderer?.zPosition == 3)
XCTAssert(dRenderer?.zPosition == 4 )
XCTAssert(eRenderer?.zPosition == 5 )
XCTAssert(fRenderer?.zPosition == 6 )
XCTAssert(dRenderer?.zPosition == 4)
XCTAssert(eRenderer?.zPosition == 5)
XCTAssert(fRenderer?.zPosition == 6)
}
}

View File

@ -194,8 +194,9 @@ class MacawSVGTests: XCTestCase {
func createJSON(_ testResourcePath: String) {
do {
let node = try SVGParser.parse(fullPath: testResourcePath)
let path = testResourcePath.replacingOccurrences(of: ".svg", with: ".reference")
let bundle = Bundle(for: type(of: TestUtils()))
let node = try SVGParser.parse(resource: testResourcePath, fromBundle: bundle)
let path = testResourcePath + ".reference"
try getJSONData(node: node).write(to: URL(fileURLWithPath: path))
} catch {
XCTFail(error.localizedDescription)

View File

@ -24,6 +24,12 @@
],
"node" : "Group"
},
{
"contents" : [
],
"node" : "Group"
},
{
"contents" : [
{
@ -47,6 +53,105 @@
}
],
"node" : "Group"
},
{
"contents" : [
{
"contents" : [
{
"contents" : [
],
"node" : "Group"
}
],
"node" : "Group"
}
],
"node" : "Group"
}
],
"node" : "Group"
},
{
"contents" : [
{
"contents" : [
],
"node" : "Group"
},
{
"contents" : [
],
"node" : "Group"
}
],
"node" : "Group"
},
{
"contents" : [
{
"contents" : [
{
"contents" : [
],
"node" : "Group"
}
],
"node" : "Group"
}
],
"node" : "Group"
},
{
"contents" : [
{
"contents" : [
],
"node" : "Group"
},
{
"contents" : [
],
"node" : "Group"
}
],
"node" : "Group"
},
{
"contents" : [
{
"contents" : [
],
"node" : "Group"
},
{
"contents" : [
],
"node" : "Group"
}
],
"node" : "Group"
},
{
"contents" : [
{
"contents" : [
{
"contents" : [
],
"node" : "Group"
}
],
"node" : "Group"
}
],
"node" : "Group"

View File

@ -149,7 +149,7 @@ class AnimationProducer {
}
// MARK: - Sequence animation
fileprivate func addAnimationSequence(_ animationSequnce: Animation) {
func addAnimationSequence(_ animationSequnce: Animation) {
guard let sequence = animationSequnce as? AnimationSequence else {
return
}
@ -197,73 +197,6 @@ class AnimationProducer {
}
}
// MARK: - Combine animation
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
}
}
// repeat count
if combine.repeatCount > 0.00001 {
var sequence = [Animation]()
for _ in 0..<Int(combine.repeatCount) {
sequence.append(combine)
}
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
}
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
}
longestAnimation?.next = completionAnimation
} else {
if let next = combine.next {
longestAnimation?.next = next
}
}
combine.removeFunc = { [weak combine] in
combine?.animations.forEach { animation in
animation.removeFunc?()
}
}
// Launching
combine.animations.forEach { animation in
self.addAnimation(animation)
}
}
// MARK: - Empty Animation
fileprivate func executeCompletion(_ emptyAnimation: BasicAnimation) {
emptyAnimation.completion?()

View File

@ -22,6 +22,15 @@ class AnimationUtils {
parentRenderer = parentRenderer?.parentRenderer
}
var rootRenderer = nodeRenderer
while rootRenderer?.parentRenderer != nil {
rootRenderer = rootRenderer?.parentRenderer
}
if let view = rootRenderer?.view, let bounds = rootRenderer?.node()?.bounds {
let a = view.contentLayout.layout(rect: bounds, into: view.bounds.size.toMacaw())
transform = transform.concat(with: a)
}
return transform
}

View File

@ -3,9 +3,13 @@ import Foundation
internal class CombineAnimation: BasicAnimation {
let animations: [BasicAnimation]
let toNodes: [Node]
let duration: Double
required init(animations: [BasicAnimation], delay: Double = 0.0, node: Node? = .none) {
required init(animations: [BasicAnimation], during: Double = 1.0, delay: Double = 0.0, node: Node? = .none, toNodes: [Node] = []) {
self.animations = animations
self.duration = during
self.toNodes = toNodes
super.init()
@ -78,12 +82,12 @@ internal class CombineAnimation: BasicAnimation {
}
public extension Sequence where Iterator.Element: Animation {
public func combine(delay: Double = 0.0, node: Node? = .none) -> Animation {
public func combine(delay: Double = 0.0, node: Node? = .none, toNodes: [Node] = []) -> Animation {
var toCombine = [BasicAnimation]()
self.forEach { animation in
toCombine.append(animation as! BasicAnimation)
}
return CombineAnimation(animations: toCombine, delay: delay, node: node ?? toCombine.first?.node)
return CombineAnimation(animations: toCombine, delay: delay, node: node ?? toCombine.first?.node, toNodes: toNodes)
}
}

View File

@ -82,88 +82,7 @@ public extension AnimatableVariable where T: ContentsInterpolation {
fromNode = passedFromNode
}
// Shapes on same hierarhy level
let fromShapes = fromNode.contents.compactMap { $0 as? Shape }
let toShapes = to.compactMap { $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)
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)
animations.append(animation)
}
}
// Groups on same hierahy level
let fromGroups = fromNode.contents.compactMap { $0 as? Group }
let toGroups = to.compactMap { $0 as? Group }
let minGroupsNumber = min(fromGroups.count, toGroups.count)
for i in 0..<minGroupsNumber {
let fromGroup = fromGroups[i]
let toGroup = toGroups[i]
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)
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)
animations.append(animation)
}
// Rest nodes
let fromNodes = fromNode.contents.filter {
!($0 is Group || $0 is Shape)
}
let toNodes = to.filter {
!($0 is Group || $0 is Shape)
}
fromNodes.forEach { node in
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)
animations.append(animation)
}
return animations.combine(node: fromNode)
return CombineAnimation(animations: [], during: during, node: fromNode, toNodes: to)
}
public func animate(from: Group? = nil, to: [Node], during: Double = 1.0, delay: Double = 0.0) {

View File

@ -7,7 +7,9 @@
//
class ShapeAnimation: AnimationImpl<Shape> {
convenience init(animatedNode: Shape, finalValue: Shape, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
let toParentGlobalTransfrom: Transform
convenience init(animatedNode: Shape, finalValue: Shape, toParentGlobalTransfrom: Transform = .identity, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
let interpolationFunc = { (t: Double) -> Shape in
if t == 0 {
@ -17,10 +19,11 @@ class ShapeAnimation: AnimationImpl<Shape> {
return finalValue
}
self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps)
self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, toParentGlobalTransfrom: toParentGlobalTransfrom, 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) {
init(animatedNode: Shape, valueFunc: @escaping (Double) -> Shape, toParentGlobalTransfrom: Transform = .identity, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
self.toParentGlobalTransfrom = toParentGlobalTransfrom
super.init(observableValue: AnimatableVariable<Shape>(animatedNode), valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
type = .shape
node = animatedNode
@ -30,7 +33,8 @@ class ShapeAnimation: AnimationImpl<Shape> {
}
}
init(animatedNode: Shape, factory: @escaping (() -> ((Double) -> Shape)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
init(animatedNode: Shape, factory: @escaping (() -> ((Double) -> Shape)), toParentGlobalTransfrom: Transform = .identity, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
self.toParentGlobalTransfrom = toParentGlobalTransfrom
super.init(observableValue: AnimatableVariable<Shape>(animatedNode), factory: factory, animationDuration: animationDuration, delay: delay, fps: fps)
type = .shape
node = animatedNode

View File

@ -51,8 +51,8 @@ class AnimationCache {
let cgRect = shapeBounds.toCG()
let origFrame = CGRect(x: 0.0, y: 0.0,
width: round(cgRect.width),
height: round(cgRect.height))
width: cgRect.width,
height: cgRect.height)
layer.bounds = origFrame
layer.anchorPoint = CGPoint(
@ -78,7 +78,7 @@ class AnimationCache {
}
layer.opacity = Float(node.opacity)
layer.node = node
layer.renderer = renderer
layer.contentsScale = calculateAnimationScale(animation: animation)

View File

@ -0,0 +1,185 @@
//
// CombinationAnimationGenerator.swift
// Macaw
//
// Created by Alisa Mylnikova on 17/12/2018.
//
import UIKit
extension AnimationProducer {
func createChildAnimations(_ combineAnimation: Animation, globalToPosition: Transform = .identity, animations: [Animation] = []) -> [Animation] {
guard let combine = combineAnimation as? CombineAnimation else {
return animations
}
let globalToPosition = globalToPosition
let during = combine.duration
let delay = combine.delay
let fromNode = combine.node as! Group
let to = combine.toNodes
// Shapes on same hierarhy level
let fromShapes = fromNode.contents.compactMap { $0 as? Shape }
let toShapes = to.compactMap { $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, toParentGlobalTransfrom: globalToPosition, 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)
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)
animations.append(animation)
}
}
// Groups on same hierahy level
let fromGroups = fromNode.contents.compactMap { $0 as? Group }
let toGroups = to.compactMap { $0 as? Group }
let minGroupsNumber = min(fromGroups.count, toGroups.count)
for i in 0..<minGroupsNumber {
let fromGroup = fromGroups[i]
let toGroup = toGroups[i]
let groupAnimation = fromGroup.contentsVar.animation(to: toGroup.contents, during: during, delay: delay)
let groupAnimations = createChildAnimations(groupAnimation, globalToPosition: globalToPosition.concat(with: toGroup.place), animations: animations)
animations.append(contentsOf: groupAnimations)
}
for i in minGroupsNumber..<fromGroups.count {
let groupToHide = fromGroups[i]
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)
animations.append(animation)
}
// Rest nodes
let fromNodes = fromNode.contents.filter {
!($0 is Group || $0 is Shape)
}
let toNodes = to.filter {
!($0 is Group || $0 is Shape)
}
fromNodes.forEach { node in
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)
animations.append(animation)
}
return animations
}
// MARK: - Combine animation
func addCombineAnimation(_ combineAnimation: Animation) {
guard let combine = combineAnimation as? CombineAnimation,
let renderer = combine.nodeRenderer,
let view = renderer.view else {
return
}
var animations = combine.animations
if let toBounds = combine.toNodes.group().bounds {
let globalTransform = view.contentLayout.layout(rect: toBounds, into: view.frame.size.toMacaw())
let childAnimations = createChildAnimations(combine, globalToPosition: globalTransform) as! [BasicAnimation]
animations.append(contentsOf: childAnimations)
}
// Reversing
if combine.autoreverses {
animations.forEach { animation in
animation.autoreverses = true
}
}
// repeat count
if combine.repeatCount > 0.00001 {
var sequence = [Animation]()
for _ in 0..<Int(combine.repeatCount) {
sequence.append(combine)
}
combine.repeatCount = 0.0
addAnimationSequence(sequence.sequence())
return
}
// Looking for longest animation
var longestAnimation: BasicAnimation?
animations.forEach { animation in
guard let longest = longestAnimation else {
longestAnimation = animation
return
}
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
}
longestAnimation?.next = completionAnimation
} else {
if let next = combine.next {
longestAnimation?.next = next
}
}
combine.removeFunc = {
animations.forEach { animation in
animation.removeFunc?()
}
}
// Launching
animations.forEach { animation in
self.addAnimation(animation)
}
}
}

View File

@ -148,8 +148,9 @@ fileprivate func generateShapeAnimation(from: Shape, to: Shape, animation: Shape
let scaleAnimation = CABasicAnimation(keyPath: "transform")
scaleAnimation.duration = duration
let parentPos = AnimationUtils.absolutePosition(animation.nodeRenderer?.parentRenderer)
let fromPos = parentPos.concat(with: to.place)
let toPos = parentPos.concat(with: to.place)
let fromPos = parentPos.concat(with: from.place)
let toParentPos = animation.toParentGlobalTransfrom
let toPos = toParentPos.concat(with: to.place)
scaleAnimation.fromValue = CATransform3DMakeAffineTransform(fromPos.toCG())
scaleAnimation.toValue = CATransform3DMakeAffineTransform(toPos.toCG())

View File

@ -68,6 +68,12 @@ class GroupRenderer: NodeRenderer {
}) {
renderers = updatedRenderers
}
var parent: NodeRenderer = self
while let parentRenderer = parent.parentRenderer {
parent = parentRenderer
}
parent.calculateZPositionRecursively()
}
override func replaceNode(with replacementNode: Node) {

View File

@ -282,6 +282,10 @@ class NodeRenderer {
}
}
func calculateZPositionRecursively() {
calculateZPosition(self)
}
private func applyClip(in context: CGContext) {
guard let node = node() else {
return
@ -358,3 +362,15 @@ extension NodeRenderer: AnimationObserver {
}
}
@discardableResult fileprivate func calculateZPosition(_ nodeRenderer: NodeRenderer, currentIndex: Int = 0) -> Int {
nodeRenderer.zPosition = currentIndex
if let groupRenderer = nodeRenderer as? GroupRenderer {
var i = currentIndex + 1
for child in groupRenderer.renderers {
i = calculateZPosition(child, currentIndex: i)
}
return i
}
return currentIndex + 1
}

View File

@ -178,8 +178,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
}
override open func draw(_ rect: CGRect) {
calculateZPosition(renderer)
context.cgContext = MGraphicsGetCurrentContext()
guard let ctx = context.cgContext else {
return
@ -192,22 +190,11 @@ open class MacawView: MView, MGestureRecognizerDelegate {
guard let renderer = renderer else {
return
}
renderer.calculateZPositionRecursively()
ctx.concatenate(layoutHelper.getTransform(renderer, contentLayout, bounds.size.toMacaw()))
renderer.render(in: ctx, force: false, opacity: node.opacity)
}
@discardableResult private func calculateZPosition(_ nodeRenderer: NodeRenderer?, currentIndex: Int = 0) -> Int {
nodeRenderer?.zPosition = currentIndex
if let groupRenderer = nodeRenderer as? GroupRenderer {
var i = currentIndex + 1
for child in groupRenderer.renderers {
i = calculateZPosition(child, currentIndex: i)
}
return i
}
return currentIndex + 1
}
public final func findNodeAt(location: CGPoint) -> Node? {
guard let ctx = context.cgContext else {
return .none

View File

@ -7,7 +7,7 @@ import AppKit
#endif
class ShapeLayer: CAShapeLayer {
weak var node: Node?
weak var renderer: NodeRenderer?
var renderTransform: CGAffineTransform?
weak var animationCache: AnimationCache?
var shouldRenderContent = true
@ -19,14 +19,6 @@ class ShapeLayer: CAShapeLayer {
return
}
guard let node = node else {
return
}
guard let animationCache = animationCache else {
return
}
let renderContext = RenderContext(view: .none)
renderContext.cgContext = ctx
@ -34,8 +26,7 @@ class ShapeLayer: CAShapeLayer {
ctx.concatenate(renderTransform)
}
let renderer = RenderUtils.createNodeRenderer(node, view: renderContext.view, animationCache: animationCache)
renderer.directRender(in: ctx, force: isForceRenderingEnabled)
renderer.dispose()
renderer!.directRender(in: ctx, force: isForceRenderingEnabled)
renderer!.dispose()
}
}