// // StarNode.swift // lottie-swift // // Created by Brandon Withrow on 1/21/19. // import Foundation import QuartzCore final class StarNodeProperties: NodePropertyMap { init(star: Star) { self.direction = star.direction self.position = NodeProperty(provider: KeyframeInterpolator(keyframes: star.position.keyframes)) self.outerRadius = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRadius.keyframes)) self.outerRoundedness = NodeProperty(provider: KeyframeInterpolator(keyframes: star.outerRoundness.keyframes)) if let innerRadiusKeyframes = star.innerRadius?.keyframes { self.innerRadius = NodeProperty(provider: KeyframeInterpolator(keyframes: innerRadiusKeyframes)) } else { self.innerRadius = NodeProperty(provider: SingleValueProvider(Vector1D(0))) } if let innderRoundedness = star.innerRoundness?.keyframes { self.innerRoundedness = NodeProperty(provider: KeyframeInterpolator(keyframes: innderRoundedness)) } else { self.innerRoundedness = NodeProperty(provider: SingleValueProvider(Vector1D(0))) } self.rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: star.rotation.keyframes)) self.points = NodeProperty(provider: KeyframeInterpolator(keyframes: star.points.keyframes)) let keypathProperties: [String : AnyNodeProperty] = [ "Position" : position, "Outer Radius" : outerRadius, "Outer Roundedness" : outerRoundedness, "Inner Radius" : innerRadius, "Inner Roundedness" : innerRoundedness, "Rotation" : rotation, "Points" : points ] self.properties = Array(keypathProperties.values) } let properties: [AnyNodeProperty] let direction: PathDirection let position: NodeProperty let outerRadius: NodeProperty let outerRoundedness: NodeProperty let innerRadius: NodeProperty let innerRoundedness: NodeProperty let rotation: NodeProperty let points: NodeProperty } final class StarNode: AnimatorNode, PathNode { let properties: StarNodeProperties let pathOutput: PathOutputNode init(parentNode: AnimatorNode?, star: Star) { self.pathOutput = PathOutputNode(parent: parentNode?.outputNode) self.properties = StarNodeProperties(star: star) self.parentNode = parentNode } // MARK: Animator Node var propertyMap: NodePropertyMap { return properties } let parentNode: AnimatorNode? var hasLocalUpdates: Bool = false var hasUpstreamUpdates: Bool = false var lastUpdateFrame: CGFloat? = nil var isEnabled: Bool = true { didSet{ self.pathOutput.isEnabled = self.isEnabled } } /// Magic number needed for building path data static let PolystarConstant: CGFloat = 0.47829 func rebuildOutputs(frame: CGFloat) { let outerRadius = properties.outerRadius.value.cgFloatValue let innerRadius = properties.innerRadius.value.cgFloatValue let outerRoundedness = properties.outerRoundedness.value.cgFloatValue * 0.01 let innerRoundedness = properties.innerRoundedness.value.cgFloatValue * 0.01 let numberOfPoints = properties.points.value.cgFloatValue let rotation = properties.rotation.value.cgFloatValue let position = properties.position.value.pointValue var currentAngle = (rotation - 90).toRadians() let anglePerPoint = (2 * CGFloat.pi) / numberOfPoints let halfAnglePerPoint = anglePerPoint / 2.0 let partialPointAmount = numberOfPoints - floor(numberOfPoints) var point: CGPoint = .zero var partialPointRadius: CGFloat = 0 if partialPointAmount != 0 { currentAngle += halfAnglePerPoint * (1 - partialPointAmount) partialPointRadius = innerRadius + partialPointAmount * (outerRadius - innerRadius) point.x = (partialPointRadius * cos(currentAngle)) point.y = (partialPointRadius * sin(currentAngle)) currentAngle += anglePerPoint * partialPointAmount / 2 } else { point.x = (outerRadius * cos(currentAngle)) point.y = (outerRadius * sin(currentAngle)) currentAngle += halfAnglePerPoint } var vertices = [CurveVertex]() vertices.append(CurveVertex(point: point + position, inTangentRelative: .zero, outTangentRelative: .zero)) var previousPoint = point var longSegment = false let numPoints: Int = Int(ceil(numberOfPoints) * 2) for i in 0..